Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Hosted-services / ExtJS

Ext JS MVVM Tricks: Part 2. ViewModel Isolation

5.00/5 (3 votes)
30 Mar 2017CPOL2 min read 13.2K  
Completing Part 1 and making ViewModels isolated

Introduction

In the first part, we've gone one step closer to the component's encapsulation. We solved an outer binding problem and made inner bindings possible. In this part, we are going to solve the data field name conflict which may happen among the ViewModels in a hierarchy.

Name Conflict

Let's imagine we are creating 2 components, an outer and an inner. We don't want to expose the internals of a component so we want to use configs to manipulate its state. In this case, a non-unique color config in each.

For the experiment to be clear, we won't use the fix from Part 1. We change the ViewModel in handlers instead. Outer component's color is bound to a text field, to a label and to the color config of the inner component.

JavaScript
Ext.define('Fiddle.view.OuterContainer', {
    // ...

    viewModel: {
        data: {
            color: null
        }
    },

    config: {
        color: null
    },

    items: [{
        xtype: 'textfield',
        fieldLabel: 'Enter color',
        bind: '{color}',
        listeners: {
            change: 'colorField_change'
        }
    }, {
        xtype: 'displayfield',
        fieldLabel: 'Color',
        bind: '{color}'
    }, {
        xtype: 'innercontainer',
        bind: {
            color: '{color}' // <<-- non-unique
        }
    }],

    colorField_change: function (field, value) {
        this.setColor(value);
    },

    updateColor: function (color) {
        this.getViewModel().set('color', color);
    }
});
JavaScript
Ext.define('Fiddle.view.InnerContainer', {
    // ...

    viewModel: {
        data: {
            color: null
        }
    },

    config: {
        color: null
    },

    items: [{
        xtype: 'textfield',
        fieldLabel: 'Color',
        bind: '{color}'
    }],

    updateColor: function (color) {
        this.getViewModel().set('color', color);
    }
});

Image 1

If we try doing this, we'll find that bindings doesn't work properly. The reason is that both ViewModels contain the color data field. To avoid this problem, we should name them uniquely. But is this convenient? And does this guarantee uniqueness?

The Extension

For this reason, the SplitViewModel extension was created. It gives unique internal names to the data fields of a ViewModel. As a result, ViewModels never interfere. To complete our example, we are going to make an explicit back-reference binding as shown:

JavaScript
Ext.define('Fiddle.view.OuterContainer', {
    // ...
    
    viewModel: {
        name: 'outercontainer',
        data: {
            color: null
        }
    },
    
    // ...

    items: [{
        xtype: 'innercontainer',
        bind: {
            color: '{outercontainer|color}'
        }
    }]

    // ...
});

What has changed here?

We gave a name to the outer ViewModel since it's anonymous (in the same file). Either way, the name would be taken from the alias automatically.

We gave an explicit back-reference to the outer ViewModel using the name and the | char:'{outercontainer|color}'.

Image 2

Results

ViewModels are now isolated from each other. We can create independent views using both ViewControllers and ViewModels with no worrying about ViewModels' interference. With this extension, bindings become very predictable.

Limitations

If you had nested views both with ViewModels like in the example above, then you must add an explicit back-reference to your bindings where necessary. Even if property names are unique. All you have to do is add ViewModel's name or type followed by the | char before the property name.

Using the Code

Find the extension at GitHub:

and import it:

JavaScript
Ext.application({
    requires: [
        'Ext.vmx.app.SplitViewModel'
    ]
});

Combining Extensions

I find the results impressive when both extensions are used (Part 1). Take a look at this example:

There is no single handler. Everything is done with a declaration syntax. Component's state is controlled by the configuration properties but the internals are bound via the ViewModel. I wish Sencha would have done this by default.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)