Map JSON Object to Model/Store - extjs

I'm using Worklight framework to construct a mobile app for IOS, and is using Sencha Touch 2.3 to build the app.
Due to the environment, i cannot use proxy in Sencha Touch Store/Model objects to load data from the server, as i would need to use Worklight's adapter to retrieve the info. I have managed to do that using some boilerplate codes.
However, i wish that i could utilize the Sencha Model more, and as such, am thinking whether it is possible for me to load a JSON object into the Model object automatically, without specifying a proxy.
Currently i'm doing a lot of loop and setter call to load the data from the JSON object to a model, like below:
var profile = Ext.create('Profile', {
Id: rawProfile.Id,
Name: rawProfile.Name
Age: rawProfile.Age
.....
}
where rawProfile is the JSON object i loaded from the server.
Any way i can make this cleaner?

You could create a Model class which would contain the data contained in your rawProfile object.
Ext.define('MyModel', {
extend: 'Ext.data.Model',
fields: [{
name: 'Id',
name: 'Age',
...
}],
proxy: {
type: 'memory',
reader: 'json'
}
});
I've also set an in memory proxy which will read json objects.
You could then create a Store which would use the model you defined and the in memory proxy (meaning you wouldn't be using Ext's build in ajax messaging).
Ext.create('MyStore', {
model: 'MyModel',
autoLoad: false
});
Setting the autoLoad to be false, as you want to get the data from a different source.
Therefore, once you have your json object you can load it into the store by calling store.loadRawData(jsonObject).
In this case, the jsonObject would be the object containing all the json objects returned from the server, meaning that your code doesn't have to handle iterating through the records and you can leave it to the Sencha classes.
However you would need to figure out the writing data back to the server.
I'd recommend running through the Sencha Data Package tutorial as it gives a good intro to the data package

If all the fields map 1:1 with the json object, you can do this:
var profile = Ext.create('Profile', rawProfile);
Alternatively (or to avoid the convert functions for fields to be called), you can directly set the data property on the created model.
var profile = Ext.create('Profile');
profile.data = rawProfile;
Again, this requires a 1:1 on the fields and json object.

Related

Backbone Collection get property

Got a server returning a JSON object like so:
{
'key1':'value'
'key2':{
'key2_0':'value'
}
}
And a collection:
var Collection = Backbone.Collection.extend({
url:api.url//which returns the object above
});
var collection = new Collection();
collection.fetch({
success:function(data){
//do something
}
});
Now i need to use certain properties of the collection throughout my application, but say i need key1, i always have to do collection.at(0).get('key1');//returns 'value', because the data returned is stored within the collection, in a new Array at key 0.
Question:
How to directly... collection.get('key1')//now returns undefined... because it is.
I know i could expose an object to the global scope in the collection success function some_other_var = data.toJSON()[0] and access the some_other_var properties directly, but that's not what i'm looking for;
In order to use the get() function from a Backbone.Collection you need to know the model id or cid wanted.
For instance, lets say your data coming from the server is like follow:
[{
id: '123',
name: 'Alex'
}, {
id: '456',
name: 'Jhon'
}]
In that case you can do this:
this.collection.get('123').get('name') // Return "Alex"
Keep in mind that collection is just a set of model, so behind the scenes by doing collection.get() you are getting a model
Tip: If you don't have any kind of id in your server data, there is always the option of using underscore methods:
find
filter
some
contains
etc
It seems like you're trying to ascribe attributes to a collection, but a collection is merely a set of models. Having additional data that is constant throughout the collection suggests that it should be wrapped inside another Model, which is demonstrated here: Persisting & loading metadata in a backbone.js collection

Sencha extjs creating and populating a Model instance via Ajax

I have a Model defined with a mapping:
Ext.define('IM.model.Source', { extend : 'Ext.data.Model', fields : [
{
"name": "billref_id",
"mapping": "billref.id"
},...
If I create a Grid with a Store using this Model and load it, every record in the Store has the billref_id attribute correctly initialised to the value of billref.id from my JSON data.
If I instead load the same data using an Ext.Ajax.request, and then feed that data into a Model instance this way:
var response = Ext.Ajax.request({
async: false,
method:'GET',
url: 'im_read.json',
params:{pkValue:1}
});
var items = Ext.decode(response.responseText);
record = Ext.create('IM.model.Source', items.rows[0]);
the resulting record does not have the mapped fields populated from the JSON data.
Only the non-mapped members of the model have values assigned.
Is it a known issue that creating and populating a Model instance this way doesn't work with mapped fields, or am I doing something wrong?
Thanks,
The mapping of a model's field is only used by a Ext.data.reader.Reader, which is usually configured on your store. If you are creating an instance of the model yourself, the mapping is not considered, since the reader is not invoked and model expects you to provide the data by the fields' names (see docs).
To solve your problem, you can just call the reader's read function with your AJAX object:
var response = Ext.Ajax.request({
async: false,
method:'GET',
url: 'im_read.json',
params:{pkValue:1}
});
var resultSet = myStore.getReader().read(response);
That will create an Ext.data.ResultSet containing your correctly-mapped records.

How to have several CRUD methods using a proxy with Sencha Touch 2 framework?

I've coded a small mobile application using Sencha Touch 2 framework. For the moment, I manage some articles from a database in localhost. I've written a database management using PHP CRUD methods contained here in ArticleService.php file. My 'read' fonction get back all the articles. But I want to have an other 'read' method to read for exemple a specific article according to its id or the last 5 articles, etc. But the store proxy (I think so) allows 1 methods for each main operation. So in my case, for 'read' operation I just have an only 'read' method.
Here's is my 'store' source code :
Ext.define("MyApp.store.ArticleStore", {
extend: "Ext.data.Store",
requires: ["MyApp.model.ArticleModel"],
config: {
model: "MyApp.model.ArticleModel",
proxy: {
type: "ajax",
api: {
create: "http://localhost/MobileApplication/MyApp/services/ArticleService.php?action=create",
read: "http://localhost/MobileApplication/MyApp/services/ArticleService.php?action=read",
update: "http://localhost/MobileApplication/MyApp/services/ArticleService.php?action=update",
destroy: "http://localhost/MobileApplication/MyApp/services/ArticleService.php?action=destroy"
},
extraParams: {
keyword: ""
},
reader: {
type: "json",
rootProperty: "articles",
totalProperty: "total"
}
},
autoLoad: true
}
});
Is there a possible way to have several methods for each main CRUD operation (for example 3 differents 'read' methods to manage my articles display) ? I'm really lost.
Thanks in advance for your help!
You don't need different methods for read. Once you load the store it will be in the memory and you will be able to find any record you want.
If you're store is too big to be loaded at once - take a look at remoteFilter property: http://docs.sencha.com/touch/2.1.1/#!/api/Ext.data.Store-cfg-remoteFilter This way filters you will set up (like id = 1 or parent_id = 2) will be passed to the server so it can return proper record set.

Sencha Architect XML TreeStore fails to load/map XML

I'm trying to use XML TreeStore in Sencha Architect and it fails to load any data from the given XML. While I can access that store as an object, its getCount() returns 'undefined' and methods like getNodeById('foobar') result in 'cannot read property 'foobar' of undefined' which makes me think the mapping doesn't happen (store is initialised but failed to parse the data / instantiate models).
The same XML with the same model but accessed via a flat store in the same basic setup works fine (e.g. getCount() returns 2 as expected). Tree store with a JSON version of data again works fine as well.
The class for my problematic store by Sencha Architect is:
Ext.define('MyApp.store.DocumentStore', {
extend: 'Ext.data.TreeStore',
requires: [
'MyApp.model.Document'
],
constructor: function(cfg) {
var me = this;
cfg = cfg || {};
me.callParent([Ext.apply({
autoLoad: true,
storeId: 'TreeDocumentStore',
model: 'MyApp.model.Document',
proxy: {
type: 'ajax',
url: 'DocumentTemplates.xml',
reader: {
type: 'xml',
root: 'Templates',
record: 'Template'
}
}
}, cfg)]);
}
});
The source XML file store refers to is:
<?xml version="1.0" encoding="UTF-8"?>
<Templates>
<Template>
<Id>A253338C-CDFA-4182-9E1C-652EAAB713F8</Id>
<Title>Template 1</Title>
<Children />
</Template>
<Template>
<Id>26D5B83C-7E9E-49E2-B174-F9A6C7BA07A8</Id>
<Title>Template 2</Title>
<Children />
</Template>
</Templates>
In case I have misunderstood the purpose of 'root' property I tried different node names for global root and supposed child node roots setting 'root' property of the reader to all the possible values, but no luck.
I tried to remove empty from the nodes as well, same effect.
This is not about displaying items from the store in a view yet, I'm only trying to access the data in controller.
P.S. Dummy project with an XML file, tree store and controller accessing that store in onLaunch is here: http://sdrv.ms/RAsj26
Having spent some time with this issue my own answer to a similar question is probably relevant: https://stackoverflow.com/a/12886943/454266
Looks like tree structures for XML sources are not supported properly in the current version of framework, be it TreeStore or association mechanisms.

Dynamic Model with ExtJS 4

With ExtJS 3.x, I was able to use the "fields" property of a Store, but it seems with ExtJS 4 I have to absolutely use a Model. It's fine, but in my case, it's not a static Model, and I need to define the fields on the fly and sometimes to change them.
I could re-create a Model, but I need to use a different name as it's apparently not possible to modify an exisiting Model, neither delete it. If I try to use Ext.regModel with the same name, ExtJS crashes.
Thanks for your help!
4.1 UPDATE:
As an update... in 4.1 there is now a static method setFields which can be used to define the model prototype fields. It works well in a controller's init method.
When I did this, I wanted to have some static fields defined in the model class and then set some more dynamically. Unfortunately the new setFields method replaces all fields with the argument, it was easy enough to handle though.
This example uses the MVC pattern where my model and store are included in the controller's model array and store array (providing me with the handy getters used below):
Ext.define('ST.controller.Main', {
extend: 'Ext.app.Controller',
models: ['User', 'Reference'],
stores: ['CurrentUser', 'PermissionRef'],
views: ['MainPanel'],
init: function() {
var me = this;
me.getPermissionRefStore().on('load', function(store, records) {
var model = me.getUserModel();
// this returns the static fields already defined
// in my User model class
fields = model.prototype.fields.getRange();
// add the permission options (dynamic fields) to the static fields
Ext.each(records, function(permission) {
fields.push({name: permission.get('name'), type: 'bool'});
});
// 4.1 method to update the User model fields
model.setFields(fields);
// now load the current user (it will use the updated model)
me.getCurrentUserStore().load();
});
}
});
The User model and CurrentUser store are created exactly like regular, non-dynamic models and stores would be and included in their respective controller arrays, the 'User' model is simply missing the dynamic fields which are added as shown above.
I also went into that problem. I have a service which is responsible for fetching metadata from the server and adapting the models and stores to this metadata.
I therefore defined an empty model and configured the store to use this model.
When the meta data is processed, I add the new/additional fields to the prototype of the model like this (metaDataStore is the store containing the meta data, model is the model which can be obtained from the model manager):
var fields = [];
metaDataStore.each(function(item) {
fields.push(Ext.create('Ext.data.Field', {
name: item.get('field')
}));
});
model.prototype.fields.removeAll();
model.prototype.fields.addAll(fields);
When I then call load on a store using this model or create new model instances the new fields are processed correctly.
Here's a very simple example. Just use a normal Ext.data.Store but instead of a model, specify the fields property:
// you can specify a simple string ('totally')
// or an object with an Ext.data.Field ('dynamic')
var fields = ['totally', {name : 'dynamic', type : 'string'}];
var newStore = new MyApp.store.Object({
fields : fields
// other options like proxy, autoLoad...
});
Don't specify a model property - it seems that it would override the fields property.
I also wanted to change the columns and content of an existing grid dynamically:
// reconfigure the grid to use the new store and other columns
var newColumns = [
{header: 'Totally', dataIndex: 'totally'},
{header: 'Dynamic', dataIndex: 'dynamic'}
];
myGrid.reconfigure(newStore, newColumns);
From the Ext JS 4 documentation about the "fields" property of Ext.data.Store:
This may be used in place of
specifying a model configuration. The
fields should be a set of
Ext.data.Field configuration objects.
The store will automatically create a
Ext.data.Model with these fields. In
general this configuration option
should be avoided, it exists for the
purposes of backwards compatibility.
For anything more complicated, such as
specifying a particular id property or
assocations, a Ext.data.Model should
be defined and specified for the model
config.
So be careful - Sencha may remove it in the future.

Resources