在Knockout中绑定可能是多种数据类型的属性

时间:2021-07-31 17:03:40

So I have a custom knockout binding for handling time durations. I have an issue where a value in one of my forms might be a duration, but could also be a string or other value. The issue arises from the fact that duration values are represented as objects with two properties, duration and time_unit (which is itself an object with 2 properties. I have the various bound nodes tied up inside if bindings.

所以我有一个自定义的敲除绑定来处理持续时间。我有一个问题,其中一个表单中的值可能是一个持续时间,但也可能是一个字符串或其他值。问题产生于持续时间值表示为具有两个属性的对象,持续时间和time_unit(它本身是一个具有2个属性的对象。如果绑定,我将各种绑定节点绑定在内部。

init: function(element, valueAccessor, allBindingsAccessor, viewModel, context) {

    var allBindings = ko.toJS(allBindingsAccessor() || {}),
        source = allBindings.source || [],
        observable = valueAccessor(),
        value = ko.toJS(ko.utils.unwrapObservable(observable)),

        duration = new DurationControl({
            inputNode: inputNode,
            source: source,
            defaultValue: value
        });

    //attach duration control to element and render here

    ko.utils.registerEventHandler(inputNode.getDOMNode(), 'blur', function () {
        var observable = valueAccessor();

        if (!observable.viewModelUpdating) {
            observable.viewModelUpdating = ko.observable(false);
        }

        if (duration.isValueValid(true)) {
            observable.viewModelUpdating(true);
            observable.duration(duration.getDuration());
            observable.time_unit.value(duration.getTimeUnit());
            observable.time_unit.id(sourceIdValueMap[duration.getTimeUnit()] || 0);
            observable.viewModelUpdating(false);
        }
    });
}

And my bound html

我绑定的HTML

<!-- ko if: type() == 'string' -->
<div class="control wide">
    <input type="text" data-bind="value: value" />
</div>
<!-- /ko -->

<!-- ko if: type() == 'duration' -->
<div class="control">
   <input type="text" data-bind="duration: value, source: metadata.time_units" />
</div>
<!-- /ko -->

If i do the initial binding with value being the correct object format like so

如果我做的初始绑定值是正确的对象格式,如此

...,
value: {
    duration: '',
    time_unit: {
        value: '',
        id: '',
    }
},
...

everything works great. But if i start with value in some other format, like ..., value: 'nada', ... it breaks trying to access observable.duration (and observable.time_unit.*).

一切都很棒。但是,如果我从其他格式的值开始,比如......,value:'nada',...它会破坏尝试访问observable.duration(和observable.time_unit。*)。

When I evaluate value with the proper set up, i Get the object described above back out. If i try manually adding the duration/time_unit properties as observables, i still just get the empty string back out.

当我通过适当的设置评估值时,我将上述对象取回。如果我尝试手动将duration / time_unit属性添加为observables,我仍然只是将空字符串退出。

How do I best go about updating the viewmodel/bindings/etc from inside my init function so that it behaves as if the model was initially in that state when I initialized it?

我如何最好地从我的init函数内部更新viewmodel / bindings / etc,以便它在我初始化时表现得好像模型最初处于该状态?

1 个解决方案

#1


2  

The custom binding is not the solution to your problem. Knockout will bind everything it encounters in the DOM therefore whichever type of object you initialise your value with, the other one will fail. I've implemented something similar where an observable in my view-model stores multiple 'types' of object, which you will need 'type-specific' portions of UI bound to each. This is how i tackled the problem:

自定义绑定不是您的问题的解决方案。 Knockout将绑定它在DOM中遇到的所有内容,因此无论您使用哪个类型的对象初始化,另一个都将失败。我已经实现了类似的东西,在我的视图模型中的一个observable存储了多个“类型”的对象,你需要将'特定于类型'的UI部分绑定到每个对象。这就是我解决这个问题的方法:

Remove all instances of, if: type() == '<type>' and implement each piece of HTML as a template.

删除if:type()==' '的所有实例,并将每段HTML实现为模板。

Now refactor that decision making process. Use a computed observable to decide which template is shown based on the type(). Something like this...

现在重构决策过程。使用计算的observable来决定基于type()显示哪个模板。这样的东西......

function ViewModel(){
    var self = this;

    self.type = ko.observable();
    self.value = ko.observable();

    self.currentValueTemplate = ko.computed(function(){
       switch(self.type()) {
           case 'string':
               return 'stringTemplate';

           case 'duration':
               return 'durationTemplate';

           default:
               throw 'invalid type';
       }
    });

Now simply add a template placeholder...

现在只需添加模板占位符...

<!-- ko template: { name: currentValueTemplate, data: value } -->
<!-- /ko -->

You may want to adapt this to fit the specifics of your application, but it is a tidier approach and will scale much better should you want to store an increasing variety of object types.

您可能希望对此进行调整以适合您的应用程序的细节,但它是一种更整洁的方法,如果您想要存储越来越多的对象类型,它将更好地扩展。

#1


2  

The custom binding is not the solution to your problem. Knockout will bind everything it encounters in the DOM therefore whichever type of object you initialise your value with, the other one will fail. I've implemented something similar where an observable in my view-model stores multiple 'types' of object, which you will need 'type-specific' portions of UI bound to each. This is how i tackled the problem:

自定义绑定不是您的问题的解决方案。 Knockout将绑定它在DOM中遇到的所有内容,因此无论您使用哪个类型的对象初始化,另一个都将失败。我已经实现了类似的东西,在我的视图模型中的一个observable存储了多个“类型”的对象,你需要将'特定于类型'的UI部分绑定到每个对象。这就是我解决这个问题的方法:

Remove all instances of, if: type() == '<type>' and implement each piece of HTML as a template.

删除if:type()==' '的所有实例,并将每段HTML实现为模板。

Now refactor that decision making process. Use a computed observable to decide which template is shown based on the type(). Something like this...

现在重构决策过程。使用计算的observable来决定基于type()显示哪个模板。这样的东西......

function ViewModel(){
    var self = this;

    self.type = ko.observable();
    self.value = ko.observable();

    self.currentValueTemplate = ko.computed(function(){
       switch(self.type()) {
           case 'string':
               return 'stringTemplate';

           case 'duration':
               return 'durationTemplate';

           default:
               throw 'invalid type';
       }
    });

Now simply add a template placeholder...

现在只需添加模板占位符...

<!-- ko template: { name: currentValueTemplate, data: value } -->
<!-- /ko -->

You may want to adapt this to fit the specifics of your application, but it is a tidier approach and will scale much better should you want to store an increasing variety of object types.

您可能希望对此进行调整以适合您的应用程序的细节,但它是一种更整洁的方法,如果您想要存储越来越多的对象类型,它将更好地扩展。