Use localstorageProvider and localstorageProxy in extjs4 - extjs

i try to use a combination of Ext.state.LocalStorageProvider and Ext.data.proxy.LocalStorage anyone know a way to do that?
In my app (is use extjs 4.2) i initialize the localStorageProvider in this way:
var storageProvider = new Ext.state.LocalStorageProvider( {prefix: 'sample'});
Ext.state.Manager.setProvider(storageProvider);
With this configuration if i use stateful component all is save and reload.
The problem is came out when I had to introduce in my app a TabPanel with a dynamic set Tab dependent on the user's local session.
The tabs contain a set of information that I'm going to save in a LocalStorage proxy dataStore, but when I close the tabpanel the datastore is deleted, as if it were a memoryproxy datastore.
There is a way to transfer the datatstore in a state manage by the Ext.state.LocalStorageProvider?
Any kind of help is usefull.
[EDIT]: This is my code for localstorageStore.
Ext.define('Book', {
fields: ['id', 'name', 'layout'],
extend: 'Ext.data.Model',
});
var layoutStore = Ext.create('Ext.data.Store', {
model: "Book",
proxy: {
type: 'localstorage',
id : 'sample'+'_books'
}
});
For add element in my store, i use:
store.add(newElement);
store.synch();
For edit element for example only a field, in the store:
var record = store.getAt(index);
record.data.layout = encodeValue;
store.insert(index, record);
and for remove element i use
store.remove(record);
store.sync();

Related

State provider does not restore sorters properly

I'm trying to store my grid state using the Ext.state.CookieProvider. The problem is I can't restore sorters parameters while state itself (width, order) is restoring properly.
First I've created cookieprovider in init() method of the viewport viewcontroller:
Ext.state.Manager.setProvider(Ext.create('Ext.state.CookieProvider', {}));
My store is set to auto load with remote sorting:
Ext.define('MyApp.requests.store.QueryRequestsGridStore', {
extend: 'Ext.data.Store',
model: 'MyApp.requests.model.QueryRequestsGridModel',
alias: 'store.queryRequestsGrid',
remoteSort: true,
autoLoad: true,
proxy: {
startParam: 'offset',
limitParam: 'limit',
url: '/requests',
noCache: false,
type: 'ajax',
reader: {
type: 'json',
rootProperty: 'data',
totalProperty: 'total'
}
},
});
Store is defined in grid using viewmodel binds:
bind: {
store: '{queryRequestsGrid}'
},
I'm loading the grid containing store from the viewport viewcontroller on button click like this:
var panelToAddName = Ext.create('MyApp.requests.view.QueryRequestsGridView', {});
var mainViewPort = Ext.ComponentQuery.query('#mainViewPort')[0];
var regionPanel = mainViewPort.down('[region=center][xtype=panel]');
regionPanel.removeAll();
regionPanel.add(panel);
Cookie contains sorters, but grid is loaded without any sort parameters.
"storeState":{"sorters":[{"root":"data","property":"date_completed","direction":"ASC"}]}}}
I've dug into the ext-all-debug.js source file and found initState() method of a 'Ext.state.Stateful' class.
initState: function() {
var me = this,
id = me.stateful && me.getStateId(),
hasListeners = me.hasListeners,
state, combinedState, i, len, plugins, plugin, pluginType;
if (id) {
combinedState = Ext.state.Manager.get(id);
if (combinedState) {
state = Ext.apply({}, combinedState);
if (!hasListeners.beforestaterestore || me.fireEvent('beforestaterestore', me, combinedState) !== false) {
plugins = me.getPlugins() || [];
for (i = 0 , len = plugins.length; i < len; i++) {
plugin = plugins[i];
if (plugin) {
pluginType = plugin.ptype;
if (plugin.applyState) {
plugin.applyState(state[pluginType], combinedState);
}
delete state[pluginType];
}
}
me.applyState(state);
if (hasListeners.staterestore) {
me.fireEvent('staterestore', me, combinedState);
}
}
}
}
},
If to log me.store from inside of this method, the store is shown in console as ext-empty-store while me is my loaded grid. Seems like state is applying before the store is properly loaded.
If to reuse the initState method inside beforerender grid event, sorters are restoring from cookie properly.
Any suggestions?
I have not worked with viewmodel binds as the sole bind between store and grid, and can't comment on whether that is supposed to work at all, or just by accident.
But I know that the viewmodel is processed very late, because the view has to be fully initialized first (including applyState), so the viewmodel can find all the components it wants to bind the listeners to.
So please try to add the store using any of the two "old-school" methods that work even without the viewmodel: store:'MyStoreId' or store:Ext.create('MyApp.store.MyStore') on the grid. That way, the store should be bound to the grid before applyState.
Furthermore, I see another issue you should address: Your store loads directly after store init. (autoLoad:true). At that time, it is not yet bound to the grid; thus, no sort/filter has been applied, which means that with remoteSort/remoteFilter enabled, you are sending too many requests to the server. I would recommend to load the store only after it has been applied to the grid (in grid.initComponent after the callParent call, or from grid.boxready listener). If you really want to use autoLoad, I'd recommend to look into setAutoLoad method

Loading Model from another model file in extjs

formGrid=me.abstractComponent.query('grid[itemId=grid]')[0],
Objmodel= Ext.create('Ext.document.model.GridModel');
formStore = Ext.create('Ext.data.Store',{
model:Objmodel,
data: data
});
formGrid.bindStore(formStore);
formStore.load();
I've been trying to load the store using the model, but error occurs saying that model not found. Is there any way such that i can load the model from another file in store above?
Store model attribute means you have to give the definition of Model not instance of model
Ext.define('app.model.User', {
extend: 'Ext.data.Model',
fields: ['first', 'last']
});
var store = Ext.create('Ext.data.Store',{
model: 'app.model.User'
});
Now if you want to add data to grid using model
var model = Ext.ModelManager.create({
first: 'Ed',
last: 'Spencer'
}, 'app.model.User');
store.add(model)

Extjs combobox is not auto-selecting the displayField

UPDATE -
I HAVE ALSO MADE A MOCK IN JSFIDDLE http://jsfiddle.net/mAgfU/371/
AND WITH A FORM PANEL : http://jsfiddle.net/kA6mD/2/
I have the bellow comboox.
When I use the following code to set the form values:
this.form.loadRecord(data);
the entire form is acting ok except from the combo.
instead of giving me the displayField, I get the valueField in the display.
As you can see in the image above, the combo should show the word "Walla" (displayField) instead of "1" (valueField)
Ext.define('App.view.ForeignCombo', {
extend: 'Ext.form.ComboBox',
alias: 'widget.foreigncombo',
queryMode: 'local',
displayField: 'Name',
valueField: 'Id',
editable: false,
matchFieldWidth: false,
constructor: function(config) {
Ext.apply(this, config);
Ext.define('BrnadComboModel', {
extend: 'Ext.data.Model',
fields: ['Id', 'Name']
});
this.store = Ext.create('Ext.data.Store', {
model: 'BrnadComboModel',
autoLoad: true,
proxy: {
type: 'ajax',
url: '/api/Brand/',
reader: {
type: 'json',
root: 'Results'
}
},
pageSize: 50,
});
this.callParent();
}
}
);
this is how I use it:
{
"xtype": 'foreigncombo'
, "name": 'Brand.Id'
, "fieldLabel": 'Brand.Id'
}
There is no race bewtween the form display and the combo ajax request, the combo store is autoLoad:true, meaning I see that it has already been loaded...
Thanks
I used your fiddle a an example. Place a breakpoint in line 87 (Ext.ComponentQuery.query('comobobox')....), in this fiddle http://jsfiddle.net/kA6mD/9/, and set a watch to Ext.ComponentQuery.query('combobox')[0].store.data.. you'll notice the store has no data. This may be linked to what I mentioned in the comment.
I know there must be a better way of doing this, but what I usually use as a workaround is either load the store at some point before in the app or use a synchronous Ext.Ajax.request and load each record at a time in the store.
As this is a combo for brands I suppose you could load the store before (i.e. app load) and lookup for the store instead of creating a new one each time you create a foreigncombo component, so the first solution should work.
As for the second workaround it should also work, it takes a little bit more coding but its actually pretty easy. It should look something like this...
Ext.Ajax.request({
url:'your/url/',
async:false,
.....
success:function(response){
var records = Ext.JSON.decode(response.responseText);
for(var m=0; m<records.length; m++){
var record = Ext.create('YourBrandModel',{
abbr:records[m].abbr,
name:records[m].name
});
store.add(record);
}
}
})
You should do this as few times as possible as it may slow down the user experience if it gets called everytime you create a "foreigncombo", so checking if this store exists before creating one might be a good idea.
Please take in cosideration that I have not tested this code, so you may have to tweak it a little in order for it to work. But it should get you on tracks.

sencha touch 2: binding associations data to existing store

I have a simple data model that looks something like this (actual code below):
model Game:
fields: id, team_1_id, team_2_id
model GameScore:
fields: id, game_id, team_1_score, team_2_score, is_final, submission_date
model SpiritScore:
fields: id, game_id, team_1_score, team_2_score
What I want seems simple. I already have code that loads Games and GameScores in bulk. I have a 'Game' instance in hand, and can call gameScores(). And I get a store, but it's empty. I have code that will dynamically load it, by placing the store into the model's hasMany definition. But what I would really like is some way to bind the Game.gameScores() call to the my existing GameScores store. Even if it used a normal filter underneath, that gives me a single record that I can bind and use in a view. (Important note: the data does not come in nested form.)
This leads to my second question. Game:GameScores is 1:many, but I only ever display the most recent one (from live score reporting). What is the general approach here? I can also manually build a filter from the game_id, but I can only bind 1 record to a view, so I don't see how I can bring that other information into a view, short of a proper hasMany relationship. Is there another way?
Any and all advice, including telling me to RTFM (with a link to the relevant manual) would be greatly appreciated! I've been pulling my hair out on this (pro bono side project) for the last week.
Cheers!
b
Ext.define('TouchMill.model.Game', {
extend: 'Ext.data.Model',
config: {
fields: [ 'id', 'team_1_id', 'team_2_id' ],
hasMany: {
model: 'TouchMill.model.GameScore',
name: 'gameScores',
},
},
});
Ext.define('TouchMill.model.GameScore', {
extend: 'Ext.data.Model',
config: {
fields: [ 'id', 'game_id', 'team_1_score', 'team_2_score', 'is_final', 'submission_date', ],
},
// belongsTo necessary? Don't think so unless I want parent func?
});
Ext.define('TouchMill.model.SpiritScore', {
extend: 'Ext.data.Model',
config: {
fields: [ 'id', 'game_id', 'team_1_score', 'team_2_score', ],
},
},
I've never used touch, so I'm speaking about Ext4 here (4.2 to be precise)... And, your model definitions seem a bit broken to me (is that working with touch?). But whatever, you'll get the general idea. If my code don't work in touch, please try with Ext4.
Also, I understood that you're loading all your scores at once. If that's not the case, my solution will need to be adapted...
So, my general reasoning is the following: if you've loaded all your scores in memory, then why not use a memory proxy that uses the score store's data as the data source for the store generated for the association? I tried that and, quite to my surprise, it worked without a glitch.
To understand this, you need to know that a proxy is an independant data source, that is a proxy can be shared between multiple stores without problem. On the other hand, a store is expected to be bound to a single view or task. For example, if you bind the same store to two different grids, then filtering the first grid will affect the second as well.
And while most proxies do not "contain" their data, memory proxy do. Here's a relevant excerpt of Ext.data.proxy.Memory#read method:
resultSet = operation.resultSet = me.getReader().read(me.data)
So, enough theory, here's the proof of concept (tested in this fiddle):
// I instantiate this proxy myself in order to have a reference available
var masterScoreProxy = Ext.create('Ext.data.proxy.Memory');
Ext.define('TouchMill.model.GameScore', {
extend: 'Ext.data.Model',
fields: [ 'id', 'game_id', 'team_1_score', 'team_2_score', 'is_final', 'submission_date' ],
// I've used a remote server to ensure this all works even asynchronously
proxy: {
// configure your own
}
});
Ext.define('TouchMill.model.Game', {
extend: 'Ext.data.Model'
,fields: [ 'id', 'team_1_id', 'team_2_id' ]
,hasMany: {
model: 'TouchMill.model.GameScore'
,name: 'gameScores'
// required in order to avoid Ext autogenerating it as 'touchmill.model.game_id'
,foreignKey: 'game_id'
// needed if we don't want to have to call gameRecord.gameScores().load()
,autoLoad: true
// first part of the magic: make the generated store use my own proxy
,storeConfig: {
proxy: masterScoreProxy
}
}
});
// Just mocking a store with two games
var gameStore = Ext.create('Ext.data.Store', {
model: 'TouchMill.model.Game'
,data: [{id: 1}, {id: 2}]
,proxy: 'memory'
});
// Creating the "master" score store (that will use the model's proxy)
var scoreStore = Ext.create('Ext.data.Store', {
model: 'TouchMill.model.GameScore'
// second part's in there
,listeners: {
load: function(store, records, success) {
if (success) {
// 1. replace the data of the generated association stores' proxy
// (I must say I'm quite surprised that I didn't had to extract the data of
// every records, nor to configure a reader and all for my shared proxy...
// But hey, that works!)
masterScoreProxy.data = records;
// 2. update already generated stores
// Alternatively, you could call gameRecord.gameScores().load() individually
// before each usage of gameRecord.gameStores()
gameStore.each(function(record) {
var childStore = record.gameScoresStore;
if (childStore) {
childStore.load();
}
});
}
}
}
});
// test first load
scoreStore.load({
callback: function(records, operation, success) {
if (success) {
// and here's to prove it
gameStore.each(function(record) {
record.gameScores().each(function(score) {
console.log('Game ' + record.id + ': ' + JSON.stringify(score.data, undefined, 2));
});
});
testRefreshedData();
}
}
});
function testRefreshedData() {
// test refreshing
scoreStore.load({
callback: function(records, operation, success) {
if (success) {
console.log('--- Scores have changed ---');
gameStore.each(function(record) {
record.gameScores().each(function(score) {
console.log('Game ' + record.id + ': ' + JSON.stringify(score.data, undefined, 2));
});
});
}
}
});
}
Regarding your other questions...
If you have a 1:n for Game:Score, you've got a 1:1 for Game:MostRecentScore... So, I'd try to use that.
As for the view, there should always be a way -- even if hackish -- to access data nested in your records. The way will depend on what you're calling view here... See, for example this question.

ExtJS 4.1 Grid panel loading is too long with over 10k entries

I've a new problem today (first one : ExtJS 4.1 How to create a window with grid dynamically)
So, to resume my application is to display data about cars. I load all my cars (over 10k) like this :
Ext.create('Ext.data.Store', {
storeId: 'CarStore',
model: 'Car_Model',
proxy: {
type: 'ajax',
url: './cars.json',
reader: 'json'
},
autoLoad: {
callback: displayMenu
}
});
My displayMenu function display a menu with buttons like this :
- Brand1
- Model1
- Model2
- Brand2
-Model1
- Etc
- All Cars
So when I create my buttons (depending of brand/model in my json store) I do this (exemple for a model button) :
var CarStore = Ext.data.StoreManager.lookup('CarStore');
var aCarButton= Ext.create('Ext.Button', {
text: myModelText,
handler: function(button, e) {
var grid = Ext.create("CarsGrid", { store: CarStore });
grid.filterByModel(myModelText);
var win = Ext.create("CarsWindow", { title: myModelText, items: [ grid ] });
win.show();
}
});
aMenuPanel.add(aAircraftButton);
Thus when I click on a button that display all cars for a brand/model in a grid that is in a window.
My problem is that the loading before display my data take about 5 second for a model, 10 for a brand and 2 min for all my cars.
PS : I use the same store for my button creation and all grids to display but with a filter (by brand, model, or none) like you see in grid.filterByModel(myModelText);
In my previous Answer VoidMain advised me to use buffered: true but that produce an error when I click a button.
If paging or buffering is absolutely not an option, you could try bumping up the timeout setting:
Ext.create('Ext.data.Store', {
storeId: 'CarStore',
model: 'Car_Model',
proxy: {
type: 'ajax',
url: './cars.json',
reader: 'json',
timeout: 180000 //3 minutes * 60 sec/min * 1000 millis/sec
},
autoLoad: {
callback: displayMenu
}
});
You're creating a menu with submenus right?
1)Don't add all models from all brands on this menu. It's 10K elements dude, nobody will browser that whole menu!! And worse, if visitor is searching for a specific car model.......... (imagine how much time to find it in a big menu like that!).
Ok, the solution.
First, use caching in PHP. IDK what framework/CMS you're using, Wordpress has built-in cache interface that can be implemented by plugins. Instead of making a query on every pageload, you store the result in HD and use it.
Second, make a better organization of your site. Instead of having trusting all its content access in a single big menu, create posts for each model, categories for brands, etc. Make a page listing brands, linking to a page listing models, etc. You already have this data in a DB, PHP can build dynamic HTML to list them, and with CSS you can make those lists very beautiful.

Resources