Hullo there,
I am building a Sencha Touch iPhone app that utilises a TreeStore which works nicely with a NestedList but I've hit a wall.
It needs a way to read data in a static store (the TreeStore), load it into localStorage so it's writable and then save some user preferences. I've figured out that the localStorageProxy is most likely what I need (I was relieved to discover I didn't have to serialise and deserialise the JSON store manually!) but after reading the documentation on it and trying and failing a few things I'm at a loss.
As it stands, it generates the localStorage… storage but it's empty, this I guess is because I haven't loaded anything into it but on doing app.stores.event_store.load(); the screen is blank and Console shows that the localStorage is empty with no errors.
My model:
Ext.regModel('Events', {
fields: [
{name: 'text', type: 'string'},
{name: 'info', type: 'string'},
{name: 'attend', type: 'boolean', defaultValue: 'false'}
]
});
My store:
app.stores.event_store = new Ext.data.TreeStore({
model: 'Events',
root: data,
storeId: 'eventStoreId',
proxy: {
type: 'localstorage',
id: 'eventsAttending',
url: 'store.js',
reader: {
type: 'tree',
root: 'items'
}
}
});
app.stores.event_store.load();
It probably stems from a lack of a fundamental understanding of how these models and stores interact but if anyone could help, good karma will be sent your way.
The full application can be found at it's GitHub repo.
-fetimo
The TreeStore expects hierarchical data
var data= {
text: 'Groceries',
items: [{
text: 'Drinks',
items: [{
text: 'Water',
items: [{
text: 'Sparkling',
leaf: true
},{
text: 'Still',
leaf: true
}]
},{
text: 'Coffee',
leaf: true
}]
}]
}
But the LocalStorageProxy is unable to match the model for this data structure.
I would use an AJAX request to load the "store.js" file using the localStorage directly to "store" the data blob.
Ext.Ajax.request({
url: 'store.js',
success: function(response, opts) {
localStorage.StoreJSBlob = response.responseText;
}
});
then your store will look something like this:
var store = new Ext.data.TreeStore({
model: 'Events',
root: localStore.StoreJSBlob,
proxy: {
type: 'ajax',
reader: {
type: 'tree',
root: 'items'
}
}
});
You just have to chain the events together correctly, as the async calls could trip you up
Related
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();
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.
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.
Using ExtJS4 Store, I want to execute a Javascript function to return json data, ie. getMyJsonData, to load the data into the store.
function getMyJsonData() {
var myjson = {... };
return myjson;
}
Trawling through the doco, I can't find a way to define a callback function to load the data, all I found was a Memory Store which loads an already defined data object.
How can I call a function instead ?
Ext.define('perhoo.store.Users',
{
extend: 'Ext.data.Store',
model: 'perhoo.model.User',
autoLoad: true,
data : {
users: [
{ id: 1, name: 'joe44', email: 'joe#joe.com'},
{ id: 2, name: 'bloggs44', email: 'bloggs#joe.com'}
]
},
proxy: {
type: 'memory',
data: this.data,
reader: {
type : 'json',
root : 'users'
}
}
EDIT
The reason I want to call a function is because I want to execute LinkedIn API.
And going through Ext JSONP Proxy (as it's cross domain) makes things 10x more complicated as I have to get LinkedIn auth etc etc (which I don't know how to do it yet)
i.e.
var mydata = null;
function onLinkedInAuth() {
// Linked in api to find connections
IN.API.Connections("me").result( function(result) {
mydata = result;
} );
}
ExtJS4's store using proxy to load data, check below:
var myStore = Ext.create('Ext.data.Store', {
model: 'User',
proxy: {
type: 'ajax',
url : '/users.json',
reader: {
type: 'json',
root: 'users'
}
},
autoLoad: true
});
When executed, it reads data from the url, /users.json, and store into itself.
Also, if you want to process data yourself and load them into store yourself, you can check below:
//this is the model we will be using in the store
Ext.define('User', {
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'int'},
{name: 'name', type: 'string'},
{name: 'phone', type: 'string', mapping: 'phoneNumber'}
]
});
//this data does not line up to our model fields - the phone field is called phoneNumber
var data = {
users: [
{
id: 1,
name: 'Ed Spencer',
phoneNumber: '555 1234'
},
{
id: 2,
name: 'Abe Elias',
phoneNumber: '666 1234'
}
]
};
//note how we set the 'root' in the reader to match the data structure above
var store = Ext.create('Ext.data.Store', {
autoLoad: true,
model: 'User',
data : data,
proxy: {
type: 'memory',
reader: {
type: 'json',
root: 'users'
}
}
});
And you could easily usesetProxy to change the behavior when you want.
Links:
http://docs.sencha.com/ext-js/4-0/#!/api/Ext.data.AbstractStore-method-setProxy
http://docs.sencha.com/ext-js/4-0/#!/api/Ext.data.proxy.Memory
I want to dynamically set up the left-side navigation menu just like iPad-style.
So, I make some modification on the demo example. You could also visit this official example here.
sink.StructureStore = new Ext.data.TreeStore({
model: 'Demo',
//root: {
// items: sink.Structure
//},
proxy: {
type: 'ajax',
url: 'words.json',
reader: {
type: 'json',
root: 'items'
}
}
});
For easier implementation, I try to get the JSON data from the "words.json".
(Ideally, JSONP type is better...tried, but no luck.)
Here is the content of "words.json":
{
text: 'User Interface',
cls: 'launchscreen',
items: [{
text: 'Buttons',
card: demos.Buttons,
source: 'src/demos/buttons.js',
leaf: true
},
{
text: 'Forms',
card: demos.Forms,
source: 'src/demos/forms.js',
leaf: true
},
{
text: 'List',
card: demos.List,
source: 'src/demos/list.js',
leaf: true
}]
}
It ends up nothing appearing. What's wrong? Do I mistake it? (API here)
What do I want to do?
Like a dictionary, left side are those navigation items of word. On clicking it, the meaning of the word will be showed in right-side view.
I can't run NestedList example in sencha framework. Clicking on the table cell and push another view on it (i.e., in Sencha: NestedList) is what I want to do.
Have tried and no luck:
use the NestedList example
replace proxy with ScriptTagProxy (JSONP)
use easier proxy implementation (showed in the code)
I am not so sure whether my description is clear enough or not, feel free to tell me which part is unclear. And thanks in advance!
If words.json looks like what you have above then that could be your problem.
This is what it should look like.
{
"text": "User Interface",
"cls": "launchscreen",
"items": [{
"text": "Buttons",
"source": "src/demos/buttons.js",
"leaf": true
}, {
"text": "Forms",
"source": "src/demos/forms.js",
"leaf": true
}, {
"text": "List",
"source": "src/demos/list.js",
"leaf": true
}]
}
I've also attached a fully working copy of what you wanted using both a memory proxy (default) and an ajax proxy.
Ext.regApplication({
name: 'test',
launch : function(){
var nL = new Ext.NestedList({
store: test.stores.testTreeStore,
fullscreen: true,
itemTextTpl : '{text}'
});
}
});
Ext.ns('test.data');
test.data.words = {
text: 'User Interface',
cls: 'launchscreen',
items: [{
text: 'Buttons',
source: 'src/demos/buttons.js',
leaf: true
},
{
text: 'Forms',
source: 'src/demos/forms.js',
leaf: true
},
{
text: 'List',
source: 'src/demos/list.js',
leaf: true
}]
};
test.models.testTreeModel = Ext.regModel('testTreeModel', {
fields: ['text','source','card','leaf'],
proxy: {
type: 'memory',
data: test.data.words,
//type: 'ajax',
//url: 'words.json',
reader: {
type: 'json',
root: 'items'
}
}
});
test.stores.testTreeStore = new Ext.data.TreeStore({
model: 'testTreeModel'
});