Extjs two instances of stores not using different models - extjs

edit http://jsfiddle.net/zns6B/4/ Added js fiddle link and running into a cannot get 'Fields' of undefined now
edit2 So i found that the second grids store model is correct with Sc.Model.B. But the records in the store have ids that are Sc.Model.A . So my store model is set to Sc.Model.B but the store is using Sc.Model.A . It still gets stores the data in the store but only as if the model was set to Sc.Model.A in the first place.
edit3 I have take all the creation of instance out of my ListGrid. I have instead added them when creating the list grid. I have added the following. This does not work either. I am at a lose for what to do.
var obj1 = Ext.create('Sc.ListGrid',{
title: "first Component",
foo: true,
id: 'firstGrid',
myStore: Ext.create('My.Store.MyStore',{model:Ext.create('My.Model.Model'});
renderTo: 'renderToMe1'
});
I am trying to generate these two grids. When foo == true i want it to generate a store with model A. When it equals false i want it to use model B. I have tried to just specifically add the My.Model.MyModel but that does not work. The second grid will somehow inherit the first models model. I have changed it just to try and use fields instead of using the model at all but the second grid still uses the first grids.
I have also tried declaring the Stores inside the initComponent as well but i get the same result either way.
var obj1 = Ext.create('Sc.ListGrid',{
title: "first Component",
foo: true,
id: 'firstGrid',
renderTo: 'renderToMe1'
});
var obj2 = Ext.create('Sc.ListGrid',{
title: "second Component",
foo: false,
renderTo: 'renderToMe2'
});
Sc.ListGrid
Ext.define('Sc.ListGrid', {
extend: 'Ext.grid.Panel',
title: 'Grid',
store: Ext.data.StoreManager.lookup('bleh'),
requires: ['stuff'],
columns: [
{ text: 'id', dataIndex: 'id' },
],
config:{
foo: null,
},
initComponent: function(){
if(this.foo == true){
Ext.apply(this,{
store: this.buildStore1()
});
}
if(this.foo == false){
Ext.apply(this,{
store: this.buildStore2()
});
}
this.callParent();
},
buildStore1:function(){
return Ext.create('Sc.Store.League.LeagueStore',{url:'somewhere',fields:["S"]});
},
buildStore2:function(){
return Ext.create('Sc.Store.League.LeagueStore',{url:'somewhere',fields:["A"]});
}
});
Also an example of a model i am trying to use as well.
Ext.define('Sc.Model.A', {
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'string'},
{name: 'type', type: 'string'},
{name: 'gamename', type: 'string'}
]
});
Ext.define('Sc.Model.B', {
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'string'},
{name: 'type', type: 'string'},
{name: 'gamename', type: 'string'},
{name: 'something1', type: 'string'},
{name: 'something2', type: 'string'},
]
});
It will create both grids and load the data from my webservice. When i check the grid with Sc.Model.B's data it will have id and type. But will not have any data for something1, and something2. My webserivce is returning json and all values are entered. There are no nulls. If i Ext.getCmp('firstGrid').getStore().getData(0); If i use Ext.getCmp('firstGrid').getStore() and check the model name. It shows Model B but reflects A

Do you need it to be done in the initComponent()??
This is a fiddle I saved from a while ago when I was trying to do something similar. If you need help tweaking it let me know and ill update it.
The main thing to note is the grid.reconfigure(store,columns);
That will change the grid's store and columns appropriately.
http://jsfiddle.net/zqG55/1/

The issue was that the proxy wasn't being set or created properly because the proxy model was referencing the previous model instance. This is my solution
var themodel = 'A.Model.SomeModel';
var myProxy = new Ext.data.proxy.Ajax({
model: themodel,
url: url,
reader: {
type: 'json',
}
});
Ext.apply(this,{
columns: modelColumns.columns,
store: Ext.create('M.Store.MyStore',{
model: themodel ,
autoLoad: true,
proxy: myProxy
})
});

Related

Using Ext.crete in Ext.define

Is it safe to use an Ext.create call in a Ext.define call ?
Example:
Ext.define('app.store.MyStore',{
extend: 'Ext.data.Store',
model: Ext.create('Ext.data.Model', {
idProperty: 'Id',
fields: [
{ name: 'Id', type: 'number' },
{ name: 'Name', type: 'number' }
]
}),
...
No, it is not. In fact with your code the model definition does not even get to the store. Try creating an instance of your store and check what fields the model of the store has:
const myStore = Ext.create('app.store.MyStore');
console.log(myStore.getModel().getFields());
The result will be an empty array. Now define your model and store like this:
Ext.define('app.store.MyModel', {
extend: 'Ext.data.Model',
idProperty: 'Id',
fields: [{
name: 'Id',
type: 'number'
}, {
name: 'Name',
type: 'number'
}]
});
Ext.define('app.store.MyStore', {
extend: 'Ext.data.Store',
model: 'app.store.MyModel'
});
Try the again to create a store and check for the model fields like above, now you will get an array with two elements, as expected.
Even if it were possible I would not recommend it. Chances are high that you will need this model definition somewhere else in you application.

Trying to use JSONStore in a combo

In our application we have a lot of name/value stores, and they are created at load time and put into a JSONStore like so :
Ext.create("Ext.data.JsonStore", {
data: data,
model: 'EM.model.controlUnit.CodeList',
storeId: "cl_" + tableId,
sorters: [{
sorterFn: aSorterFunction
}],
});
The model is pretty simple and looks like this :
Ext.define('EM.model.controlUnit.CodeList', {
extend: 'Ext.data.Model',
fields: [{
name: 'value', type: 'int'
}, {
name: 'label', type: 'string'
}, {
name: 'description', type: 'string'
}]
});
I thought stores were pretty interchangeable so I decided to use the store in the combo (There is no special combo store so I thought a JSONStore must be as good as a SimpleStore). I get the store like so :
var msDataStore = Ext.getStore("cl_t_cl_maritalstatus");
And use the store like so :
{
xtype: 'combo',
fieldLabel: 'Marital Status',
displayField: "label",
valueField: "value",
store: msDataStore
}
The combo is filled with the values from the store when I run the application, however, when I pop down the combo box, this error is thrown :
ext-debug-w-comments.js:9951 Uncaught
Ext.data.proxy.Server.buildUrl(): You are using a ServerProxy but have
not supplied it with a url.
I do not want any server proxy. These are simple locally stored name value collections.
Can JSONStores be used with combos?
If not. What is the best way to convert a JSONStore into something acceptable for the combo. I can chop, change, restructure the store object. But I just want to know if there is something simpler that I can do before going on some kind of long and pointless journey.
This problem is related 'proxy' property. Default proxy for JsonStore is 'ajax';
proxy: {
type : 'ajax',
reader: 'json',
writer: 'json'
}
You should override with 'memory' like that;
proxy: {
type: 'memory'
}
Your final store is;
Ext.create("Ext.data.JsonStore", {
data: data,
model: 'EM.model.controlUnit.CodeList',
storeId: "cl_" + tableId,
proxy: {
type: 'memory'
}
sorters: [{
sorterFn: aSorterFunction
}],
});
A JsonStore without URL is completely acceptable, but you have to make sure the combo does not trigger a load operation when clicking on the dropdown. This is done by adding to the combo definition the config option queryMode:'local':
{
xtype: 'combo',
fieldLabel: 'Marital Status',
displayField: "label",
valueField: "value",
queryMode: 'local',
store: msDataStore
}

Is there a way to define model fields in function?

The normal way to define a model is as follows:
Ext.define('App.MyModel', {
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'int'},
{name: 'name', type: 'string'}
]
});
Is there a way to specify the fields (and perhaps all the config properties) inside an init-type method, similar to how a component's config can be set in initComponent()?
It would be useful to have this capability in order to do things like set local vars for certain repetitive properties.
You can define a function anywhere you want in Javascript!
Ext.define('App.MyModel', {
extend: 'Ext.data.Model',
fields: function() {
return [
{name: 'id', type: 'int'},
{name: 'name', type: 'string'}
];
}() // important: run it immediately!
});
You could also do something like this in initComponent. I use this sometimes when creating stores within a component passing in a variable based on whether or not I want the store to autoLoad.
Ext.define('App.MyModel', {
extend: 'Ext.data.Model',
initFields: function(fieldType) {
return [
{name: 'id', type: fieldType},
{name: 'name', type: fieldType}
];
},
initComponent: function() {
var me = this;
Ext.apply(me, {
fields: me.initFields('string')
});
me.callParent();
}
});

extjs model associations in a list view

I have two models: Page and Department. I am showing pages in a list view in extjs and I would like to display the Department's Name instead of the department_id in the List view. I have not gotten to the part of actually adding the department to the page VIA the GUI, only through direct db insert statements, but I would like to at least be able to display the department name in the list view.
I have the following so far, this is showing department_id
models
Ext.define('ExtMVC.model.Department', {
extend: 'Ext.data.Model',
fields: ['name']
});
Ext.define('ExtMVC.model.Page', {
extend: 'Ext.data.Model',
fields: ['title','body','department_id'],
associations: [
{type: 'belongsTo', model: 'Department'}
]
});
stores
Ext.define('ExtMVC.store.Pages', {
extend: 'Ext.data.Store',
model: 'ExtMVC.model.Page',
autoLoad: true,
proxy: {
type: 'rest',
url: '/admin/pages',
format: 'json'
}
});
Ext.define('ExtMVC.store.Departments', {
extend: 'Ext.data.Store',
model: 'ExtMVC.model.Department',
autoLoad: true,
proxy: {
type: 'rest',
url: '/admin/departments',
format: 'json'
}
});
List View
Ext.define('ExtMVC.view.page.List' ,{
extend: 'Ext.grid.Panel',
alias : 'widget.pagelist',
title : 'All Pages',
store: 'Pages',
initComponent: function() {
this.tbar = [{
text: 'Create Page', action: 'create'
}];
this.columns = [
{header: 'Title', dataIndex: 'title', flex: 1},
{header: 'Department', dataIndex: 'department_id', flex: 1}
];
this.callParent(arguments);
}
});
controller (fwiw)
Ext.define('ExtMVC.controller.Pages', {
extend: 'Ext.app.Controller',
init: function() {
this.control({
'pagelist': {
itemdblclick: this.editPage
},
'pagelist > toolbar > button[action=create]': {
click: this.onCreatePage
},
'pageadd button[action=save]': {
click: this.doCreatePage
},
'pageedit button[action=save]': {
click: this.updatePage
}
});
},
onCreatePage: function () {
var view = Ext.widget('pageadd');
},
onPanelRendered: function() {
console.log('The panel was rendered');
},
doCreatePage: function (button) {
var win = button.up('window'),
form = win.down('form'),
values = form.getValues(),
store = this.getPagesStore();
if (form.getForm().isValid()) {
store.add(values);
win.close();
this.getPagesStore().sync();
}
},
updatePage: function (button) {
var win = button.up('window'),
form = win.down('form'),
record = form.getRecord(),
values = form.getValues(),
store = this.getPagesStore();
if (form.getForm().isValid()) {
record.set(values);
win.close();
this.getPagesStore().sync();
}
},
editPage: function(grid, record) {
var view = Ext.widget('pageedit');
view.down('form').loadRecord(record);
},
stores: [
'Pages',
'Departments'
],
models: [
'Page'
],
views: [
'page.List',
'page.Add',
'page.Edit'
]
});
Ext's associations have visibly not been designed for use in stores, but rather for working with single records... So, I agree with what has already been said, that you'd better flatten your model on the server-side. Nevertheless, it is possible to achieve what you want.
The association won't load your associated model (i.e. Department) until you call the generated getter method (i.e. getDepartment()). Trying to go this way, that is calling this method for each Page record loaded in your store would require an incredible amount of hack because the grid reacts synchronously to the refresh event of the store, while the getDepartment() method returns asynchronously...
That's why you will have to load your departments data in the same request that loads your pages. That is, your server must return records of the form:
{title: 'First Page', body: 'Lorem', department_id: 1, department: {name: 'Foo'}}
In order for your Page model's proxy to consume this, you need to configure your association this way:
Ext.define('ExtMVC.model.Page', {
// ...
associations: [{
type: 'belongsTo'
// You need the fully qualified name of your associated model here
// ... which will prevent Ext from generating everything magically
,model: 'ExtMVC.model.Department'
// So you must also configure the getter/setter names (if you need them)
,getterName: 'getDepartment'
// Child data will be loaded from this node (in the parent's data)
,associationKey: 'department'
// Friendly name of the node in the associated data (would default to the FQ model name)
,name: 'department'
}]
});
Then comes the really ugly part. Your grid's columns cannot access the associated data with the classic dataIndex property. However, provided the associated records have already been loaded, this can be accessed from a TemplateColumn like this:
{
header: 'Department'
,xtype: 'templatecolumn'
,tpl: '{department.name}'
}
Unfortunately, that will prevent you from using some more appropriate column class (date column, etc.), that you may have configured globally. Also, this column loses track of the model field it represents, which means that some features based on introspection won't be able to do their magic (the grid filter ux, for example, uses the field type to decide automatically on the type of filter).
But in the particular case you've exposed, that won't matter...
Complete Example
Here's what it gives when you bring it all together (or see it in action)...
Ext.define('ExtMVC.model.Department', {
extend: 'Ext.data.Model',
fields: ['name'],
proxy: {
type: 'memory'
,reader: 'json'
,data: [
{id: 1, name: 'Foo'}
,{id: 2, name: 'Bar'}
,{id: 30, name: 'Baz'}
]
}
});
Ext.define('ExtMVC.model.Page', {
extend: 'Ext.data.Model',
fields: ['title','body','department_id'],
associations: [{
type: 'belongsTo'
,model: 'ExtMVC.model.Department'
,getterName: 'getDepartment'
,associationKey: 'department'
,name: 'department'
}],
proxy: {
type: 'memory'
,reader: 'json'
,data: [
{title: 'First Page', body: 'Lorem', department_id: 1, department: {name: 'Foo'}}
,{title: 'Second Page', department: {name: 'Bar'}}
,{title: 'Last Page', department: {name: 'Baz'}}
]
}
});
Ext.define('ExtMVC.store.Pages', {
extend: 'Ext.data.Store',
model: 'ExtMVC.model.Page',
autoLoad: true
});
Ext.define('ExtMVC.view.page.List', {
extend: 'Ext.grid.Panel',
alias : 'widget.pagelist',
title : 'All Pages',
store: Ext.create('ExtMVC.store.Pages'),
initComponent: function() {
this.tbar = [{
text: 'Create Page', action: 'create'
}];
this.columns = [
{header: 'Title', dataIndex: 'title', flex: 1}
,{header: 'Department', xtype: 'templatecolumn', flex: 1, tpl: '{department.name}'}
];
this.callParent(arguments);
}
});
Ext.widget('pagelist', {renderTo: 'ct', height: 200});
As one of the comments mentions, it would be a lot simpler to bind the department name to the Pages model when working with a grid/list. Denormalisation for display is not a bad thing.
An alternative is perhaps reverse the modelling, in that you could say the a department 'hasMany' Pages.
This allows you define the primary and foreign keys on the relationship and then your department store will (per row) have a automatic 'pages()' store which will contain the child info.
I've typically done this for master/detail forms where I want to bind 'pages()' to a list/grid, but keep the department model as the master record on a form for example.
Why shouldn't use renderer config function on the grid's column ?
This implies to (auto)load the departments store upfront on application launch (you might need it in more places anyway, think of grid cell editor as full department list).
{
name:'Department',
dataIndex:'department_id',
renderer: function(deptId){
return Ext.data.StoreManager.lookup('Departments').getById(deptId).get('name');
}
}
PS: I was using myself denormalisation for display, but doesn't feel good ;)

ExtJS4: Value read in Store does not get populated in datafield

As part of my EXTJS 4 learning process, I am trying to establish a simple process of database connection - loading a value in a data Store - taking the value and placing it in a dataField.
The data is loaded fine from the php script and placed into the Store via a json call. (as confirmed through FireBug)
However, the dataField, does not seem to be able to load the value.
Here is what I have so far:
//Model definition
Ext.define('FingerModel', {
extend: 'Ext.data.Model',
fields: [
{name: 'name', type: 'string'}
]
});
//Store Definition
var est_data = new Ext.data.Store({
model: 'FingerModel',
proxy: {
type: 'ajax',
url: 'finger.php',
extraParams: {opt: 'getName'},
reader: {
type: 'json',
root: 'results',
totalProperty: 'total'
}
},
autoLoad: true,
// turn off remote sorting
remoteSort: false
});
//Form definition
var fingerForm = Ext.create('Ext.form.Panel', {
width: 500,
title: 'Finger',
waitMsgTarget: true,
items: [{
xtype: 'fieldset',
title: 'Finger Form',
items: [{
xtype:'textfield',
fieldLabel: 'Location Name',
name: 'name'
}]
}]
});
fingerForm.getForm().loadRecord(FingerModel);
Anybody see anything obvious that I'm doing wrong?
Thanks in advance.
M.
Ext.form.field.Text does not have a 'store' property. How would it know which row of the store to use?
You should use Form.loadRecord() to load the model into the form, and it will set form fields with the same name as the model field names.

Resources