How to bind correctly a formula with a store in Sencha ExtJs v6? - extjs

Here is a configuration for the formula:
formulas: {
//this binding with the store did not work :(
countDeactivatedVehicles: {
bind: {
bindTo: "{organizationCars}",
deep: true,
},
get: function (store) {
return store.query("isCarActive", false).getCount();
}
}
}
(currently now the count that we want is only displayed once initially meaning that on load it works ok)
When the models inside the store organizationCars have an attribute updated the binding does not work, the store is not alerted that its models have been updated.
What ideally should happen is when the model gets updated the event is propagated to the store so that the store knows that is changed. This way the binding would work (?) and the formula would get calculated.

Deepbinding, does not bind that deep.
Here is the answer to your question: Fiddle
I got it working in there.
But - personally - I would go with Theo's idea, because deep binding, is a lot of overhead.

I don't think this is actually possible using formulas, but you can do using events.
by listening to load datachanged and update events you can be notified of any changes to the store, from here you can do what you would do in a formula and manually set on the ViewModel.
This fiddle shows the solution best: https://fiddle.sencha.com/#view/editor&fiddle/1qvf
Store
Ext.define('Fiddle.Store', {
extend: 'Ext.data.Store',
alias: 'store.test',
listeners: {
load: 'storeUpdate',
update: 'storeUpdate',
datachanged: 'storeUpdate'
},
fields: [{
name: 'include',
type: 'bool'
}]
});
ViewModel
Ext.define('Fiddle.StoreBinderViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.storebinder',
stores: {
teststore: {
type: 'test'
}
},
data: {
includedTotal: 0
}
});
Controller
Ext.define('Fiddle.StoreBinderController', {
extend: 'Ext.app.ViewController',
alias: 'controller.storebinder',
storeUpdate: function (store) {
var recs = store.query('include', true);
this.getViewModel().set('includedTotal', recs.length)
}
});

Related

EXTJS 5 - ViewModel getting Store returns null value

Im currently having an issue regarding stores declared in ViewModel, using Extjs5's MVVM feature.
Simple Use case:
ViewModel:
Ext.define('App.view.view1.View1Model', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.view1',`
data: {
},
stores:{
Company:{
model: 'UM.model.CompanyData',
type: 'CompanyData', //Store name
autoLoad: true
}
}
}];
While code below is my viewController:
ViewController:
Ext.define('App.view.view1.View1Controller', {
extend: 'Ext.app.ViewController',
alias: 'controller.view1',
onloadnewdata: function(event, item,store){
var vm= this.getViewModel();
var store=vm.getStore('Company');
console.log(store);
}
});
On firing some event with "onloadnewdata" function based on the console store is null.
All I need to do is get the store declared on the ViewModel. Please help me which part is wrong or is this a bug?
Appreciate if you share some ideas or other approach. Thanks.
I think you need to modify the code something like below. In order to get the reference to the ViewModel first you need to get the reference to View.
Ext.define('App.view.view1.View1Controller', {
extend: 'Ext.app.ViewController',
alias: 'controller.view1',
onloadnewdata: function(event, item,store){
var store = this.getView().getViewModel().getStore('Company');
console.log(store);
}
});
You can refer this sencha fiddle https://fiddle.sencha.com/#fiddle/fpb. Just check this following controller TestApp.view.grid.GenericGridController . Hope it helps!

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.

Why the refs to itemId cannot trigger the initialize event in Sencha Touch?

This is the controller code:
Ext.define('XXX.controller.XXX', {
extend: 'Ext.app.Controller',
config: {
views: ['CustomView','CarouselView'],
refs: {
custom: "carouselview #customid"
},
control: {
custom: {
initialize : function() {
alert("it's loading")
}
}
}
},
launch: function(){
Ext.Viewport.add(Ext.create('XXX.view.CustomView'));
console.log(this.getCustom()) // ——> This works, it is not undefined
}
});
and this is the carousel view code:
Ext.define('XXX.view.CarouselView', {
extend: 'Ext.Carousel',
xtype: 'carouselview',
defaults: {
styleHtmlContent: true
},
config:{
direction: 'horizontal',
items: [
{
xtype: 'customview',
itemId: 'customid'
}
]
}
});
Now it's the customview :
Ext.define('XXX.view.CustomView', {
extend: 'Ext.Panel',
xtype: 'customview',
config: {
tpl: XXX
}
});
in the controllers's launch function, it can log the right value, but the initialize event can't be triggered.
And if i change refs to { custom: "customview" }, the initialize event can be triggered.
IMHO you (and someone answered below) misunderstand the use of itemId config.
Here is the difference between id and itemId:
id is the global identifier of a component. It can be used directly as a selector in Ext.ComponentQuery class which refs uses behind the scene. So if you want something like "carouselview #customid", you have to use id instead of itemId.
itemId is the global identifier within a class from which the component derives from. For example, assume that you have an Ext.Button with itemId: "myCustomButton", then you can have access to it via this refs: button#myCustomButton (please note that there's no space between them). This way, Ext.ComponentQuery first looks for all components xtyped button, then find the instance with that itemId.
So, if you want to use some string as "first-class" selector, you will have to use id. If you want to use itemId, you may want to always include its xtype before the itemId. Therefore, 2 possible solutions are:
First solution (still use itemId): custom: "carouselview customview#customid"
Second solution: keep your refs, but change #customid from itemId to id
Hope this helps.
UPDATE:
Just figured out that you are trying to initialize on something that get's the itemId on initialize :) Sorry, took me some time.
Basically the fireEvent('initialize') has already been in the past when you are trying to listen to it in the controller.
Use the xtype to initialize or simply:
Ext.define('XXX.view.CustomView', {
extend: 'Ext.Panel',
xtype: 'customview',
config: {
tpl: XXX
},
initialize: function() { // good way to use initialize inside the view, as it belongs to the view and there is not user input handled
}
});
OR
Ext.define('XXX.controller.XXX', {
extend: 'Ext.app.Controller',
config: {
views: ['CustomView','CarouselView'],
refs: {
custom: ".carouselview .customview" // --> HERE use this
},
control: {
custom: {
initialize : function() {
alert("it's loading") // Yeah, now you are getting here
}
}
}
},
launch: function(){ // --> this will be the same as if you are placing it in app.js launch
Ext.Viewport.add(Ext.create('XXX.view.CustomView')); // --> here the initialize happends and this.getCustom() does not yet exists
console.log(this.getCustom()) // ——> here this.getCustom() exists
}
});

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: ...
}
});

Building search application form and displaying result in grid upon submit in extjs-mvc

I am trying to build a search application using ExtJS. I have created dummy form to search for personal details. I have a php script connected to mysql DB. I am able to pass form parameters to php and able to get the return result in msg box. but I am not understanding how to pass it to store and display the same in grid in MVC. I have tried to pass the return data of php to store and then called Grid (List.js) in controller. still did not work. I have shown all the codes that i have used to do this.Another doubt which i have, is that essential to use proxy part of code (i.e url:app/scripts/Info.php) in both store and onSearchButtonClick function in controller? as I can directly pass the return values to store from onSearchButtonClick function, I hope it is not essential to connect php script in both places. However, it would be really nice experts clarify this.
Following is my store:
Ext.define('App.store.Info', {
extend: 'Ext.data.Store',
model: 'App.model.Info',
alias: 'widget.infostore',
pageSize : 50,
autoLoad : false,
remoteFilter: true,
proxy :{
type : 'ajax',
url : 'app/scripts/Info.php',
reader : {
type : 'json',
root : 'result',
successProperty : 'success'
}
},
listeners: {
load : function(store) {
store.each(function(record) {
record.commit();
});
}
}
});
My model looks perfect, simply to reduce somuch code I havent put here
Here is my grid:
Ext.define('App.view.info.List' ,{
extend: 'Ext.grid.Panel',
alias : 'widget.infolist',
store : 'Info',
initComponent: function(){
this.columns = [
{header:'PID',dataIndex:'pid'},
{header:'Name',dataIndex:'name'},
{header:'Address', dataIndex:'address'},
{header:'Contact', dataIndex:'contact'}
];
this.callParent(arguments);
}
});
This is what my php script returns:
{'success':true, 'result':{'pid':'100','name':'Suman','address':'Bangalore','contact':'suman#xyz.com'}}
Here is controller:
Ext.define('App.controller.Info', {
extend: 'App.controller.Base',
models: ['Info'],
stores: ['Info'],
views: [
'info.Index',
'info.List'
],
refs: [{ref: 'info',selector: 'info'}],
init: function(){
console.log('Main controller init');
this.control({
'button[action=search]':{
click: this.onSearchButtonClick
}
});
},
onSearchButtonClick:function(){
var form = Ext.getCmp('ppanel');
if(form.getForm().isValid()){
Ext.Ajax.request({
waitMsg: 'Searching...',
method: 'POST',
url: 'app/scripts/Info.php',
params: {
searchData: Ext.encode(form.getValues())
},
scope:this,
success: this.onSearchSuccess,
failure: this.onSearchFailure
//Ext.MessageBox.alert("XXXXX","dat");
});
}
},
onSearchSuccess: function(response){
var gData = Ext.JSON.decode(response.responseText);
//var grid = Ext.widget('infolist'); //not working -need help
this.getInfoStore().load(gData);
//Ext.getCmp().setActiveItem('infolist'); //not working-need help
//this.getViewport().getLayout().setActiveItem('infolist'); //not working need help
Ext.MessageBox.alert("XXXXX",response.responseText); //works
},
onSearchFailure: function(err){
Ext.MessageBox.alert('Status', 'Error occured during searching...');
}
});
I hope I have provided required information to understand the problem. Looking forward some sort of help.
The problem is that you have two instances of the store, one in grid and one in controller.
If you want a single instance store (like it seems you want) you have two options:
Add it to your application
Assign a storeId to your store definition.
(if you already added that store to your application, ignore the above text)
Or, better yet, do not work directly with the store but with your grid, like this:
First add a ref to your view->grid in your controller:
refs: [{ref: 'info',selector: 'info'},{selector:'infolist', ref:'infoGrid'}]
And then, in your onSearchSuccess handler, instead of calling: this.getInfoStore().load(gData); you should call: this.getInfoGrid().getStore().loadData(gData);
BTW: this.getInfoStore().load(gData); will never load an array of data or a record, for that you should use: this.getInfoStore().loadData(gData);
Hope this gets you in the right track.

Resources