首页 > 解决方案 > 当需要父视图模型时,Extjs 使用子视图模型数据

问题描述

考虑这样的片段(可以在https://fiddle.sencha.com/上运行,并且在右上角的组合框中选择经典而不是现代):

Ext.define('ReusableComponent', {
    xtype: 'reusable',
    extend: 'Ext.Container',
    
    config: {
        foo: 'Foo'
    },
    
    updateFoo: function(foo) {
        this.getViewModel().set('foo', foo);  
    },
    
    viewModel: {
        data: {
            foo: 'Foo'
        }
    },
    
    items: [{
        bind: {
            html: 'foo = {foo}'
        }
    }]
});

Ext.define('ConcreteComponent', {
    extend: 'Ext.panel.Panel',
    
    viewModel: {
        data: {
            foo: 'Bar'
        }
    },
    
    layout: 'fit',
    items: [{
        xtype: 'reusable',
        bind: {
            foo: '{foo}'
        }
    }]
});

Ext.application({
    name : 'Fiddle',

    launch : function() {
        Ext.create('ConcreteComponent', {
            renderTo: Ext.getBody(),
            title: 'ConcreteComponent',
            width: 200,
            height: 200
        });
    }
});

梦想是拥有一个可重用的组件,它具有定义的外部接口,并且知道它应该足够了。那些使用该组件的人不必知道它的内部结构。在此示例中,外部接口是从 Ext.Container 继承的通用配置(如宽度/高度/等),以及fooconfig.

所以说我然后尝试在 ConcreteComponent 中使用它。我知道可重用组件有 config foo,因此我应该能够将它绑定到我自己的视图模型,这就是我所做的。但是,这不起作用,它显示foo = Foo, 而不是 (expected) foo = Bar。原因似乎很清楚——我在不知不觉中使用了孩子的视图模型中已经存在的名称,而 extjs 选择了它而不是我在 ConcreteComponent 中定义的名称。也很清楚如何创可贴修复此问题(例如,在 ConcreteComponent 中将 viewmodel 数据属性从 重命名foofoo2)。但这迫使人们了解该可重用组件的内部结构,而不仅仅是它的公共接口。有没有办法解决这个问题?还是无论如何都应该将子视图模型视为其公共界面的一部分?

标签: extjsextjs6-classic

解决方案


看起来解决方案是简单地在私有属性下手动创建一个视图模型(它分解了视图模型的子父链 extjs 在组件的视图模型及其容器的视图模型https://docs.sencha.com/extjs/6.2.0/ classic/Ext.Component.html#cfg-viewModel),并通过 viewModel 配置将其显式传递给可重用组件的子组件。使用defaults似乎工作正常。我在偶然发现颜色选择器的源代码https://docs.sencha.com/extjs/6.2.0/classic/src/Selector.js.html时看到了解决方案。这是问题的固定代码

Ext.define('ReusableComponent', {
    xtype: 'reusable',
    extend: 'Ext.Container',
    
    config: {
        foo: 'Foo'
    },
    
    updateFoo: function(foo) {
        this.childViewModel.set('foo', foo);  
    },
    
    constructor: function() {
        this.childViewModel = Ext.create('Ext.app.ViewModel', {
            data: {
                foo: 'Foo'
            }
        });
        this.defaults = {
            viewModel: this.childViewModel
        };
        this.callParent(arguments);
    },
    
    items: [{
        bind: {
            html: 'foo = {foo}'
        }
    }]
});

Ext.define('ConcreteComponent', {
    extend: 'Ext.panel.Panel',
    
    viewModel: {
        data: {
            foo: 'Bar'
        }
    },
    
    layout: 'fit',
    items: [{
        xtype: 'reusable',
        bind: {
            foo: '{foo}'
        }
    }]
});

Ext.application({
    name : 'Fiddle',

    launch : function() {
        Ext.create('ConcreteComponent', {
            renderTo: Ext.getBody(),
            title: 'ConcreteComponent',
            width: 200,
            height: 200
        });
    }
});

推荐阅读