Constantly changing requirements and tight deadlines pushed us to use ExtJS 4 to create a prototype.
The problems in ExtJS that we encountered during development hardly exceeded the experience that we received while preparing ExtJS.
There are many articles on the ExtJS level 4 basic on the Internet. For example, I was very pleased with this article
http://css.dzone.com/articles/how-use-extjs-4-jquery . But there are not many serious “non-cash” articles on solving (or creating) any problems with the help of ExtJS.
')
I suggest you heed the first article in the ExtJS patch lab ... Well, a little about the social. network ... quite a bit.
For the tape posts, it was decided to use
Ext.panel.Grid with
Ext.data.Store . You can say these are the fundamental things for which you chose to use ExtJS.
Why the
grid , and not the
dataView (later the end had to abandon the grid in favor of the view). Grid liked the visual mode of sorting and filtering data, an “endless” scroll. The joy of the opportunities provided simply overwhelmed.
Ext.data.Store
Data comes from the server using the call API, then it is transmitted in raw form to the
store's loadData method. We did not use the proxy built into the Store because we have an API like that of
Foursqure .
var myStore = Ext.create('Ext.data.Store', { model: 'Post', filterOnLoad: true, idProperty: 'id', filters: new Ext.util.Filter({ filterFn: function (item) { return item.get("status") > 0; } }) }); … API.Events.get().done(function(posts){ myStore.loadData(posts); });
The Post model is a successor to the Ext.data.Model class.
Ext.define('Post', { extend: 'Ext.data.Model', idProperty: 'id', constructor: function (raw) { return this.callParent([this.prepare(raw), raw[this.idProperty]]); }, prepare: function (raw) { … } });
In the
Post model, raw data is prepared through the
prepare method. In the constructor of the parent class data comes already in the processed form.
What happens in the
prepare method:
For example, casting the date of a post from a string to a
Date object.
this.data = new Date(this.data);
... getting the string value of the name of the post from
typeId through the hash table of matches
this.typeName = Dict.post.types[this.typeId];
And so on...
Store stores data inside itself in the form of instantiated models, in the
loadData method
there is a check, if the data is still raw, then we transfer it to the model's constructor, and write it in ourselves, and so on with each element of the array.
Pleasant utility # 1
Thus
loadData accepts both an array of raw objects, and already instantiated models.
Feature # 1
When a single object is transferred to
loadData, an error occurs, the method expects only an array, even if from a single element.
Feature # 2
Objects in
javascript are passed by reference, so the transferred raw data in
loadData will become an array of models.
API.Events.get().done(function(posts){
If you need the original data after loading in unchanged form, use the
Ext.Array.clone () method, but this method does not support deep cloning, like
jQuery.extend .
Problem # 1
The
loadData method has a second parameter,
append (false) ; Those. from the server come new posts, we must add them to the existing ones. To do this, set the second parameter to true .
In our case the storage uses filtering by loading (filterOnLoad = true)
How filtering works:
Store stores all records in two collections, the main one is
data , and the hidden one is
snapshot . The “filterOnLoad: true” key in the
Store forces the storage when loading data in any way to check it on the filters and discard the
snapshot filtered into its internal storage.
The use of filters in the Store and the loadData method are incompatible.
Listing from ext-all-debug.js file (the
loadRecords method
is used by
loadData ... and not only)
loadRecords: function(records, options) { … if (!options.addRecords) { delete me.snapshot; me.clearData(); } me.data.addAll(records); … }
Here lies the problem,
options.addRecords in our case is
true , the
snapshot deletion does not occur, the data falls only in the main collection.
API.Posts.get().done(function(posts){ myStore.loadData(posts);
At the first boot, only 2 entries went through, and the second one was all.
me.data contains 2 + 10 records, and
me.snapshot only 8, because data from the second call did not fall into the hidden storage.
In order to re-sort our collection, we need to clean up the old filters and apply new ones; we cannot simply “re-read” the filters (which is very, very strange).
The mystore.clearFilter () method kills the last 10 records, with the result that we have 8 records in the repository.
Decision
loadRecords: function(records, options) { … if (!options.addRecords) { delete me.snapshot; me.clearData(); } me.data.addAll(records);
Problem # 2
The
idProperty key
is not used for its intended purpose.
The
idProperty field is an analogue of the primary key in the database. In our case, the data has a unique ID key, according to which fresh posts should be added to the repository. If a post changes, for example, adding a comment to it, we must update this entry in the repository. The
loadData method in
append mode with the idProperty = "id" key set should solve this problem. But in fact, this key is not used for this, and we cannot make the data aggregation automatic.
Having rummaged in source codes I have found out that such functionality is present, but simply is not used. The me.data.addAll (records) method can accept not only arrays, but also key-value objects. You only need to slightly modify the same
loadRecords method to use this feature.
Decision
loadRecords: function(records, options) { … if (!options.addRecords) { delete me.snapshot; me.clearData(); } me.data.addAll(records); if (this.idProperty) { var indexRecord = {}; for (i = 0; i < length; i++) { indexRecord[records[i].data[this.idProperty]] = records[i]; } me.data.addAll(indexRecord); if (options.addRecords && me.snapshot) { me.snapshot.addAll(indexRecord); } records = []; for (key in indexRecord) { records.push(indexRecord[key]); } length = records.length; } else { me.data.addAll(records); if (options.addRecords && me.snapshot) { me.snapshot.addAll(records); } } … }
After this change, as the key, we have the
id values, and we can add new data, they will not be duplicated ...
PS Don't forget that it’s bad to
crawl into the sources with your edits, use
Ext.override . Thus, you get rid of the problem when updating the framework version, you just write your patch file, and when you fix the old version of the old problem, you remove your solution from the patch.
On the horizon, consideration of the problem of synchronization of the Store with its presentation, and what to do if there are several representations.
Applications are accepted, perhaps there are some more interesting problems and their solutions.