How to properly use MVVM viewmodel store?
List.js:
Ext.define('some.List', {
extend: 'Ext.tree.Panel',
requires: [
'some.ListModel'
],
rootVisible : false,
hideHeaders: true,
viewModel: {
type: 'list'
},
bind: {store:'{mlists}'},
columns: [{
xtype: 'treecolumn',
dataIndex: 'name',
flex: 1,
sortable: false,
}]
});
ListModel.js:
Ext.define('some.ListModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.list',
requires: [
'Ext.data.proxy.Ajax'
],
stores: {
mlists: {
autoLoad: true,
fields: [
{ name: 'id', type: 'int'},
{ name: 'name', type: 'string' },
{ name: 'count' },
{ name: 'group' }
],
proxy: {
type: 'ajax',
api: {
read: 'php/lists/read.php'
},
reader: {
type: 'json',
rootProperty: 'lists'
}
}
}
}
});
i get error:
Uncaught TypeError: undefined is not a function Panel.js?_dc=1404313037482:430
Ext.define.bindStore Panel.js?_dc=1404313037482:430
Ext.define.reconfigure Table.js?_dc=1404313037482:1417
Ext.define.setStore Table.js?_dc=1404313037482:1376
Ext.define.privates.onBindNotify Bindable.js?_dc=1404313037482:681
Ext.define.privates.notify BaseBinding.js?_dc=1404313037482:83
Ext.define.privates.react Binding.js?_dc=1404313037482:206
Ext.define.notify Scheduler.js?_dc=1404313037482:394
Ext.define.onTick Scheduler.js?_dc=1404313037482:425
(anonymous function) Function.js?_dc=1404313037482:121
There are several issues with the code:
If it is Sencha Cmd generated then view someList should be in view directory. Of course it would work when it is elsewhere but there must be a good reason not to follow Sencha recommended directory structure. This is not related to the issue.
Sencha recommends to start namespace with a capital letter to distinguish from other variables. It should read Some, not some in this case. It is not related to the issue.
someList extends TreePanel so it must use a TreeStore. mlists is normal store. It can be cause of the issue.
If you want to define a tree store in ViewModel, configure it with type:'tree' config option and do not forget to configure also root option that is mandatory for tree stores.
Then you can bind it normally as any other store the way you are already using.
Related
I am working in a Sencha Touch app and with associated models
Parent model:
Ext.define('xx.model.TemplateModel', {
extend : 'Ext.data.Model',
requires : [
'xx.model.BrandsModel'
],
config : {
useCache : false,
idProperty: 'templates',
fields : [
{
name: 'id',
type: 'string'
},
{
name: 'name',
type: 'string'
},
{
name: 'validFrom',
type: 'auto'
},
{
name: 'validTo',
type: 'auto'
}
]
},
associations: [
{
type : 'hasMany',
associatedModel: 'xx.model.BrandsModel',
ownerModel : 'xx.model.TemplateModel',
autoLoad : true,
associationKey : 'brands'
}
]
});
and associated model:
Ext.define('xx.model.BrandsModel', {
extend : 'Ext.data.Model',
requires: [],
config : {
useCache : false,
idProperty: 'Brands',
fields : [
{
name: 'brandId',
type: 'string'
},
{
name: 'brandText',
type: 'string'
}
]
}
});
First point, this implementation is in the correct way?, because I cannot see this associated model when I am debugging in the chrome console.
Second point, What would it be the correct way to test this and to see the records?
Thank you.
I would have expected that it throws an error:
You have no "brands" field on your template model, but have specified the associationKey to be brands
You have not specified a primaryKey on the association although you use a modified idProperty on the BrandsModel
Also, please have a look at the example in the API docs how to lessen your code by using readable shortcuts.
Cant delete a record from the store.
My model
Ext.define('touch.model.FilesModel', {
extend: 'Ext.data.Model',
requires: [
'Ext.data.identifier.Uuid'
],
config: {
identifier: 'uuid',
idProperty: 'iD',
fields: [
{ name: 'iD', type: 'auto' },
{ name: 'fileName', type: 'auto' }
]
}
});
and this is my SessionStorage
Ext.define('touch.store.FilesStore', {
extend: 'Ext.data.Store',
requires: [
'Ext.data.proxy.SessionStorage'
],
config: {
storeId: 'FilesStore',
autoLoad: true,
model: 'touch.model.FilesModel',
proxy: {
type: 'sessionstorage',
id: 'FilesStore-store-unique'
},
sorters: [{
property: 'created',
direction: 'DESC'
}]
}
});
I am trying to remove a record from the store, and it doesnt work.
i tried:
var filesStore = Ext.getStore('FilesStore');
filesStore.remove(filesStore.getAt(index)); // fails on this line: "Uncaught TypeError: Cannot read property 'destroy' of null" remove from the store
filesStore.sync();
then i tried just to do
var filesStore = Ext.getStore('FilesStore');
filesStore.getAt(index).erase() // fails with: [ERROR][Anonymous] You are trying to erase a model instance that doesn't have a Proxy specified
I have a really strange problem with remote store wich looks like this:
store:
Ext.define('freetextOrder.store.Attachment', {
extend: 'Ext.data.Store',
model: 'freetextOrder.model.Attachment',
autoLoad: false,
proxy: {
actionMethods: {
create: "POST",
read: "POST",
update: "POST",
destroy: "POST"
},
type: 'ajax',
filterParam: 'filter',
remoteFilter: true,
autoSync: true,
api: {
read: 'ajax/Attachment/Cartarticle/Init',
update: 'ajax/Attachment/Cartarticle/Update',
create: 'ajax/Attachment/Cartarticle/Create',
destroy: 'ajax/Attachment/Cartarticle/Destroy'
},
reader: {
type: 'json',
root: 'results',
successProperty: 'success'
},
writer: {
type: 'json',
allowSingle: false
},
extraParams: {sid: config.sid}
},
listeners: {
datachanged: function (store) {
if (Ext.getCmp('attachmentGrid'))
if (store.getCount() > 0) {
Ext.getCmp('attachmentGrid').show();
} else {
Ext.getCmp('attachmentGrid').hide();
}
}
}
});
model:
Ext.define('freetextOrder.model.Attachment', {
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'int'},
{name: 'cartarticle_id', type: 'int'},
{name: 'orig_filename', type: 'string'},
{name: 'upload_time', type: 'date'}
]
});
And my mission impossible, load the filter from the store:
{
xtype: 'combobox',
queryMode: 'local',
mode: 'local',
fieldLabel: 'template',
id: 'attachmentTemplate',
name: 'test',
store: Ext.create('freetextOrder.store.Attachment'),
valueField: 'cartarticle_id',
displayField: 'orig_filename',
lazyInit: false,
editable: true,
invalidCls: '',
listeners: {
afterrender: function () {
var combo = Ext.getCmp('attachmentTemplate')
combo.getStore().clearFilter(true);
combo.getStore().addFilter({property: 'id', value: config.options.attTemplateIds});
combo.getStore().load();
console.log(combo.getStore().getById(49));
}
}
}
The problem is the event wich i am trying to use, i tried everything to make the damn thing load the store elements, but it just wont budge.
The console.log(combo.getStore().getById(49)); returns an object so the store is loaded. but somhow it fails to load into the options of the combobox it self....
WIERD PART IS:
When i go to the page and execute the code in chrome comand prompt:
var combo = Ext.getCmp('attachmentTemplate')
combo.getStore().clearFilter(true);
combo.getStore().addFilter({property: 'id', value: config.options.attTemplateIds});
combo.getStore().load();
The options get loaded. i am on the end of my rope. i have to past the config.options.attTemplateIds to the filter. tht is the only requirement. and tht value is only available where the xtype: 'combobox', is defined.
For idiot sake i even tried the most hack option setTimeout around the elements... still nothing... really strange behavior.
There are two things or events: the first is combo's afterRender, the second is store's load;
combo.getStore().getById(49) must use after the second event; but you put it in the combo's afterRender, while the data of store might be not already!
As i stumbeld on this question after a year and wasted AGAIN 4h trying to get it to work, without succes.. and after i rememberd the workaround.. im posting it for others to use.
You can use the extra params for your filtering. Just have to do somthing like this
var store = Ext.getStore('xxxx');
store.getProxy().setExtraParam("xxx", newValue);
store.load();
I'm having trouble trying to invoke getters/setters on a Model object that has an association with one other model. Here are the classes:
Category.js
Ext.define('Chapter05.model.Category', {
extend: 'Ext.data.Model',
fields: [
{ name: 'id', type: 'int' },
{ name: 'name', type: 'string' }
]
})
Product.js
Ext.define('Chapter05.model.Product', {
extend: 'Ext.data.Model',
requires: [
'Chapter05.model.Category'
],
fields: [
{ name: 'id', type: 'int' },
{ name: 'category_id', type: 'int' },
{ name: 'name', type: 'string' }
],
// we can use the belongsTo shortcut on the model to create a belongsTo association
associations: [
{ type: 'belongsTo', model: 'Chapter05.model.Category' }
]
})
Main.js
Ext.define('Chapter05.view.Main', {
extend: 'Ext.container.Container',
requires:[
'Ext.tab.Panel'
'Chapter05.model.Product',
'Chapter05.model.Category',
],
xtype: 'app-main',
layout: 'vbox',
items: [
{
xtype: 'button',
text: 'Category',
handler: function(evt) {
var product = new Chapter05.model.Product({
id: 100,
category_id: 20,
name: 'Sneakers'
});
product.getCategory(function(category, operation) {
// do something with the category object
alert(category.get('id')); // alerts 20
}, this);
}
}
]
});
The error occurs at the line where product.getCategory(...) is. I get the following message in Safari Web Inspector:
TypeError: 'undefined' is not a function (evaluating 'product.getCategory')
Am I forgetting to do something?
P.S.
The project(Chapter05) was generated using Sencha Cmd. Hence, the fully qualified names.
I've had a similar problem with a hasOne relation. It was solved by specifying the getters/setters and the associationKey yourself.
Something like:
belongsTo: {
model: 'Chapter05.model.Category',
getterName: 'getCategory',
setterName: 'setCategory',
associationKey: 'category_id'
}
I am using ListFilter plugin to filter results on a Grid panel. The column definition is.
{
header: 'Provider',
filter: {
type: 'list',
store: Ext.getStore('MyApp.store.Provider'),
dataIndex: 'provider_id',
labelField: 'name'
}
}
MyApp.store.Provider is created as
Ext.create('Ext.data.Store', {
storeId: 'MyApp.store.Provider',
autoDestroy: true,
autoLoad: {start: 0, limit: 50},
autoSync: true,
model: 'MyApp.model.Provider',
pageSize: 50,
proxy: {
type: 'ajax',
api: {
create: 'proxy/provider/create',
read: 'proxy/provider/read',
update: 'proxy/provider/update',
destroy: 'proxy/provider/destroy'
},
reader: {
type: 'json',
root: 'data',
successProperty: 'success',
messageProperty: 'message',
totalProperty: 'total'
},
writer: {
allowSingle: false,
type: 'json',
writeAllFields: false,
root: 'data'
}
}
});
And lastly model MyApp.model.Provider is defined as
Ext.define('MyApp.model.Provider', {
extend: 'Ext.data.Model',
fields: [
{ name: 'provider_id', type: 'int'},
'name',
{ name: 'create_time', type: 'date', dateFormat: appDateFormat },
{ name: 'status', type: 'int'}
],
idProperty: 'provider_id',
displayProperty: 'name' // A custom property used for dynamically use the property to display
})
Now this code does not show any sub-menu in the filter menu. It just shows loading. See the image.
Update
I have solved it using following filter config. This actually populates options config manually. So no store is used here.
{
type: 'list',
labelField: 'name',
options: (function () {
var opts = [];
fS.load(function (records, operation, success) {
for (var i = 0; i < records.length; i++) {
var ar = {
id: records[i].get('provider_id'),
name: records[i].get('name')
};
opts.push(ar);
}
});
return opts;
})(),
single: true
}
It seems 'id' is hard-coded. id: records[i].get('provider_id'), does not look good. Though it works.
But I am still looking for a proper way to do it.
Note: The expected behavior can be found on ExtJS 4.1.1. See this jsfiddle. I have reproduced it. But this very same thing does not work on ExtJS 4.0.7
I didn't tried this myself but you need to set the ID manually with the idField property [new to ExtJS4.1.3] which is per default set to id. So I guess this will work:
{
header: 'Provider',
filter: {
type: 'list',
idField: 'provider_id',
store: Ext.getStore('MyApp.store.Provider'),
dataIndex: 'provider_id',
labelField: 'name'
}
}
Update
OK, I looked at the source and I can now tell you that this is the answer. So will have to either live with your workarround until 4.2 is out or you can apply the following changes to your Ext.ux.grid.menu.ListMenu to make it run:
add the idField with a default value.
look within the constructor for this lines
case 'object': options.push([value.id, value[this.labelField]]); break;
// some more lines
fields: ['id', this.labelField],
and replace it with
case 'object': options.push([value[me.idField], value[me.labelField]]); break;
// some more lines
fields: [me.idField, me.labelField],
and within the onLoad function look for
itemValue = records[i].get('id');
and replace it with
itemValue = records[i].get(me.idField);
and that pretty much is it.