Knockout - 通过在对象上设置 属性 来更改对象的 'subclass'

Knockout - change an object's 'subclass' by setting a property on it

在 Knockout 中,我有一个 Setting 实体数组,这些实体可能具有不同的 类型 (即设置实体的 'subclasses')。这些类型是动态的,它们可以在 UI 中由用户更改。根据设置实体的类型,它需要不同的对象属性,并使其他属性变得多余。

我目前正在做的(我想改变的)是定义一个设置构造函数,它具有所有设置类型所需的所有属性;使构造函数非常庞大,并包含许多不必要的属性。保存应用程序数据时,我 'ignore' 不应在根据其类型设置实例的属性。

我已经在 this fiddle 中尝试过,使用 type 属性 上的订阅在类型更改时添加/删除属性。我想知道在 KnockoutJS 中是否有可能 在 运行-time 期间可靠地更改对象的属性(即订阅是否总是在模板更新之前触发?),这会对性能产生任何[积极]影响吗?。或者这种方法是完全错误的,我应该用子类实例替换整个对象(如果是这样,订阅不会丢失)吗?

编辑: 依赖属性是实际数据(因此它们应该保存到它们的模型中,而不仅仅是在视图中可用)。我已经尝试过组件方法,但以后如何检索它们的属性? http://jsfiddle.net/kevinvanlierde/zmx3u4an/1/

使用在您的设置编辑器中绑定但由每个单独的子类实例拥有的组件。

<div data-bind="foreach: settings">
    <select data-bind="options: $root.types, value: type"></select>
    <input type="text" data-bind="textInput: value"/>
    <div data-bind="component: { name: componentName, params: componentParams }"></div>
</div>

颜色设置的示例组件 name/params。对于不同的设置子类,这些属性会有所不同。

this.componentName = 'color-viewer';
this.componentParams = {
    colorText: this.value,
};

使用这种方法,更改当前设置 "type" 需要您将一个设置子类实例替换为另一个。这在微不足道的情况下可能会很麻烦,但是通过单一责任保持模型的小型化是有好处的。

为了解决保存属性的问题,我重新编写了组件代码。有些成员(主要是计算的)只用于视图,不应该保存,所以我把 save-able 的东西放在 "data" 成员中:

function Setting(params) {
    var self = this;
    // What getData fetches
    self.data = {
        type: ko.observable(params.type),
        value: ko.observable(params.value || '')
    };
    self.data.type.subscribe(function (value) {
        // Give the conversion a little time
        setTimeout(function () {
            console.log("GetData:", self.getData());
        }, 30);
    });
}

每个专用类型都将把它的特殊成员添加到基础 object 中,并将添加一个方法 toSetting 以将其恢复为基础 object 类型。基本类型在其原型中有一个 do-nothing 版本的 toSetting,因此在类型转换器中首先调用 toSetting 始终是安全的。

我向 Title 类型添加了一个 headingSize 参数,并使其确定文本显示的标题大小。

function toTitle(setting) {
    var self = setting;
    var data = setting.data;
    var value = data.value();
    self.toSetting();
    data.headingSize = ko.observable(4);
    self.capitalized = ko.computed(function () {
        return value ? value.slice(0, 1).toUpperCase() + value.slice(1) : '';
    });
    self.headerText = ko.computed(function () {
        return '<h' + data.headingSize() + '>' + self.capitalized() + '</h' + data.headingSize() + '>';
    });
    self.toSetting = function () {
        delete self.data.headingSize;
        delete self.capitalized;
        delete self.toSetting;
    };
    return self;
}

我是这样注册的:

ko.components.register('title', {
    viewModel: toTitle,
    template: '<select data-bind="options:$root.sizes, value: data.headingSize"></select><div data-bind="html:headerText"></div>'
});

我在设置中添加了一个按钮"save",它会在页面底部显示将要保存的内容。

http://jsfiddle.net/1kfvesq3/3/