【问题标题】:ExtJS 5 MVVM: Updating Parent's ViewModel from Child'sExtJS 5 MVVM:从孩子的更新父视图模型
【发布时间】:2016-11-25 04:54:09
【问题描述】:

我的 ExtJS 5 组件的 ViewModel 正在从祖先的 ViewModel 继承数据。

如何从孩子的角度更新父母的 ViewModel 数据?如果我尝试在childPanel ViewModel 上创建myProp 的新实例childPanel.getViewModel().set('myProp', 'foo');,而不是更新parentPanel 的VM。使用myProp 值的其他组件将不同于childPanel

通过在子节点上创建本地myProp,当父节点的myProp 发生变化时,子节点不会随之改变,因为已断开关系。

我只想要 myProp 属性的 1 个实例,并且该值位于父级的 ViewModel 上。

要解决这个问题,我的子虚拟机似乎必须知道该属性是继承的还是存储在本地的,这要求子虚拟机知道应用程序的正确架构。它必须了解比我熟悉的更多的应用程序架构,从而显着地将子级与父级耦合。


示例展示了子创建panelTitle 的新实例而不是更新父实例:

https://fiddle.sencha.com/#fiddle/1e09

编辑:将按钮的点击事件移动到 ViewController

Ext.application({
    name: 'Fiddle',

    launch: function() {

        Ext.define('ChildPanel', {
            extend: 'Ext.panel.Panel',
            alias: 'widget.childpanel',
            controller: 'childpanelcontroller',
            bind: {
                title: '{panelTitle}'
            },

            // first panel has its own viewmodel, which is a child of the viewport's VM
            viewModel: {
                data: {
                    'btnText': 'Click to Change Title'
                }
            },

            items: [{
                xtype: 'button',
                scale: 'large',
                bind: {
                    text: '{btnText}'
                },
                listeners: {
                    'click': 'onButtonClick'
                }
            }]
        });

        Ext.define('ChildPanelController', {
            extend: 'Ext.app.ViewController',
            alias: 'controller.childpanelcontroller',
            onButtonClick: function(btn) {
                // updates first panel's VM
                this.getViewModel().set('panelTitle', '<span style="color:red;">Now They Have Different Titles</span>');

                debugger;

                window.setTimeout(function() {

                    // by setting the first panel's VM instead of the viewport's, 
                    // the first panel will not use viewport's VM for `panelTitle` property again
                    btn.up('viewport').getViewModel().set('panelTitle', '<span style="color:green;">And Are No Longer Using the Same VM Data</span>');

                }, 500);
            }
        });



        Ext.create('Ext.container.Viewport', {
            viewModel: {
                data: {
                    'panelTitle': 'Both Have the Same Title'
                }
            },
            layout: 'hbox',
            items: [{
                xtype: 'childpanel',
                flex: 1
            }, {
                // second panel uses the viewport's viewmodel
                xtype: 'panel',
                bind: {
                    title: '{panelTitle}'
                },
                flex: 1,
                margin: '0 0 0 25px'
            }]
        });
    }
});

【问题讨论】:

  • 您希望孩子更改父母的属性,但您不希望孩子知道其与父母的关系?
  • 孩子知道该属性存在,但不知道也不关心它的来源。该财产可以从其父母或其曾曾曾曾祖父母继承。如果它来自多个级别,孩子需要知道它需要跳转到正确的 VM (child.getViewModel().getParent().getParent().getParent()...) 才能更新它。

标签: extjs mvvm viewmodel extjs5


【解决方案1】:

After talking to Sencha support,我为Ext.app.ViewModel set 函数创建了一个覆盖。

Ext.define('Overrides.app.ViewModel', {
    override: 'Ext.app.ViewModel',

    /**
     * Override adds option to only update the ViewModel that "owns" a {@link #property-data} property.
     * It will traverse the ViewModel tree to find the ancestor that the initial ViewModel inherited `path` from.
     * If no owner is found, it updates the initial ViewModel.
     *
     * Only uses the override if `onlyUpdateOwner` parameter is `true`.
     *
     * @param {Boolean} [onlyUpdateOwner=false] If `true`, uses override to update VM where `path` is stored
     * @inheritdoc Ext.app.ViewModel#method-set
     * @override_ext_version ExtJS 5.1.2.748
     */
    set: function (path, value, onlyUpdateOwner) {
        var vm = this,
            foundOwner = false,
            ownerVM = vm;

        onlyUpdateOwner = Ext.valueFrom(onlyUpdateOwner, false);

        if (onlyUpdateOwner) {

            // find ViewModel that initial ViewModel inherited `path` from (if it did)
            do {
                // `true` if `path` ***ever*** had a value (anything but `undefined`)
                if (ownerVM.hadValue && ownerVM.hadValue[path]) {
                    break;
                }
            } while (ownerVM = ownerVM.getParent());
        }


        // reverts to initial ViewModel if did not inherit it
        ownerVM = ownerVM || vm;

        ownerVM.callParent([path, value]);
    }
});

它现在为开发人员提供了仅更新作为属性源的 ViewModel 的选项。

如果它找不到“拥有”该属性的 ViewModel,它会回退到最初调用的 ViewModel。

它通过使用适当的 ViewModel 上下文调用原始 ExtJS set 函数来完成。

【讨论】:

    【解决方案2】:

    为了从任何子组件中获取 ViewModel,您可以使用以下代码覆盖 Component 类:

    Ext.define(null, {
        override: 'Ext.Component',
        getVM() {
    
            let vm = this.up();
    
            do {
    
                if(vm.getViewModel())
                    break;
    
             } while (vm = vm.up());
    
            return vm.getViewModel()
    
        }
        
    });
    

    然后,您可以从任何子组件调用 this.getVM(),无论它们有多深。可以做类似的事情来获取 ViewController。

    我想知道这个功能是否已经是 ExtJS 的一部分。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-19
      • 1970-01-01
      • 2013-08-26
      • 1970-01-01
      • 2017-05-04
      • 1970-01-01
      相关资源
      最近更新 更多