I've got a simple viewmodel with a formula (pretty much cut and paste from sencha docs), but it formulas are not working.
A full example is here :
https://fiddle.sencha.com/fiddle/3lpa
Summarised :
Ext.define('vm', {
extend: 'Ext.app.ViewModel',
formulas: {
bar: function (get) {
return true;
}
}});
...
vm.get('bar')
When executed - this returns "null", not "true"
Also - the vm.getFormulas() is empty.
Any suggestions on what I'm doing wrong ?
Formula calculation is managed by ExtJS in a complicated way to optimize performance. In your example ExtJS does not see a reason to calculate your formula. Try this code, as you see the bind statement references the ViewModel data, so it will be calculated correctly:
Ext.define('vm', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.vm',
formulas: {
bar: function (get) {
return true;
}
}})
Ext.application({
name : 'Fiddle',
launch : function() {
var component =Ext.create({
xtype: 'component',
bind: {
html: '{bar}'
},
renderTo: Ext.getBody(),
viewModel: {
type: 'vm'
}
});
}
});
Related
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)
}
});
For example I have 5 dataViews with elements (simple rows). Does ability exist to realize multiselection between this dataView (all of this dataViews have different data-stores)? I mean non-algorithmic realization... Of course, I can write some logic to implement this, but maybe some standard solution exist?
You can try something like this, maybe this can work :
Ext.define('MyApp.controller.DataViews', {
extend : 'Ext.app.Controller',
init : function() {
this.views = {};
this.listen({
component: {
'dataview-1' : {
'render' : this.onDataViewRender,
'itemclick' : this.onItemClick
},
'dataview-2' : {
'render' : this.onDataViewRender,
'itemclick' : this.onItemClick
}
}
});
},
onDataViewRender: function(view){
this.views[view.getXType()] = view;
},
onItemClick: function(view, record, items, index, e){
//If Ctrl is not pressed when clicking an item, clear the selection of all views before proceeding
if(!e.ctrlKey)
{
Ext.Object.each(this.views, function(xtype, viewObj){
viewObj.getSelectionModel().deselectAll();
});
}
}
});
Ext.define('MyApp.view.DataView1', {
extend: 'Ext.view.View',
xtype: 'dataview-1',
multiSelect: true,
});
Ext.define('MyApp.view.DataView2', {
extend: 'Ext.view.View',
xtype: 'dataview-2',
multiSelect: true,
});
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.
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
}
});
I have a controller, and I want to pass a simple string value to the next View.
For that, I am creating the View like this.
var nextView = Ext.create('MyApp.view.NextView', {
content: 'value'
});
Ext.Viewport.add(nextView);
Ext.Viewport.animateActiveItem(nextView, {
type: 'slide',
direction: 'left'
});
On the NextView, I have a label and I want to set the HTML property of the label to the value that I am passing from the controller. ie. value.
My NextView looks like this.
Ext.define('MyApp.view.NextView', {
extend: 'Ext.form.Panel',
config: {
content: 'null',
items: [{
xtype: 'label',
html: 'value'
}]
}
});
I am not sure how to proceed from here. I can't have the NextView as a form. I just need to pass one string value in this situation.
What's the best way to achieve this?
Use initialize method to access config data like this:
Ext.define('MyApp.view.NextView', {
extend: 'Ext.form.Panel',
config: {
content: 'null',
items: [
{
xtype: 'label',
html: 'value'
}
]
},
initialize : function(){
this.callParent();
var val = this.config.content;
this.down('label').setHtml(val);
}
});
PS Feel free to use your favourite selector in down function
I know the question has been answered. But I just digged up a pretty natural way to pass data from controller to a view (using view's constructor). I use this in my integration of web desktop to my app.
In controller, pass data to the constructor of the view as followed:
loiTab = Ext.create('Iip.view.giips.admission.DetailedLoiTab', {closable: true, selectedLoiData: selected[0].data});
In the view, spin up a constructor as followed:
constructor: function(selectedLoiData) {
Ext.applyIf(this, selectedLoiData);
this.callParent();
},
The following method lives in the same file as the constructor. You can access selectedLoiData from any where in the view the constructor lives as followed:
initComponent: function(){
console.log(this.selectedLoiData);
}