Sencha extjs creating and populating a Model instance via Ajax - extjs

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.

Related

Returning string as json in extJs

I am working on extJs 3.2.x
I have a simple function which returns a string.
public Object getRevenueCurrency(....) {
return "USD";
}
I have Jackson mapper to map the response type to JSON.
<bean name="jsonView" class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
<property name="contentType">
<!-- <value>application/json</value> -->
<value>text/html</value>
</property>
</bean>
An attempt to retrieve the data goes like this:
currencyStore = new Ext.data.JsonStore({
id:'currencystore',
url: 'xxxxxxx?action=getcurrency',
root: 'string',
listeners: {load: function(store) {
rev_currency=store.?????;
}
}
});
currencyStore.on('exception',function( store, records, options ){
alert('Exception was called');
},this);
Fiddler shows a response from server as :
{"string": "USD"}
Although I get no server or js exception,the exception alert is called.
1.How do i extract the currency value ?
2.What is a way to extract a meaningful information on the exception in the exception handler above?
Concerning your second question:
2.What is a way to extract a meaningful information on the exception in the exception handler above?
According to the docs for the exception event the signature and parameters are:
exception( store, type, action, options, response, arg )
By inspecting the parameters (e.g. by console.log(...)) you should retrieve some useful information.
I notice you have not configured any fields on your store, so when the reader takes your json respsonse its trying to find a field named 'string' to bind to.
Take a look at the ExtJS3 docs for the JsonStore -> http://docs.sencha.com/extjs/3.4.0/#!/api/Ext.data.JsonStore
The sample config shows you how the fields are defined, so your store should look like:
currencyStore = new Ext.data.JsonStore({
id:'currencystore',
url: 'xxxxxxx?action=getcurrency',
fields: ['string'],
listeners: {load: function(store, records, options) {
rev_currency=store.?????;
}
}
});
Note I have also changed the params to your load function handler, so the new record created from your json can be accessed using records[0]
You have configured the store by setting the root property which means the store's reader is expecting a root node in the json matching 'string'.
So the reader is actually expecting the below form of response.
{"string":[{"propertyname":"USD"}]}
Instead you could just take out the root property on your store config since all that's being returned is a flat object

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

Map JSON Object to Model/Store

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.

ExtJS 4: How can I configure a store to load models for a specific set of IDs?

For example, say I have a server API for loading people that handles requests like this: GET /people/?id=101,329,27
I'd like to build a Store (probably a custom class that extends Ext.data.Store) which--assuming it has a list of people IDs--causes the proxy to make a request like the one shown above so that the returned data is only for that subset of persons.
I saw the documentation regarding remote filtering, but my concern is that to use it I would first need to call store.load() which would load all persons, then call filter() to do remote filtering. I'd like to just load the subset of persons the first time.
Thanks for any advice!
Found a solution (although still open to hearing other ideas).
First, you can call a store's load() function with a config object that will be passed to an operation. The API docs for Ext.data.Operation make it clear that one of the config options is for an array of Filter objects, so you can do this:
var idFilter = Ext.create('Ext.util.Filter', {
property: 'id',
value: '100,200,300'
});
myStore.load({
filters: [ idFilter ]
});
This results in a request where the URL querystring contains ?filter=[{"property"%3Aid%2C"value"%3A100,200,300}] (in other words, a URL-encoded version of [{ property: 'id', value: '100,200,300'}]).
You can also just call myStore.filter('id', '100,200,300') without having called .load() first. Assuming you have remoteFilter=true in your store, this will make a request with the same query params shown agove.
Sidenote: you can change the keyword used for 'filter' by configuring the 'filterParam' config option for the proxy. For example, if filterParam=q, then the querystring shown above changes to: ?q=[{"property"%3Aid%2C"value"%3A100,200,300}]
Second, you can control "structure" of the filter in the querystring. In my case, I didn't want something like filter={JSON}, as shown above. I wanted a querystring that looked like this:?id=100,200,300
For this I needed to extend a proxy and override the default getParams() function:
Ext.define('myapp.MyRestProxy', {
extend: 'Ext.data.proxy.Rest',
/**
* Override the default getParams() function inherited from Ext.data.proxy.Server.
*
* Note that the object returned by this function will eventually be used by
* Ext.data.Connection.setOptions() to include these parameters via URL
* querystring (if the request is GET) or via HTTP POST body. In either case,
* the object will be converted into one, big, URL-encoded querystring in
* Ext.data.Connection.setOptions() by a call to Ext.Object.toQueryString.
*
* #param {Ext.data.Operation} operation
* #return {Object}
* where keys are request parameter names mapped to values
*/
getParams: function(operation) {
// First call our parent's getParams() function to get a default array
// of parameters (for more info see http://bit.ly/vq4OOl).
var paramsArr = this.callParent(arguments),
paramName,
length;
// If the operation has filters, we'll customize the params array before
// returning it.
if( operation.filters ) {
// Delete whatever filter param the parent getParams() function made
// so that it won't show up in the request querystring.
delete paramsArr[this.filterParam];
// Iterate over array of Ext.util.Filter instances and add each
// filter name/value pair to the array of request params.
for (var i = 0; i < operation.filters.length; i++) {
queryParamName = operation.filters[i].property;
// If one of the query parameter names (from the filter) conflicts
// with an existing parameter name set by the default getParams()
// function, throw an error; this is unacceptable and could cause
// problems that would be hard to debug, otherwise.
if( paramsArr[ queryParamName ] ) {
throw new Error('The operation already has a parameter named "'+paramName+'"');
}
paramsArr[ queryParamName ] = operation.filters[i].value;
}
}
return paramsArr;
}
});
You can also get your Model object to load a record of itself. From a controller you can do:
this.getRequestModel().load(requestID,{ //load from server (async)
success: function(record, operation) {
.....
}
}
where Request is a model class and requestID is an ID to look up. In this scenario Model object needs to define the proxy too:
proxy: {
type: 'ajax',
reader: {
type:'json',
root: 'data'
},
api: {
create : 'request/create.json', //does not persist
read : 'request/show.json'
}
}

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