1 | /* |
---|
2 | * Ext JS Library 2.2.1 |
---|
3 | * Copyright(c) 2006-2009, Ext JS, LLC. |
---|
4 | * licensing@extjs.com |
---|
5 | * |
---|
6 | * http://extjs.com/license |
---|
7 | */ |
---|
8 | |
---|
9 | /** |
---|
10 | * @class Ext.data.Record |
---|
11 | * <p>Instances of this class encapsulate both Record <em>definition</em> information, and Record |
---|
12 | * <em>value</em> information for use in {@link Ext.data.Store} objects, or any code which needs |
---|
13 | * to access Records cached in an {@link Ext.data.Store} object.</p> |
---|
14 | * <p>Constructors for this class are generated by passing an Array of field definition objects to {@link #create}. |
---|
15 | * Instances are usually only created by {@link Ext.data.Reader} implementations when processing unformatted data |
---|
16 | * objects.</p> |
---|
17 | * <p>Note that an instance of a Record class may only belong to one {@link Ext.data.Store Store} at a time. |
---|
18 | * In order to copy data from one Store to another, use the {@link #copy} method to create an exact |
---|
19 | * copy of the Record, and insert the new instance into the other Store.</p> |
---|
20 | * <p>When serializing a Record for submission to the server, be aware that it contains many private |
---|
21 | * properties, and also a reference to its owning Store which in turn holds references to its Records. |
---|
22 | * This means that a Record may not be encoded using {@link Ext.util.JSON.encode}. Instead, use the |
---|
23 | * {@link data} and {@link id} properties.</p> |
---|
24 | * Record objects generated by this constructor inherit all the methods of Ext.data.Record listed below. |
---|
25 | * @constructor |
---|
26 | * This constructor should not be used to create Record objects. Instead, use the constructor generated by |
---|
27 | * {@link #create}. The parameters are the same. |
---|
28 | * @param {Array} data An object, the properties of which provide values for the new Record's fields. |
---|
29 | * @param {Object} id (Optional) The id of the Record. This id should be unique, and is used by the |
---|
30 | * {@link Ext.data.Store} object which owns the Record to index its collection of Records. If |
---|
31 | * not specified an integer id is generated. |
---|
32 | */ |
---|
33 | Ext.data.Record = function(data, id){ |
---|
34 | this.id = (id || id === 0) ? id : ++Ext.data.Record.AUTO_ID; |
---|
35 | this.data = data; |
---|
36 | }; |
---|
37 | |
---|
38 | /** |
---|
39 | * Generate a constructor for a specific Record layout. |
---|
40 | * @param {Array} o An Array of field definition objects which specify field names, and optionally, |
---|
41 | * data types, and a mapping for an {@link Ext.data.Reader} to extract the field's value from a data object. |
---|
42 | * Each field definition object may contain the following properties: <ul> |
---|
43 | * <li><b>name</b> : String<div class="sub-desc">The name by which the field is referenced within the Record. This is referenced by, |
---|
44 | * for example, the <em>dataIndex</em> property in column definition objects passed to {@link Ext.grid.ColumnModel}</div></li> |
---|
45 | * <li><b>mapping</b> : String<div class="sub-desc">(Optional) A path specification for use by the {@link Ext.data.Reader} implementation |
---|
46 | * that is creating the Record to access the data value from the data object. If an {@link Ext.data.JsonReader} |
---|
47 | * is being used, then this is a string containing the javascript expression to reference the data relative to |
---|
48 | * the Record item's root. If an {@link Ext.data.XmlReader} is being used, this is an {@link Ext.DomQuery} path |
---|
49 | * to the data item relative to the Record element. If the mapping expression is the same as the field name, |
---|
50 | * this may be omitted.</div></li> |
---|
51 | * <li><b>type</b> : String<div class="sub-desc">(Optional) The data type for conversion to displayable value. Possible values are |
---|
52 | * <ul><li>auto (Default, implies no conversion)</li> |
---|
53 | * <li>string</li> |
---|
54 | * <li>int</li> |
---|
55 | * <li>float</li> |
---|
56 | * <li>boolean</li> |
---|
57 | * <li>date</li></ul></div></li> |
---|
58 | * <li><b>sortType</b> : Function<div class="sub-desc">(Optional) A function which converts a Field's value to a comparable value |
---|
59 | * in order to ensure correct sort ordering. Predefined functions are provided in {@link Ext.data.SortTypes}.</div></li> |
---|
60 | * <li><b>sortDir</b> : String<div class="sub-desc">(Optional) Initial direction to sort. "ASC" or "DESC"</div></li> |
---|
61 | * <li><b>convert</b> : Function<div class="sub-desc">(Optional) A function which converts the value provided |
---|
62 | * by the Reader into an object that will be stored in the Record. It is passed the |
---|
63 | * following parameters:<ul> |
---|
64 | * <li><b>v</b> : Mixed<div class="sub-desc">The data value as read by the Reader.</div></li> |
---|
65 | * <li><b>rec</b> : Mixed<div class="sub-desc">The data object containing the row as read by the Reader. |
---|
66 | * Depending on Reader type, this could be an Array, an object, or an XML element.</div></li> |
---|
67 | * </ul></div></li> |
---|
68 | * <li><b>dateFormat</b> : String<div class="sub-desc">(Optional) A format string for the {@link Date#parseDate Date.parseDate} function, |
---|
69 | * or "timestamp" if the value provided by the Reader is a UNIX timestamp, or "time" if the value provided by the Reader is a |
---|
70 | * javascript millisecond timestamp.</div></li> |
---|
71 | * <li><b>defaultValue</b> : Mixed<div class="sub-desc">(Optional) The default value used <b>when a Record is being created by a |
---|
72 | * {@link Ext.data.Reader Reader}</b> when the item referenced by the <b><tt>mapping</tt></b> does not exist in the data object |
---|
73 | * (i.e. undefined). (defaults to "")</div></li> |
---|
74 | * </ul> |
---|
75 | * The constructor generated by this method may be used to create new Record instances. The data object must contain properties |
---|
76 | * named after the field <b>names</b>. |
---|
77 | * <br>usage:<br><pre><code> |
---|
78 | var TopicRecord = Ext.data.Record.create([ |
---|
79 | {name: 'title', mapping: 'topic_title'}, |
---|
80 | {name: 'author', mapping: 'username'}, |
---|
81 | {name: 'totalPosts', mapping: 'topic_replies', type: 'int'}, |
---|
82 | {name: 'lastPost', mapping: 'post_time', type: 'date'}, |
---|
83 | {name: 'lastPoster', mapping: 'user2'}, |
---|
84 | {name: 'excerpt', mapping: 'post_text'} |
---|
85 | ]); |
---|
86 | |
---|
87 | var myNewRecord = new TopicRecord({ |
---|
88 | title: 'Do my job please', |
---|
89 | author: 'noobie', |
---|
90 | totalPosts: 1, |
---|
91 | lastPost: new Date(), |
---|
92 | lastPoster: 'Animal', |
---|
93 | excerpt: 'No way dude!' |
---|
94 | }); |
---|
95 | myStore.add(myNewRecord); |
---|
96 | </code></pre> |
---|
97 | * <p>In the simplest case, if no properties other than <tt>name</tt> are required, a field definition |
---|
98 | * may consist of just a field name string.</p> |
---|
99 | * @method create |
---|
100 | * @return {function} A constructor which is used to create new Records according |
---|
101 | * to the definition. |
---|
102 | * @static |
---|
103 | */ |
---|
104 | Ext.data.Record.create = function(o){ |
---|
105 | var f = Ext.extend(Ext.data.Record, {}); |
---|
106 | var p = f.prototype; |
---|
107 | p.fields = new Ext.util.MixedCollection(false, function(field){ |
---|
108 | return field.name; |
---|
109 | }); |
---|
110 | for(var i = 0, len = o.length; i < len; i++){ |
---|
111 | p.fields.add(new Ext.data.Field(o[i])); |
---|
112 | } |
---|
113 | f.getField = function(name){ |
---|
114 | return p.fields.get(name); |
---|
115 | }; |
---|
116 | return f; |
---|
117 | }; |
---|
118 | |
---|
119 | Ext.data.Record.AUTO_ID = 1000; |
---|
120 | Ext.data.Record.EDIT = 'edit'; |
---|
121 | Ext.data.Record.REJECT = 'reject'; |
---|
122 | Ext.data.Record.COMMIT = 'commit'; |
---|
123 | |
---|
124 | Ext.data.Record.prototype = { |
---|
125 | /** |
---|
126 | * <p><b>This property is stored in the Record definition's <u>prototype</u></b></p> |
---|
127 | * A MixedCollection containing the defined {@link Ext.data.Field Field}s for this Record. Read-only. |
---|
128 | * @property fields |
---|
129 | * @type Ext.util.MixedCollection |
---|
130 | */ |
---|
131 | /** |
---|
132 | * An object hash representing the data for this Record. Every field name in the Record definition |
---|
133 | * is represented by a property of that name in this object. Note that unless you specified a field |
---|
134 | * with name "id" in the Record definition, this will <b>not</b> contain an <tt>id</tt> property. |
---|
135 | * @property data |
---|
136 | * @type {Object} |
---|
137 | */ |
---|
138 | /** |
---|
139 | * The unique ID of the Record as specified at construction time. |
---|
140 | * @property id |
---|
141 | * @type {Object} |
---|
142 | */ |
---|
143 | /** |
---|
144 | * Readonly flag - true if this Record has been modified. |
---|
145 | * @type Boolean |
---|
146 | */ |
---|
147 | dirty : false, |
---|
148 | editing : false, |
---|
149 | error: null, |
---|
150 | /** |
---|
151 | * This object contains a key and value storing the original values of all modified fields or is null if no fields have been modified. |
---|
152 | * @property modified |
---|
153 | * @type {Object} |
---|
154 | */ |
---|
155 | modified: null, |
---|
156 | |
---|
157 | // private |
---|
158 | join : function(store){ |
---|
159 | this.store = store; |
---|
160 | }, |
---|
161 | |
---|
162 | /** |
---|
163 | * Set the named field to the specified value. |
---|
164 | * @param {String} name The name of the field to set. |
---|
165 | * @param {Object} value The value to set the field to. |
---|
166 | */ |
---|
167 | set : function(name, value){ |
---|
168 | if(String(this.data[name]) == String(value)){ |
---|
169 | return; |
---|
170 | } |
---|
171 | this.dirty = true; |
---|
172 | if(!this.modified){ |
---|
173 | this.modified = {}; |
---|
174 | } |
---|
175 | if(typeof this.modified[name] == 'undefined'){ |
---|
176 | this.modified[name] = this.data[name]; |
---|
177 | } |
---|
178 | this.data[name] = value; |
---|
179 | if(!this.editing && this.store){ |
---|
180 | this.store.afterEdit(this); |
---|
181 | } |
---|
182 | }, |
---|
183 | |
---|
184 | /** |
---|
185 | * Get the value of the named field. |
---|
186 | * @param {String} name The name of the field to get the value of. |
---|
187 | * @return {Object} The value of the field. |
---|
188 | */ |
---|
189 | get : function(name){ |
---|
190 | return this.data[name]; |
---|
191 | }, |
---|
192 | |
---|
193 | /** |
---|
194 | * Begin an edit. While in edit mode, no events are relayed to the containing store. |
---|
195 | */ |
---|
196 | beginEdit : function(){ |
---|
197 | this.editing = true; |
---|
198 | this.modified = {}; |
---|
199 | }, |
---|
200 | |
---|
201 | /** |
---|
202 | * Cancels all changes made in the current edit operation. |
---|
203 | */ |
---|
204 | cancelEdit : function(){ |
---|
205 | this.editing = false; |
---|
206 | delete this.modified; |
---|
207 | }, |
---|
208 | |
---|
209 | /** |
---|
210 | * End an edit. If any data was modified, the containing store is notified. |
---|
211 | */ |
---|
212 | endEdit : function(){ |
---|
213 | this.editing = false; |
---|
214 | if(this.dirty && this.store){ |
---|
215 | this.store.afterEdit(this); |
---|
216 | } |
---|
217 | }, |
---|
218 | |
---|
219 | /** |
---|
220 | * Usually called by the {@link Ext.data.Store} which owns the Record. |
---|
221 | * Rejects all changes made to the Record since either creation, or the last commit operation. |
---|
222 | * Modified fields are reverted to their original values. |
---|
223 | * <p> |
---|
224 | * Developers should subscribe to the {@link Ext.data.Store#update} event to have their code notified |
---|
225 | * of reject operations. |
---|
226 | * @param {Boolean} silent (optional) True to skip notification of the owning store of the change (defaults to false) |
---|
227 | */ |
---|
228 | reject : function(silent){ |
---|
229 | var m = this.modified; |
---|
230 | for(var n in m){ |
---|
231 | if(typeof m[n] != "function"){ |
---|
232 | this.data[n] = m[n]; |
---|
233 | } |
---|
234 | } |
---|
235 | this.dirty = false; |
---|
236 | delete this.modified; |
---|
237 | this.editing = false; |
---|
238 | if(this.store && silent !== true){ |
---|
239 | this.store.afterReject(this); |
---|
240 | } |
---|
241 | }, |
---|
242 | |
---|
243 | /** |
---|
244 | * Usually called by the {@link Ext.data.Store} which owns the Record. |
---|
245 | * Commits all changes made to the Record since either creation, or the last commit operation. |
---|
246 | * <p> |
---|
247 | * Developers should subscribe to the {@link Ext.data.Store#update} event to have their code notified |
---|
248 | * of commit operations. |
---|
249 | * @param {Boolean} silent (optional) True to skip notification of the owning store of the change (defaults to false) |
---|
250 | */ |
---|
251 | commit : function(silent){ |
---|
252 | this.dirty = false; |
---|
253 | delete this.modified; |
---|
254 | this.editing = false; |
---|
255 | if(this.store && silent !== true){ |
---|
256 | this.store.afterCommit(this); |
---|
257 | } |
---|
258 | }, |
---|
259 | |
---|
260 | /** |
---|
261 | * Gets a hash of only the fields that have been modified since this Record was created or commited. |
---|
262 | * @return Object |
---|
263 | */ |
---|
264 | getChanges : function(){ |
---|
265 | var m = this.modified, cs = {}; |
---|
266 | for(var n in m){ |
---|
267 | if(m.hasOwnProperty(n)){ |
---|
268 | cs[n] = this.data[n]; |
---|
269 | } |
---|
270 | } |
---|
271 | return cs; |
---|
272 | }, |
---|
273 | |
---|
274 | // private |
---|
275 | hasError : function(){ |
---|
276 | return this.error != null; |
---|
277 | }, |
---|
278 | |
---|
279 | // private |
---|
280 | clearError : function(){ |
---|
281 | this.error = null; |
---|
282 | }, |
---|
283 | |
---|
284 | /** |
---|
285 | * Creates a copy of this Record. |
---|
286 | * @param {String} id (optional) A new Record id if you don't want to use this Record's id |
---|
287 | * @return {Record} |
---|
288 | */ |
---|
289 | copy : function(newId) { |
---|
290 | return new this.constructor(Ext.apply({}, this.data), newId || this.id); |
---|
291 | }, |
---|
292 | |
---|
293 | /** |
---|
294 | * Returns true if the field passed has been modified since the load or last commit. |
---|
295 | * @param {String} fieldName |
---|
296 | * @return {Boolean} |
---|
297 | */ |
---|
298 | isModified : function(fieldName){ |
---|
299 | return !!(this.modified && this.modified.hasOwnProperty(fieldName)); |
---|
300 | } |
---|
301 | }; |
---|