Reader.html
31.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>The source code</title>
<link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
<script type="text/javascript" src="../resources/prettify/prettify.js"></script>
<style type="text/css">
.highlight { display: block; background-color: #ddd; }
</style>
<script type="text/javascript">
function highlight() {
document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
}
</script>
</head>
<body onload="prettyPrint(); highlight();">
<pre class="prettyprint lang-js"><span id='Ext-data-reader-Reader'>/**
</span> * @author Ed Spencer
*
* Readers are used to interpret data to be loaded into a {@link Ext.data.Model Model} instance or a {@link
* Ext.data.Store Store} - often in response to an AJAX request. In general there is usually no need to create
* a Reader instance directly, since a Reader is almost always used together with a {@link Ext.data.proxy.Proxy Proxy},
* and is configured using the Proxy's {@link Ext.data.proxy.Proxy#cfg-reader reader} configuration property:
*
* Ext.create('Ext.data.Store', {
* model: 'User',
* proxy: {
* type: 'ajax',
* url : 'users.json',
* reader: {
* type: 'json',
* root: 'users'
* }
* },
* });
*
* The above reader is configured to consume a JSON string that looks something like this:
*
* {
* "success": true,
* "users": [
* { "name": "User 1" },
* { "name": "User 2" }
* ]
* }
*
*
* # Loading Nested Data
*
* Readers have the ability to automatically load deeply-nested data objects based on the {@link Ext.data.association.Association
* associations} configured on each Model. Below is an example demonstrating the flexibility of these associations in a
* fictional CRM system which manages a User, their Orders, OrderItems and Products. First we'll define the models:
*
* Ext.define("User", {
* extend: 'Ext.data.Model',
* fields: [
* 'id', 'name'
* ],
*
* hasMany: {model: 'Order', name: 'orders'},
*
* proxy: {
* type: 'rest',
* url : 'users.json',
* reader: {
* type: 'json',
* root: 'users'
* }
* }
* });
*
* Ext.define("Order", {
* extend: 'Ext.data.Model',
* fields: [
* 'id', 'total'
* ],
*
* hasMany : {model: 'OrderItem', name: 'orderItems', associationKey: 'order_items'},
* belongsTo: 'User'
* });
*
* Ext.define("OrderItem", {
* extend: 'Ext.data.Model',
* fields: [
* 'id', 'price', 'quantity', 'order_id', 'product_id'
* ],
*
* belongsTo: ['Order', {model: 'Product', associationKey: 'product'}]
* });
*
* Ext.define("Product", {
* extend: 'Ext.data.Model',
* fields: [
* 'id', 'name'
* ],
*
* hasMany: 'OrderItem'
* });
*
* This may be a lot to take in - basically a User has many Orders, each of which is composed of several OrderItems.
* Finally, each OrderItem has a single Product. This allows us to consume data like this:
*
* {
* "users": [
* {
* "id": 123,
* "name": "Ed",
* "orders": [
* {
* "id": 50,
* "total": 100,
* "order_items": [
* {
* "id" : 20,
* "price" : 40,
* "quantity": 2,
* "product" : {
* "id": 1000,
* "name": "MacBook Pro"
* }
* },
* {
* "id" : 21,
* "price" : 20,
* "quantity": 3,
* "product" : {
* "id": 1001,
* "name": "iPhone"
* }
* }
* ]
* }
* ]
* }
* ]
* }
*
* The JSON response is deeply nested - it returns all Users (in this case just 1 for simplicity's sake), all of the
* Orders for each User (again just 1 in this case), all of the OrderItems for each Order (2 order items in this case),
* and finally the Product associated with each OrderItem. Now we can read the data and use it as follows:
*
* var store = Ext.create('Ext.data.Store', {
* model: "User"
* });
*
* store.load({
* callback: function() {
* //the user that was loaded
* var user = store.first();
*
* console.log("Orders for " + user.get('name') + ":")
*
* //iterate over the Orders for each User
* user.orders().each(function(order) {
* console.log("Order ID: " + order.getId() + ", which contains items:");
*
* //iterate over the OrderItems for each Order
* order.orderItems().each(function(orderItem) {
* //we know that the Product data is already loaded, so we can use the synchronous getProduct
* //usually, we would use the asynchronous version (see {@link Ext.data.association.BelongsTo})
* var product = orderItem.getProduct();
*
* console.log(orderItem.get('quantity') + ' orders of ' + product.get('name'));
* });
* });
* }
* });
*
* Running the code above results in the following:
*
* Orders for Ed:
* Order ID: 50, which contains items:
* 2 orders of MacBook Pro
* 3 orders of iPhone
*/
Ext.define('Ext.data.reader.Reader', {
requires: ['Ext.data.ResultSet', 'Ext.XTemplate'],
alternateClassName: ['Ext.data.Reader', 'Ext.data.DataReader'],
mixins: {
observable: 'Ext.util.Observable'
},
<span id='Ext-data-reader-Reader-cfg-idProperty'> /**
</span> * @cfg {String} idProperty
* Name of the property within a row object that contains a record identifier value. Defaults to the id of the
* model. If an idProperty is explicitly specified it will override the idProperty defined on the model.
*/
<span id='Ext-data-reader-Reader-cfg-totalProperty'> /**
</span> * @cfg {String} [totalProperty="total"]
* Name of the property from which to retrieve the total number of records in the dataset. This is only needed if
* the whole dataset is not passed in one go, but is being paged from the remote server.
*/
totalProperty: 'total',
<span id='Ext-data-reader-Reader-cfg-successProperty'> /**
</span> * @cfg {String} [successProperty="success"]
* Name of the property from which to retrieve the `success` attribute, the value of which indicates
* whether a given request succeeded or failed (typically a boolean or 'true'|'false'). See
* {@link Ext.data.proxy.Server}.{@link Ext.data.proxy.Server#exception exception} for additional information.
*/
successProperty: 'success',
<span id='Ext-data-reader-Reader-cfg-root'> /**
</span> * @cfg {String} root
* The name of the property which contains the data items corresponding to the Model(s) for which this
* Reader is configured. For JSON reader it's a property name (or a dot-separated list of property names
* if the root is nested). For XML reader it's a CSS selector. For Array reader the root is not applicable
* since the data is assumed to be a single-level array of arrays.
*
* By default the natural root of the data will be used: the root JSON array, the root XML element, or the array.
*
* The data packet value for this property should be an empty array to clear the data or show no data.
*/
root: '',
<span id='Ext-data-reader-Reader-cfg-messageProperty'> /**
</span> * @cfg {String} messageProperty
* The name of the property which contains a response message. This property is optional.
*/
<span id='Ext-data-reader-Reader-cfg-implicitIncludes'> /**
</span> * @cfg {Boolean} [implicitIncludes=true]
* True to automatically parse models nested within other models in a response object. See the
* Ext.data.reader.Reader intro docs for full explanation.
*/
implicitIncludes: true,
<span id='Ext-data-reader-Reader-cfg-readRecordsOnFailure'> /**
</span> * @cfg {Boolean} [readRecordsOnFailure=true]
* True to extract the records from a data packet even if the {@link #successProperty} returns false.
*/
readRecordsOnFailure: true,
<span id='Ext-data-reader-Reader-property-metaData'> /**
</span> * @property {Object} metaData
* The raw meta data that was most recently read, if any. Meta data can include existing
* Reader config options like {@link #idProperty}, {@link #totalProperty}, etc. that get
* automatically applied to the Reader, and those can still be accessed directly from the Reader
* if needed. However, meta data is also often used to pass other custom data to be processed
* by application code. For example, it is common when reconfiguring the data model of a grid to
* also pass a corresponding column model config to be applied to the grid. Any such data will
* not get applied to the Reader directly (it just gets passed through and is ignored by Ext).
* This metaData property gives you access to all meta data that was passed, including any such
* custom data ignored by the reader.
*
* This is a read-only property, and it will get replaced each time a new meta data object is
* passed to the reader. Note that typically you would handle proxy's
* {@link Ext.data.proxy.Proxy#metachange metachange} event which passes this exact same meta
* object to listeners. However this property is available if it's more convenient to access it
* via the reader directly in certain cases.
* @readonly
*/
/*
* @property {Boolean} isReader
* `true` in this class to identify an object as an instantiated Reader, or subclass thereof.
*/
isReader: true,
// Private flag to the generated convertRecordData function to indicate whether to apply Field default
// values to fields for which no value is present in the raw data.
// This is set to false by a Server Proxy which is reading the response from a "create" or "update" operation.
applyDefaults: true,
lastFieldGeneration: null,
<span id='Ext-data-reader-Reader-method-constructor'> /**
</span> * Creates new Reader.
* @param {Object} config (optional) Config object.
*/
constructor: function(config) {
var me = this;
me.mixins.observable.constructor.call(me, config);
me.fieldCount = 0;
me.model = Ext.ModelManager.getModel(me.model);
me.accessExpressionFn = Ext.Function.bind(me.createFieldAccessExpression, me);
// Extractors can only be calculated if the fields MixedCollection has been set.
// A Model may only complete its setup (set the prototype properties) after asynchronous loading
// which would mean that there may be no "fields"
// If this happens, the load callback will call proxy.setModel which calls reader.setModel which
// triggers buildExtractors.
if (me.model && me.model.prototype.fields) {
me.buildExtractors();
}
this.addEvents(
<span id='Ext-data-reader-Reader-event-exception'> /**
</span> * @event
* Fires when the reader receives improperly encoded data from the server
* @param {Ext.data.reader.Reader} reader A reference to this reader
* @param {XMLHttpRequest} response The XMLHttpRequest response object
* @param {Ext.data.ResultSet} error The error object
*/
'exception'
);
},
<span id='Ext-data-reader-Reader-method-setModel'> /**
</span> * Sets a new model for the reader.
* @private
* @param {Object} model The model to set.
* @param {Boolean} setOnProxy True to also set on the Proxy, if one is configured
*/
setModel: function(model, setOnProxy) {
var me = this;
me.model = Ext.ModelManager.getModel(model);
me.buildExtractors(true);
if (setOnProxy && me.proxy) {
me.proxy.setModel(me.model, true);
}
},
<span id='Ext-data-reader-Reader-method-read'> /**
</span> * Reads the given response object. This method normalizes the different types of response object that may be passed to it.
* If it's an XMLHttpRequest object, hand off to the subclass' {@link #getResponseData} method.
* Else, hand off the reading of records to the {@link #readRecords} method.
* @param {Object} response The response object. This may be either an XMLHttpRequest object or a plain JS object
* @return {Ext.data.ResultSet} The parsed or default ResultSet object
*/
read: function(response) {
var data;
if (response) {
data = response.responseText ? this.getResponseData(response) : this.readRecords(response);
}
return data || this.nullResultSet;
},
<span id='Ext-data-reader-Reader-method-readRecords'> /**
</span> * Abstracts common functionality used by all Reader subclasses. Each subclass is expected to call this function
* before running its own logic and returning the Ext.data.ResultSet instance. For most Readers additional
* processing should not be needed.
* @param {Object} data The raw data object
* @return {Ext.data.ResultSet} A ResultSet object
*/
readRecords: function(data) {
var me = this,
success,
recordCount,
records,
root,
total,
value,
message;
/*
* We check here whether fields collection has changed since the last read.
* This works around an issue when a Model is used for both a Tree and another
* source, because the tree decorates the model with extra fields and it causes
* issues because the readers aren't notified.
*/
if (me.lastFieldGeneration !== me.model.prototype.fields.generation) {
me.buildExtractors(true);
}
<span id='Ext-data-reader-Reader-property-rawData'> /**
</span> * @property {Object} rawData
* The raw data object that was last passed to {@link #readRecords}. Stored for further processing if needed.
*/
me.rawData = data;
data = me.getData(data);
success = true;
recordCount = 0;
records = [];
if (me.successProperty) {
value = me.getSuccess(data);
if (value === false || value === 'false') {
success = false;
}
}
if (me.messageProperty) {
message = me.getMessage(data);
}
// Only try and extract other data if call was successful
if (me.readRecordsOnFailure || success) {
// If we pass an array as the data, we dont use getRoot on the data.
// Instead the root equals to the data.
root = Ext.isArray(data) ? data : me.getRoot(data);
if (root) {
total = root.length;
}
if (me.totalProperty) {
value = parseInt(me.getTotal(data), 10);
if (!isNaN(value)) {
total = value;
}
}
if (root) {
records = me.extractData(root);
recordCount = records.length;
}
}
return new Ext.data.ResultSet({
total : total || recordCount,
count : recordCount,
records: records,
success: success,
message: message
});
},
<span id='Ext-data-reader-Reader-method-extractData'> /**
</span> * Returns extracted, type-cast rows of data.
* @param {Object[]/Object} root from server response
* @return {Array} An array of records containing the extracted data
* @private
*/
extractData : function(root) {
var me = this,
records = [],
Model = me.model,
length = root.length,
convertedValues, node, record, i;
if (!root.length && Ext.isObject(root)) {
root = [root];
length = 1;
}
for (i = 0; i < length; i++) {
node = root[i];
if (!node.isModel) {
// Create a record with an empty data object.
// Populate that data object by extracting and converting field values from raw data
record = new Model(undefined, me.getId(node), node, convertedValues = {});
// If the server did not include an id in the response data, the Model constructor will mark the record as phantom.
// We need to set phantom to false here because records created from a server response using a reader by definition are not phantom records.
record.phantom = false;
// Use generated function to extract all fields at once
me.convertRecordData(convertedValues, node, record);
records.push(record);
if (me.implicitIncludes) {
me.readAssociated(record, node);
}
} else {
// If we're given a model instance in the data, just push it on
// without doing any conversion
records.push(node);
}
}
return records;
},
<span id='Ext-data-reader-Reader-method-readAssociated'> /**
</span> * @private
* Loads a record's associations from the data object. This prepopulates hasMany and belongsTo associations
* on the record provided.
* @param {Ext.data.Model} record The record to load associations for
* @param {Object} data The data object
* @return {String} Return value description
*/
readAssociated: function(record, data) {
var associations = record.associations.items,
i = 0,
length = associations.length,
association, associationData, proxy, reader;
for (; i < length; i++) {
association = associations[i];
associationData = this.getAssociatedDataRoot(data, association.associationKey || association.name);
if (associationData) {
reader = association.getReader();
if (!reader) {
proxy = association.associatedModel.proxy;
// if the associated model has a Reader already, use that, otherwise attempt to create a sensible one
if (proxy) {
reader = proxy.getReader();
} else {
reader = new this.constructor({
model: association.associatedName
});
}
}
association.read(record, reader, associationData);
}
}
},
<span id='Ext-data-reader-Reader-method-getAssociatedDataRoot'> /**
</span> * @private
* Used internally by {@link #readAssociated}. Given a data object (which could be json, xml etc) for a specific
* record, this should return the relevant part of that data for the given association name. This is only really
* needed to support the XML Reader, which has to do a query to get the associated data object
* @param {Object} data The raw data object
* @param {String} associationName The name of the association to get data for (uses associationKey if present)
* @return {Object} The root
*/
getAssociatedDataRoot: function(data, associationName) {
return data[associationName];
},
getFields: function() {
return this.model.prototype.fields.items;
},
<span id='Ext-data-reader-Reader-method-getData'> /**
</span> * @private
* By default this function just returns what is passed to it. It can be overridden in a subclass
* to return something else. See XmlReader for an example.
* @param {Object} data The data object
* @return {Object} The normalized data object
*/
getData: function(data) {
return data;
},
<span id='Ext-data-reader-Reader-method-getRoot'> /**
</span> * @private
* This will usually need to be implemented in a subclass. Given a generic data object (the type depends on the type
* of data we are reading), this function should return the object as configured by the Reader's 'root' meta data config.
* See XmlReader's getRoot implementation for an example. By default the same data object will simply be returned.
* @param {Object} data The data object
* @return {Object} The same data object
*/
getRoot: function(data) {
return data;
},
<span id='Ext-data-reader-Reader-method-getResponseData'> /**
</span> * Takes a raw response object (as passed to the {@link #read} method) and returns the useful data
* segment from it. This must be implemented by each subclass.
* @param {Object} response The response object
* @return {Ext.data.ResultSet} A ResultSet object
*/
getResponseData: function(response) {
//<debug>
Ext.Error.raise("getResponseData must be implemented in the Ext.data.reader.Reader subclass");
//</debug>
},
<span id='Ext-data-reader-Reader-method-onMetaChange'> /**
</span> * @private
* Reconfigures the meta data tied to this Reader
*/
onMetaChange : function(meta) {
var me = this,
fields = meta.fields || me.getFields(),
newModel,
clientIdProperty;
// save off the raw meta data
me.metaData = meta;
// set any reader-specific configs from meta if available
me.root = meta.root || me.root;
me.idProperty = meta.idProperty || me.idProperty;
me.totalProperty = meta.totalProperty || me.totalProperty;
me.successProperty = meta.successProperty || me.successProperty;
me.messageProperty = meta.messageProperty || me.messageProperty;
clientIdProperty = meta.clientIdProperty;
if (me.model) {
me.model.setFields(fields, me.idProperty, clientIdProperty);
me.setModel(me.model, true);
}
else {
newModel = Ext.define("Ext.data.reader.Json-Model" + Ext.id(), {
extend: 'Ext.data.Model',
fields: fields,
clientIdProperty: clientIdProperty
});
if (me.idProperty) {
// We only do this if the reader actually has a custom idProperty set,
// otherwise let the model use its own default value. It is valid for
// the reader idProperty to be undefined, in which case it will use the
// model's idProperty (in getIdProperty()).
newModel.idProperty = me.idProperty;
}
me.setModel(newModel, true);
}
},
<span id='Ext-data-reader-Reader-method-getIdProperty'> /**
</span> * Get the idProperty to use for extracting data
* @private
* @return {String} The id property
*/
getIdProperty: function(){
return this.idProperty || this.model.prototype.idProperty;
},
<span id='Ext-data-reader-Reader-method-buildExtractors'> /**
</span> * @private
* This builds optimized functions for retrieving record data and meta data from an object.
* Subclasses may need to implement their own getRoot function.
* @param {Boolean} [force=false] True to automatically remove existing extractor functions first
*/
buildExtractors: function(force) {
var me = this,
idProp = me.getIdProperty(),
totalProp = me.totalProperty,
successProp = me.successProperty,
messageProp = me.messageProperty,
accessor,
idField,
map;
if (force === true) {
delete me.convertRecordData;
}
if (me.convertRecordData) {
return;
}
//build the extractors for all the meta data
if (totalProp) {
me.getTotal = me.createAccessor(totalProp);
}
if (successProp) {
me.getSuccess = me.createAccessor(successProp);
}
if (messageProp) {
me.getMessage = me.createAccessor(messageProp);
}
if (idProp) {
idField = me.model.prototype.fields.get(idProp);
if (idField) {
map = idField.mapping;
idProp = (map !== undefined && map !== null) ? map : idProp;
}
accessor = me.createAccessor(idProp);
me.getId = function(record) {
var id = accessor.call(me, record);
return (id === undefined || id === '') ? null : id;
};
} else {
me.getId = function() {
return null;
};
}
me.convertRecordData = me.buildRecordDataExtractor();
me.lastFieldGeneration = me.model.prototype.fields.generation;
},
recordDataExtractorTemplate : [
'var me = this\n',
' ,fields = me.model.prototype.fields\n',
' ,value\n',
' ,internalId\n',
'<tpl for="fields">',
' ,__field{#} = fields.get("{name}")\n',
'</tpl>', ';\n',
'return function(dest, source, record) {\n',
'<tpl for="fields">',
// createFieldAccessExpression must be implemented in subclasses to extract data from the source object in the correct way
' value = {[ this.createFieldAccessExpression(values, "__field" + xindex, "source") ]};\n',
// Code for processing a source property when a custom convert is defined
'<tpl if="hasCustomConvert">',
' dest["{name}"] = value === undefined ? __field{#}.convert(__field{#}.defaultValue, record) : __field{#}.convert(value, record);\n',
// Code for processing a source property when there is a default value
'<tpl elseif="defaultValue !== undefined">',
' if (value === undefined) {\n',
' if (me.applyDefaults) {\n',
'<tpl if="convert">',
' dest["{name}"] = __field{#}.convert(__field{#}.defaultValue, record);\n',
'<tpl else>',
' dest["{name}"] = __field{#}.defaultValue\n',
'</tpl>',
' };\n',
' } else {\n',
'<tpl if="convert">',
' dest["{name}"] = __field{#}.convert(value, record);\n',
'<tpl else>',
' dest["{name}"] = value;\n',
'</tpl>',
' };',
// Code for processing a source property value when there is no default value
'<tpl else>',
' if (value !== undefined) {\n',
'<tpl if="convert">',
' dest["{name}"] = __field{#}.convert(value, record);\n',
'<tpl else>',
' dest["{name}"] = value;\n',
'</tpl>',
' }\n',
'</tpl>',
'</tpl>',
// set the client id as the internalId of the record.
// clientId handles the case where a client side record did not previously exist on the server,
// so the server is passing back a client id that can be used to pair the server side record up with the client record
'<tpl if="clientIdProp">',
' if (record && (internalId = {[ this.createFieldAccessExpression(\{mapping: values.clientIdProp\}, null, "source") ]})) {\n',
' record.{["internalId"]} = internalId;\n',
' }\n',
'</tpl>',
'};'
],
<span id='Ext-data-reader-Reader-method-buildRecordDataExtractor'> /**
</span> * @private
* Return a function which will read a raw row object in the format this Reader accepts, and populates
* a record's data object with converted data values.
*
* The returned function must be passed the following parameters:
*
* - dest A record's empty data object into which the new field value properties are injected.
* - source A raw row data object of whatever type this Reader consumes
* - record The record which is being populated.
*
*/
buildRecordDataExtractor: function() {
var me = this,
modelProto = me.model.prototype,
templateData = {
clientIdProp: modelProto.clientIdProperty,
fields: modelProto.fields.items
};
me.recordDataExtractorTemplate.createFieldAccessExpression = me.accessExpressionFn;
// Here we are creating a new Function and invoking it immediately in the scope of this Reader
// It declares several vars capturing the configured context of this Reader, and returns a function
// which, when passed a record data object, a raw data row in the format this Reader is configured to read,
// and the record which is being created, will populate the record's data object from the raw row data.
return Ext.functionFactory(me.recordDataExtractorTemplate.apply(templateData)).call(me);
},
destroyReader: function() {
var me = this;
delete me.proxy;
delete me.model;
delete me.convertRecordData;
delete me.getId;
delete me.getTotal;
delete me.getSuccess;
delete me.getMessage;
}
}, function() {
var proto = this.prototype;
Ext.apply(proto, {
// Private. Empty ResultSet to return when response is falsy (null|undefined|empty string)
nullResultSet: new Ext.data.ResultSet({
total : 0,
count : 0,
records: [],
success: true
}),
recordDataExtractorTemplate: new Ext.XTemplate(proto.recordDataExtractorTemplate)
});
});
</pre>
</body>
</html>