ExtJS 5.1.1 Immediately fire binding in ViewModel

Seems like i have some misunderstanding with binding timings. I have a simple combobox with value binded to some object in viewmodel. Selecting new value, firing change event, which fired after setValue method, so my new value is already set, but my viewmodel is not updated yet. When my viewmodel will be updated? I found some information about scheduler, which says i need to run notify() method to immediately apply changes to viewmodel, but it doesn't help me at all.

Ext.define('MyModel', {
    extend: 'Ext.data.Model',
    idProperty: 'foo',
    fields: [{
        name: 'bar',
        type: 'string'
    }]
});

Ext.define('MyViewModel',{
    extend: 'Ext.app.ViewModel',
    alias: 'viewmodel.my',
    data: {
        testObj: {
            foo: null,
            bar: null
        }
    },
    stores:{
        combostore: {
            model: 'MyModel',
            data: [{
                foo: '1',
                bar: 'qwerty'
            },{
                foo: '2',
                bar: 'ytrewq'
            }]
        }
    }
});

Ext.define('MyViewController', {
    extend: 'Ext.app.ViewController',
    alias: 'controller.my',
    onChange: function() {
        var vm = this.getViewModel();
        vm.notify();
        console.log(vm.get('testObj.foo'));//supposed to be current value
    }
});

Ext.application({
    name : 'Fiddle',

    launch : function() {
        Ext.create('Ext.container.Viewport', {
            controller: 'my',
            viewModel: {
                type: 'my'
            },
            layout : 'vbox',
            items : [
                { 
                    xtype : 'combo',
                    valueField: 'foo',
                    displayField: 'bar',
                    queryMode: 'local',
                    bind: {
                        store: '{combostore}',
                        value: '{testObj.foo}'
                    },
                    listeners:{
                        change: 'onChange'
                    }

                }
            ]
        });
    }
});

Here's fiddle aswell: https://fiddle.sencha.com/#fiddle/r88

I know it is a late response, however I think this problem is still relevant. See this fiddle: https://fiddle.sencha.com/#fiddle/2l6m&view/editor.

Essentially the idea is that you listen to the bound variables changes instead of listening to the combobox selection change (which triggers the re-evaluation of the bound variables).

The key code is in the constructor I added for the view model:

Ext.define('MyViewModel',{
    extend: 'Ext.app.ViewModel',
    alias: 'viewmodel.my',

    // added this to enable the binding
    constructor: function () {
        var me = this;
        me.callParent(arguments);

        me.bind('{selectedItem}', function (value) {
            console.log('combobox selected item changed (bar value): ' + (value === null ? "null": value.get('bar')));
            console.log(me.getView().getController());
        });

        me.bind('{testObj.foo}', function (value) {
            console.log('combobox value (foo value): ' + value);

            // you can access the controller
            console.log(me.getView().getController());
        });
    },
    data: {
        testObj: {
            foo: null,
            bar: null
        },
        selectedItem: null,
    },
    stores:{
        combostore: {
            model: 'MyModel',
            data: [{
                foo: '1',
                bar: 'qwerty'
            },{
                foo: '2',
                bar: 'ytrewq'
            }]
        }
    }
});

and here is the view (note the binding to the selection):

Ext.application({
    name : 'Fiddle',

    launch : function() {
        Ext.create('Ext.container.Viewport', {
            controller: 'my',
            viewModel: {
                type: 'my'
            },
            layout : 'vbox',
            items : [
                {
                    xtype : 'combo',
                    valueField: 'foo',
                    displayField: 'bar',
                    queryMode: 'local',

                    bind: {
                        store: '{combostore}',
                        value: '{testObj.foo}',
                        selection: '{selectedItem}'

                    },
                    listeners:{
                        change: 'onChange'
                    }

                }
            ]
        });
    }
});

Binding the selection was not necessary but I included it anyway as another option because sometimes you may want to store additional data in the store that is bound to the list.

I hope this helps others.

View Models & Binding, Because ViewModels are associated with a view, they are also able to link to a parent ViewModel owned by ancestor components in the component hierarchy. View Models & Data Binding. Data binding and the ViewModel that powers it are powerful additions to Ext JS. Together they enable you to do more with less code and write in a much more declarative style while helping you maintain a clean separation of concerns. A ViewModel is a class that manages a data object.

You should not call notify directly on the viewmodel. It's private: http://docs.sencha.com/extjs/5.0/5.0.1-apidocs/#!/api/Ext.app.ViewModel-method-notify

Just set the data by calling vm.setData({}).

onChange: function(cb, newValue, oldValue) {
    var vm = this.getViewModel();

    console.log('newValue', newValue);
    console.log('oldValue', oldValue);
    this.getViewModel().setData({'testObj': {'foo': newValue}});
    console.log(vm.get('testObj.foo'));//supposed to be current value
}

Never the less you could consider the following example, where I use a formula to get the selected model of the combobox. See that I removed the listener, added a reference to the combobox and created a formula to bind deep onto the selected record.

Ext.define('MyModel', {
    extend: 'Ext.data.Model',
    idProperty: 'foo',
    fields: [{
        name: 'bar',
        type: 'string'
    }]
});

Ext.define('MyViewModel',{
    extend: 'Ext.app.ViewModel',
    alias: 'viewmodel.my',
    data: {
        testObj: {
            foo: null,
            bar: null
        }
    },
    stores:{
        combostore: {
            model: 'MyModel',
            data: [{
                foo: '1',
                bar: 'qwerty'
            },{
                foo: '2',
                bar: 'ytrewq'
            }]
        }
    },

    formulas: {
        currentRecord: {
            bind: {
                bindTo: '{myCombo.selection}',
                deep: true
            },
            get: function(record) {
                return record;
            },
            set: function(record) {
                this.set('currentRecord', record);
            }
        }
    }
});

Ext.define('MyViewController', {
    extend: 'Ext.app.ViewController',
    alias: 'controller.my',
    onChange: function(cb, newValue, oldValue) {
        var vm = this.getViewModel();

        console.log('newValue', newValue);
        console.log('oldValue', oldValue);
        this.getViewModel().setData({'testObj': {'foo': newValue}});
        console.log(vm.get('testObj.foo'));//supposed to be current value
    }
});




Ext.application({
    name : 'Fiddle',

    launch : function() {
        var vp = Ext.create('Ext.container.Viewport', {
            controller: 'my',
            viewModel: {
                type: 'my'
            },
            layout : 'vbox',
            items : [
                { 
                    xtype : 'combo',
                    reference: 'myCombo',
                    valueField: 'foo',
                    displayField: 'bar',
                    queryMode: 'local',
                    bind: {
                        store: '{combostore}',
                        value: '{testObj.foo}'
                    }/*,
                    listeners:{
                        change: 'onChange'
                    }*/
                },
                {
                    xtype: 'label',
                    bind: {
                        text: '{currentRecord.foo}'
                    }
                }
            ]
        });

        /* Just an example. You could also select records on the combo itself */
        vp.getViewModel().setData({'testObj': {'foo': '2'}});
    }
});

javascript, seems have misunderstanding binding timings. have simple combobox value binded object in viewmodel. selecting new value, firing change  new Ext.app.ViewModel({ stores: { users: { model: 'User', autoLoad: true } } }); This store definition contains a dynamic binding. The store will not be created until the initial value for groupId is set. Once that occurs, the store is created with the appropriate filter configuration.

Defer would be your savior:

onChange: function() {
    var vm = this.getViewModel();

    Ext.defer(function(){
       console.log(vm.get('testObj.foo'));//supposed to be current value
    },10,this);
}

Ext JS Release Notes, ExtJS 5.1.1 Immediately fire binding in ViewModel. Seems like i have some misunderstanding with binding timings. I have a simple combobox with value binded  Bindable - The config has a setter which allows this config to be set via ViewModel binding; instantiated immediately once defined and may Ext JS 5.1.1 . Show

Please use select event instead of change.

I think change event fires even before value is set.

Release Notes for Ext JS 5.1.1, Release Notes for Ext JS 5.1.1 properly; EXTJS-16738 Chart itemevents plugin fires mouse events in reverse order of EXTJS-16317 Toolbar buttons in panel disabled from viewmodel binding remain masked when panel is enabled trigger on datefield editor closes immediately; EXTJS-14892 When expanding a grid  Bindable - The config has a setter which allows this config to be set via ViewModel binding; instantiated immediately once defined and may not Ext JS 5.1.1 .

Release Notes for Ext JS 6.0.0, EXTJS-16416 Click event firing only after second click after scrollbar model in a view model does not trigger field bindings to fire; EXTJS-15595 ViewModel on datefield editor closes immediately; EXTJS-14892 When expanding a grid row​  This is because these components are bound to their ViewModel and its data object which means the two-way nature of the bind will effectively call set on ViewModel 2 which, acting as a normal JavaScript object, calls sets foo on Data 2. This "forking" can be a useful way to initialize values that are then separate by view.

Ext.grid.column.Widget, EXTJS-20117 TreePanel with a ViewModel store will throw error if a locked column is EXTJS-19001 Bindings fire repeatedly for same date value; EXTJS-​19451 EXTJS-15863 SimpleTask example>Menu button immediately lose focus after 5.1.1; EXTJS-18134 Input list element is a different height from tag elements. .toc-page{ display:none !important;}Differences Between Ext JS 5.1.1 and Ext JS 6 Classic Toolkit AddedExt.DeferredExt.PromiseExt.chart.interactions.ItemE

php - php, Each cell widget has a ViewModel injected which inherits from any The widget configuration may contain a bind config which uses the ViewModel's data. It is important to keep in mind that components using liquidLayout do not fire the Determines whether the passed Component is either an immediate child of this  Right-to-Left Support in Ext JS. Ext JS has an extensive locale library with support for over 40 languages from around the world. Some of these languages are read from right to left. This makes right-to-left (RTL) support a must. Combining Ext JS's localization package with RTL support gives your application a voice for the entire globe.

Comments
  • I believe vm.notify() won't do its job synchronously. Note that adding delay: 1 option to the listeners config does the trick, even vm.notify() becomes redundant.
  • If you have to use delay: 1 it should ring all bells you are doing something wrong. The same for calling notify() in this way..
  • 1. By default a store has its autoSync config to false. You may have to set it to true for the comboStore in your viewModel? | 2. You should not test the store in the onChange event of your combo because the combo hasn't the time to synchronize your store... | Remark: in extjs, you don't change the combo value, but the store content, so if you want to check any change in your combo, test it in its store
  • Thanks for reply, but i don't want to use setData() manually or creating formulas to fix this simple bind. Just want to know, when my viewmodel is ready after setValue() called and why adding delay:1 or buffer:1 to listener config will do the trick with accessing value in combobox change event, like @DrakeES mentioned. I suppose i should look at Ext.util.Scheduler in my viewmodel, cause i found some interesting config like tickDelay: 5 which is set to 5 milliseconds by default. And then this config used in creating a defer function for timer that will execute the next notify.
  • TickDelay is indeed used for that. Unfortunately I didn't found setting TickDelay to a higher value very useful, because it's set to the whole viewmodel. Also it's private I believe, so you can't rely on its existence..