Creating Accordion layout items from datastore in extjs - extjs

I have a Panel whose layout is accordin. I have a datastore which gets the data from server in json format and maps to a model. The data I get is a list of that model. Now in my view, I need to access the store and get all the records, and for each record I need to create a Panel and add it to the parent Panel.
My Store
Ext.define('NOS.store.ResultGroupsStore', {
extend: 'Ext.data.Store',
requires : ['NOS.model.ResultGroup'],
model: 'NOS.model.ResultGroup',
fields:['groupName'],
alias: 'widget.resultStore',
// autoLoad: true,
proxy: {
type: 'ajax',
url: 'showResult.nos',
reader: {
type: 'json'
}
}
});
My View
Ext.require('Ext.panel.Panel');
Ext.define('NOS.view.ResultView' ,{
extend: 'Ext.panel.Panel',
alias:'widget.results',
title: 'My title',
layout:'accordion',
items:null,
initComponent : function() {
console.log("reached in view initialise");
results = Ext.create('NOS.store.ResultGroupsStore');
results.load();
results.each(function(aResultGroup, index) {
console.log('Record ' + index);
});
console.log("End in view initialise");
this.callParent(arguments);
},
});
But it never enters the loop above. I am sure the data is loaded properly to the store because when I use a grid.Panel and map the columns it renders the data.
Please help!!

Store loading is asynchronous, when you you're iterating over the store, it hasn't loaded yet so the store is empty. You need to do it in the store callback.
Ext.require('Ext.panel.Panel');
Ext.define('NOS.view.ResultView' ,{
extend: 'Ext.panel.Panel',
alias:'widget.results',
title: 'My title',
layout:'accordion',
items:null,
initComponent : function() {
var results = this.results = Ext.create('NOS.store.ResultGroupsStore');
results.on('load', this.onStoreLoad, this, {
single: true
});
results.load();
this.callParent(arguments);
},
onStoreLoad: function() {
this.store.each(function(rec){
});
}
});

Related

How to access other Store records from Model's convert method in ExtJS6?

I am trying to have a calculated field inside a Model that looks something like value (maxValue), where maxValue is the maximum value among all other records currently loaded (think current page of a Grid).
Model:
Ext.define('MyApp.model.Example', {
extend: 'Ext.data.Model',
fields: [
{name: 'id'},
{name: 'value'},
{name: 'calculated_value', convert: function(value, record){
//how to access all the records here, not just the current one?
}}
]
});
A model is not aware of records, it just represents a single record, and the convert method is intended to allow you to convert the value, or combine other fields into a single value (note unless you define "depends" to reference other fields in this instance the convert is only called when the data is loaded and not if the fields it depends on change).
When you create a grid, the grid uses a store, the store contains a collection of records, and this would be the place to do this.
Within the config for your store you could add a listener for 'datachanged' which will fire whenever records are added or removed from the store, from here you could work through all the records in the store, work out a max value, and update records with it.
Ext.create('Ext.data.Store', {
model: 'Example',
proxy: {
type: 'ajax',
url : 'example.json',
reader: {
type: 'json'
}
},
listeners:{
datachanged:function(store){
var maxValue=store.max('value');
store.beginUpdate();
store.each(function(record){
record.set('calculated_value',maxValue);
});
store.endUpdate();
}
}
});
If you are loading the store from a server, then you will be implementing a reader, this would possibly be an even better place to do this.
Ext.create('Ext.data.Store', {
model: 'Example',
proxy: {
type: 'ajax',
url : 'example.json',
reader: {
type: 'json',
transform: {
fn: function(data) {
var maxValue=0;
Ext.each(data.items,function(item){
if(item.value>maxValue) maxValue=item.value;
});
Ext.each(data.items,function(item){
item.calculated_value=maxValue;
});
return data;
},
scope: this
}
}
},
});
Its also worth clarifying if you really need to duplicate this value, I presume you then want to reference somehow within the grid, perhaps in a renderer, instead you could just set the value once on the store:
Ext.create('Ext.data.Store', {
model: 'Example',
proxy: {
type: 'ajax',
url : 'example.json',
reader: {
type: 'json'
}
},
listeners:{
datachanged:function(store){
store.maxValue=store.max('value');
}
}
});
Then within your grid column config, add/update a renderer (in this example I display value as a percentage of maxValue):
{
dataIndex:'value',
renderer:function(value, metaData, record, rowIndex, colIndex, store, view){
return Math.round((100/store.maxValue)*value)+'%';
}
}

load store with dynamic param

I want to load a record based on my tree node selection and show it in my form. But my store is not loaded in my controller handler. Where and how should I load the store with my parameter?
var me = this;
var idTag= me.getMyNode();
me.getTagStore().on('beforeload', function(store, operation, eOpts) {
operation.params = {
idTag: idTag
};
}, me);
// it will not be loaded here and I get rec=undefined
var rec = me.getTagStore().load().getAt(0);
me.getMyForm().loadRecord(rec);
And here is my store:
Ext.define('TTT.store.Tag', {
extend: 'Ext.data.Store',
requires: [
'TTT.model.Tag'
],
model: 'TTT.model.Tag',
proxy: {
type: 'ajax',
url: 'tag/find.json',
reader: {
type: 'json',
root: 'data'
}
}
,baseParams: {
idTag: 30
},
});
You can call the load method manually whenever needed and pass any parameters needed, like this:
me.getTagStore().load({
params: {
idTag: idTag
}
});
When you call this you should see your request with the parameter in your console window in the browser.
In your code above, this line:
// it will not be loaded here and I get rec=undefined
var rec = me.getTagStore().load().getAt(0);
The load operation takes a little time, not much but it does take a little time. You need to listen to the load event on your store to be able to do things like this, here is an example:
me.getTagStore().load({
params: {
idTag: idTag
},
scope: this,
callback: function(records, operation, success) {
// the operation object
// contains all of the details of the load operation
console.log(records);
}
});
Checkout the documentation for store.

Extjs 5 how to store user detail after login, and use it for data binding in a ViewModel

I am new to extjs 5. Been spending alot of time trying to learn it, and have a hard time understanding some things.
I have been able to create an authentication system, but the problem im having is where to store the user object. After reading some docs and other answers here on SO, these where the strategies i came up with
Authenticate user and retrieve user data, then :-
Create a Model Instance with user data and store it in a global variable :
Problem is how do i add this to a viewmodel so that i can data bind its fields to a view. For example
bind User Name to text of a button.
Create a model link in the viewModel:
The problem with this is, How do i pass in the model Id so that i can retrieve the user from the server.
Store the user data in a global variable, then in view controller , access viewModel memory store proxy, then add the data to the store, then load the store.
Problem is when i do it this way I still cant access the data in my view for data binding.
Please see snipets below :-
Scenario 1
Application.js
Ext.define('Wilma.Application', {
extend: 'Ext.app.Application',
requires: [
"Wilma.DirectAPI",
'Wilma.view.login.Login'
],
name: 'Wilma',
views: [
'Wilma.view.MyViewport'
],
stores: [
// TODO: add stores here
],
enableQuickTips: true,
launch: function() {
ExtRemote.HWLogin.loginStatus('', function(result, event){
if(result.success){
console.log('i was called' + result.data);
//Global variable
Wilma.app.LoggedInUser = Ext.create(Wilma.model.User, result.data.user);
console.log(Wilma.app.LoggedInUser);
Ext.widget('mainviewport');
}
else {
Ext.widget('login');
}
}
});
});
ViewModel
Ext.define('Wilma.view.header.usermenu.UsermenuModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.usermenu',
requires: [
'Wilma.store.Users',
'Wilma.model.User'
],
// ???????????????
// ???????????????
// What do i do here
});
Scenario 2
App.js
Relevant part:
launch: function() {
ExtRemote.HWLogin.loginStatus('', function(result, event){
if(result.success){
console.log('i was called' + result.data);
//Global variable
Wilma.app.LoggedInUser = Ext.create(Wilma.model.User, result.data.user);
//OR
//Wilma.app.LoggedInUser = result.data.user
console.log(Wilma.app.LoggedInUser);
Ext.widget('mainviewport');
}
else {
Ext.widget('login');
}
}
});
ViewModel
links: {
loggedinuser:{
reference: 'user',
id: Wilma.app.LoggedInUser.get('_id')
}
}
Scenario 3
ViewController
Ext.define('Wilma.view.header.usermenu.UsermenuController', {
extend: 'Wilma.controller.BaseViewController',
alias: 'controller.usermenu',
onbeforerender: function(button, eOpts) {
var userstore = this.getViewModel().getStore('usermenu');
userstore.getProxy().data = Wilma.app.LoggedInUser;
userstore.load();
//userstore.add(Wilma.app.LoggedInUser);
}
});
ViewModel
Ext.define('Wilma.view.header.usermenu.UsermenuModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.usermenu',
requires: [
'Wilma.store.Users',
'Wilma.model.User'
],
stores: {
usermenu: {
model: 'Wilma.model.User',
proxy:{
type: 'memory'
},
reader: {
type:'json',
rootProperty: 'data'
//messageProperty:'message'
},
autoLoad:false
}
}
});
Please, I have been stuck on this for far too long. Please need help?
Let me summarize:
What I want to do is authenticate a user, get user details and store it somewhere databind to the user data in a viewModel and a view (in this case button text)
Thank you
In any view controller where the view has a view model, or a component that has a view model assigned, you can use getViewModel(). So:
Ext.define('MainViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.main'
});
Ext.define('MainViewController', {
extend: 'Ext.app.ViewController',
alias: 'controller.main',
launch: function() {
ExtRemote.HWLogin.loginStatus('', function(result, event) {
if(result.success){
this.getViewModel().set('currentUser',
Ext.create('User', result.data.user));
}
});
}
});
Ext.define('MainView', {
extend: 'Ext.Container',
viewModel: 'main',
bind: { title: '{currentUser.username}' }
});
After getting the login result, the currentUser is set on the view model and it becomes available for use in the view.

Cannot add listeners to store in ExtJS Controller

In my application I have a button in a toolbar. If I click on this button to open a window following code is executed:
[...]
onClick: function() {
this.windowControl = this.getController('attributesearch.Window');
this.windowControl.init();
this.windowControl.showWindow();
}
[...]
This window contains some inputfields and a combobox with a store:
Ext.define('EM.store.AttributeQuery', {
requires: ['EM.model.AttributeQuery'],
model: 'EM.model.AttributeQuery',
proxy: {
type: 'ajax',
url: './app/configuration/AttributeQueries.json',
reader: {
type: 'json',
root: 'queries'
}
},
autoLoad: true
});
Within the init method of my window controller I want to add one onLoad-listener I try to add this listener to the store:
init: function() {
this.getAttributeQueryStore().on('load', this.onStoreLoad, this);
this.control({
'attributeSearchWindow': {
afterrender: this.onWindowRendered
}
});
},
The first line in the init method this.getAttributeQueryStore().on('load', this.onStoreLoad, this); produces the following error:
Uncaught TypeError: Object [object Object] has no method 'on' app/controller/attributesearch/Window.js:9.
It seems the store is not fully (or correct) instantiated. What am I missing?
Edit:
The console output for this.getAttributeQueryStore() is this:
constructor {self: function, superclass: Object, config: emptyFn, initConfigList: Array[0], initConfigMap: Object…}
__proto__: TemplateClass
$className: "EM.store.AttributeQuery"
autoLoad: true
config: emptyFn
configMap: TemplateClass
initConfigList: Array[0]
initConfigMap: Object
model: "EM.model.AttributeQuery"
proxy: Object
requires: Array[1]
self: function constructor() {
superclass: Object
__proto__: Object
}
Why don't you just define the store's listener as part of the store's definition?
Ext.define('EM.store.AttributeQuery', {
requires: ['EM.model.AttributeQuery'],
model: 'EM.model.AttributeQuery',
proxy: {
type: 'ajax',
url: './app/configuration/AttributeQueries.json',
reader: {
type: 'json',
root: 'queries'
}
},
autoLoad: true,
listeners: {
load: function(store, records, options) {
EM.getApplication().getController('attributesearch.Window').onStoreLoad(store, records, options);
}
}
});
It is my own fault.
If you have a closer look at my store definition, you will see that i forgot to insert the extent: 'Ext.store.Store'. Thats it. Now i can add and remove listeners like i expected to.
Thank you everyone.

How to implement custom filter algorithm for Ext.data.Store

I want to filter an Ext store using a custom algorithm. The JSON/AJAX proxy returns >100 records and I need to reduce the number to the top-5 based on a number of criteria.
How to go about this (not the algorithm but where to trigger it)?
My current approach is to use a custom reader like so
Ext.define('MyReader', {
extend : 'Ext.data.reader.Json',
alias : 'reader.myReader',
getResponseData : function(response) {
var data = this.callParent([response]);
// algorithm
return filteredData;
}
});
Ext.define('SunApp.store.Stations', {
extend: 'Ext.data.Store',
requires: ['MyReader'],
config: {
model: 'SunApp.model.Station',
autoLoad: true,
proxy: {
type: 'ajax',
url: 'data.json',
reader: {
type: 'myReader'
}
}
}
});
but I'd much rather base the algorithm on the store model than on the raw JSON data. Hence, I'd like to filter while/after the data is loaded into the store. Note that the store is created implicitly by Ext when the list view is displayed:
Ext.define('SunApp.view.Stations', {
extend: 'Ext.List',
xtype: 'stations',
config: {
title: 'Stations',
store: 'Stations',
...
Therefore, I can't just add a function to the store that I invoke manually before it's being passed to the list.
In this case where the filter may need to traverse the store data (i.e. records) several times to reduce the full set to the desired set I believe (I'm still a Sencha rookie...) the following may be fine:
write an on-load event listener
do the magic filtering
call setData on the store passing the filtered data
Hence, instead of filtering in the reader it'd be like this:
Ext.define('SunApp.store.Stations', {
extend: 'Ext.data.Store',
config: {
model: ...,
sorters: [...],
listeners: {
load: function(store, records, success, eOpts) {
var filteredRecords = filter(records);
store.setData(filteredRecords);
}
},
proxy: ...
}
});

Resources