Loading multiple extjs comboboxes in a form - extjs

another ComboBox question.
In my table there are about 10 fields that are foreign keys, all presented with comboboxes.
How to fill all this combos in a form, without go 10 times to server to load the store of each one?

Are they stored as separate tables on the back end? If yes, then the correct way would be to load them going to the server 10 separate times. You can optimize this scenario by:
loading them all simultaneously
loading them all before you get to that page in advance
But you still would want to have 10 different stores in your ExtJs application.
If you wish to combine them into single store remember couple things
you would need to add additional field into this combined table so you can distinguish different lists.
you would load them all at once
then you would still need to separate them into different store objects because otherwise different UI components (comboboxes) won't be able to show different sets of data

Well known problem :) Typically when I have structure like this
var data = {
ForeignKeyObjectId: 123,
ForeignKeyObject: {
Id: 123,
SomeValue: 'Some text 1'
},
SomeOtherObjectId: 456,
SomeOtherObject: {
Id: 456,
SomeValue: 'Some text 2'
}
//, ... same 8 times more
}
I have to load each combo manually:
var combo1 = this.down('#foreignKeyObjectCombo');
combo1.setValue(data.ForeignKeyObject.Id);
combo1.setRawValue(data.ForeignKeyObject.SomeValue);
combo1.store.loadData([data.ForeignKeyObject], true);
var combo2 = this.down('#someOtherObjectCombo');
combo2.setValue(data.SomeOtherObject.Id);
combo2.setRawValue(data.SomeOtherObject.SomeValue);
combo2.store.loadData([data.SomeOtherObject], true);
// same 8 times more
In one of my previous projects on ExtJs 3 I made some overrides for form and combobox behaviour so that I could use form.getForm().loadData(data) once instead of manually setting value, rawValue like in this example. But that way was implicit, so I like more this way :)

Example:
Model 1
Ext.create('Ext.data.Store', {
model: 'EmployeeType',
data : [
{type: 1, description: 'Administrative'},
{type: 2, description: 'Operative'},
]
});
Model 2
Ext.create('Ext.data.Store', {
model: 'BloodType',
data : [
{type: 1, description: 'A+'},
{type: 2, description: 'B+'},
]
});
Even if your stores have Proxy, you can disable AutoLoad so you can load as many as you want in one single request like this:
Create the stores manually:
employeeType = Ext.create('Ext.data.Store', {model: EmployeeType});
bloodType = Ext.create('Ext.data.Store', {model: BloddType});
Create an Ajax request where you bring all combos at once:
Ext.ajax.request({
url: './catalogs/getalldata',
success: function(response) {
var json = Ext.decode(response.responseText);
employeeType.loadData(json.employeeTypes);
bloodType.loadData(json.bloodTypes);
//...
}
});

Related

How come store.add() method does not perform mapping?

I have a nested json data, namely:
{
name: 'alex',
tel: {
personal: '347xxxx',
work: '331xxxx'
}
}
Then the following model:
Ext.define("Employer", {
extend: 'Ext.data.Model',
idProperty: 'personalTel',
fields: [...
{name: 'personalTel', mapping: 'tel.personal'}
Finally the following store:
Ext.create('Ext.data.Store',{
model: 'Employer',
proxy: {
type: 'memory',
reader: {
type: 'json',
root: 'root'
}
},
data: myInitialData //an array containing json objects
As long as the data is contained in myInitialData the personalTel field is correctly set.
However, adding a new record to the store does not trigger the mapping and so I find myself with strange personalTel property, that is automatic IDs extjs puts!
ExtJS allows you to load multiple models via nesting when using a reader. It does not allow those models to be created when instantiating the model directly, which is what adding the object to the store does.
The idea is that each model is treated separately with its own store. Want to add a customer with a telephone number? Create the telephone number first, put it in its store, then create the customer with a reference to the telephone number.
This approach is a bit clumsy, though, and only works with models that really are separate entities.
An alternative approach would be to use a custom type, or simply to use the 'auto' type (which treats the data as a blob that you can do what you want with). Both approaches have their drawbacks.

extjs 4 line chart rendering problems

I am having trouble with extjs rendering the line chart below. Specifically, the last six values are null which are (correctly) not shown on the series line but (incorrectly) have a marker dot displayed for them (see top right of the image below).
I am pulling the graph data from a database as json:
// data store fields
Ext.define('Graphs', {
extend: 'Ext.data.Model',
fields: [
{name: 'the_quota', type: 'int'},
{name: 'count_pt_year', type: 'int'},
{name: 'date_string', type: 'string'}
]
});
// get the graph data
var graphStore = Ext.create('Ext.data.Store', {
model: 'Graphs',
proxy: {
type: 'ajax',
url: 'sqlRequest.jsp?queryName=events_getGraph',
timeout: 160000,
reader: 'json'
},
autoLoad:false
});
If I change the query to return these null values as blanks instead ('') then the json reader converts them to zeros and the values display as zero along the bottom of the graph with a series line, which is worse then having the markers plastered to the ceiling without a series line.
I haven't been able to find any config option in Ext.chart.Series to hide null values on the graph. Nor have I been able to find a config option in Ext.data.Store to return blanks as blanks and not "0".
Looking for some other workaround.
Or has anyone resolved these issues from within the library itself (ext-all.js)?
There's a config option under Ext.data.Field called useNull. If the data received cannot be parsed into a number, null will be used instead. As of right now I can't recall if that alone will fix the problem, and I have a memory of once using a custom convert function that went something like this:
convert: function(value){
if(typeof value !=== 'number') // or other similar conversion
return undefined;
return value;
}
If this doesn't work, you may need to customize your store/reader to completely exclude records containing the undesirable null value.
EDIT - Using useNull looks like this: {name: 'someName', type: 'int', useNull: true}

ExtJS 4 Change grid store on the fly

Is it posible to change grid's store in ExtJS 4?
For example, i have two models:
User = Ext.define('User',{
extend: 'Ext.data.Model',
[...],
hasMany: 'Product'
});
Product = Ext.define('Product',{
extend: 'Ext.data.Model',
[...]
});
and two grids.
The first grid is linked with Store which uses User model and loads nested json data from backend, like
{
users: [{
id: 1,
products: [
{id: 1},
{id: 2}
]
}, {
id: 2,
products: [
{id: 3},
{id: 4},
{id: 5}
]
}]
}
All i want to get is when you click on the row in the first grid, the second grid must show products of the user, without connection to the server.
All i know is that user.products(); returns a Ext.data.Store object.
So the idea is to change second grid's store to user.products();, but there is no such method grid.setStore() :-)
Thanks in advance
I think a better solution would be to :
grid1.on('itemclick', function(view, record) {
grid2.reconfigure(record.products());
);
You are looking at stores the wrong way. A store is attached to the grid forever, hence there is no grid.setStore(). You do NOT change a store for a grid, instead you change the DATA in the store for that grid.
Now, solution to your problem: You are correct with the part that you already have a instance of store with your data. ie; user.products(). Still, you will have to create a store for your grid. This store will not have any data though. Now, when you need to display products information, you need to load the grid's store with data. You can use:
load()
loadData()
loadRecord()
to load data into your store. In your case, you can do the following:
myStore = user.products();
grid.getStore().loadRecords(myStore.getRange(0,myStore.getCount()),{addRecords: false});
If you want to attach a store to a grid after the grid has been created, you can use the bindStore() method.
var store = user.products();
grid.getView().bindStore(store);
Alternatively you can also use load(), loadData(), loadRecords() methods, and copy the data into your store.
Abdel Olakara's answer helped me out when I needed to update data on something that didn't have reconfigure (custom class inheriting from Ext.form.FieldSet).
But you don't need to specify addRecords or the range for getRange, because the defaults have us covered in this case.
This means you can do:
myStore = user.products();
grid.getStore().loadRecords(myStore.getRange());

Showing a limited subset of (or individual record from) a store in an Ext.DataView

In Sencha Touch, I often need to have an Ext.DataView panel that contains a small sub-set records or even a single record from the collection in the store.
For example I might have a Model for Car which has thousands of car records in it's app.stores.cars store but I want to show a smaller subset of these items (say; just sports cars) in my listOfSportsCars DataView while also showing the larger complete set of cars in my listOfCars DataView.
My first thought was to use multiple stores. So I'd have one main store for the big list of all cars, and a second store with a filter for my subset of sportscars. However, now updating a model from one store does not automatically update the record in the other store, so this defeats the purpose of using a DataView as the changes are not updated everywhere in the page when updating records.
My second attempt was to overwrite the collectData method on the DataView, which sounded exactly like what I was after:
var card = new Ext.DataView({
store: app.stores.cars,
collectData: function(records, startIndex){
// map over the records and collect just the ones we want
var r = [];
for( var i=0; i<records.length; i++ )
if( records[i].data.is_sports_car )
r.push( this.prepareData(records[i].data, 0, records[i]) );
return r;
},
tpl: new Ext.XTemplate([
'<tpl for=".">',
'<div class="car">{name}</div>',
'</tpl>'
]),
itemSelector: 'div.car'
});
A full example can be found here.
But, although it's documented that I can/should override this method, Sencha Touch really doesn't like it when you mess around with the length of the array returned by collectData so this was a dead-end.
How do others deal with displaying/updating multiple collections of the same records?
UPDATE There was a bug preventing collectData from working as expected. The bug has since been fixed in Sencha Touch 1.1.0.
As written in the comment:
I've used your democode with the last Sencha Touch release and opened all with Google Chrome. In the current version the error is fixed. (Version 1.1)
you could use Filters in order to get a subset of the data asociated to that store.
yourstore.filter('name', 'Joseph');
Also you should define 'root' as a function so it will always return an array. Readers in sencha touch asume you're always going to get an array as response, but it's not true if you are having a JSON with a single entry, try something like this:
root: function(data) {
if (data) {
if (data instanceof Array) {
return data;
} else {
return [data];
}
}
The full code for the store could be like this:
YourApp.ViewName = new Ext.data.Store({
model: 'YourApp.models.something',
proxy: {
type: 'scripttag',
url: 'http://somerandomurl/service/json',
extraParams: {
param1: 'hello'
},
reader: {
type: 'json',
root: function(data) {
if (data) {
if (data instanceof Array) {
return data;
} else {
return [data];
}
}
}
}
},
});
Hope it helps.
I use the "filter" features in the Store. Not modifying the DataView (I use a List).
Here's a snippet where I will fiter out Programs with a catagory that fit's a regex. (I have Programs with a catagory field)
MyApp.stores.Programs.filter(function(object) {
var regex = new RegExp(filterValue, 'i');
return object.data.category.search(regex) >= 0; // found match
});
You can clear the filter like this:
MyApp.stores.Programs.clearFilter(false);
This will update the DataView (I use a List) immediately (it's amazing).
So within your filter you could just filter out sports cars, or cars of a certain price, or whatever.
Hope that helps...
For my understanding of Sencha Touch this is not the best approach.
If it can be still good for performance you shoud use a second "slave" store, with inline data (http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Store) that you can populate automatically from main store with subset of information you want to show when an event occours on the master store, i.e. load event.
If you want to deal with just one store a solution I can imagine is to use an xtemplate with "tpl if" tag in the dataview where you want to show just some information
http://docs.sencha.com/touch/1-1/#!/api/Ext. to write empty records. Maybe, also better solution, could be to use a custom filter function inside xtemplate, in order to put a css with visibility hidden on the items you don't want to see.

ExtJS: Ext.data.DataReader: #realize was called with invalid remote-data

I'm receiving a "Ext.data.DataReader: #realize was called with invalid remote-data" error when I create a new record via a POST request. Although similar to the discussion at this SO conversation, my situation is slightly different:
My server returns the pk of the new record and additional information that is to be associated with the new record in the grid. My server returns the following:
{'success':true,'message':'Created Quote','data': [{'id':'610'}, {'quoteNumber':'1'}]}
Where id is the PK for the record in the mysql database. quoteNumber is a db generated value that needs to be added to the created record.
Other relevant bits:
var quoteRecord = Ext.data.Record.create([{name:'id', type:'int'},{name:'quoteNumber', type:'int'},{name:'slideID'}, {name:'speaker'},{name:'quote'}, {name:'metadataID'}, {name:'priorityID'}]);
var quoteWriter = new Ext.data.JsonWriter({ writeAllFields:false, encode:true });
var quoteReader = new Ext.data.JsonReader({id:'id', root:'data',totalProperty: 'totalitems', successProperty: 'success',messageProperty: 'message',idProperty:'id'}, quoteRecord);
I'm stumped. Anyone??
thanks
tom
[Responding with an answer instead of a comment for code formatting...]
Some indented formatting will make the difference clear. This (correct) form returns a single object with two properties:
{
'success':true,
'message':'Created Quote',
'data': [{
'id':'610',
'quoteNumber':'1'
}]
}
Your original format returned two separate objects with mismatched properties that cannot be resolved into columns:
{
'success':true,
'message':'Created Quote',
'data': [{
'id':'610'
},{
'quoteNumber':'1'
}]
}
Turns out that the response from the server should look like this:
{'success':true,'message':'Created Quote','data': [{'id':'610','quoteNumber':'1'}]}
A subtle difference, not one that I'm certain I understand.

Resources