Обвързване на свойство, което може да бъде от множество типове данни в Knockout

Така че имам персонализирано нокаут обвързване за обработка на времетраене. Имам проблем, при който стойност в един от моите формуляри може да е продължителност, но може да бъде и низ или друга стойност. Проблемът възниква от факта, че стойностите на продължителността са представени като обекти с две свойства, duration и time_unit (което само по себе си е обект с 2 свойства. Имам различните обвързани възли, свързани вътре в if обвързвания.

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);
        }
    });
}

И моят обвързан 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 -->

Ако направя първоначалното обвързване със стойност, която е правилният формат на обекта, така

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

всичко работи чудесно. Но ако започна със стойност в някакъв друг формат, като ..., value: 'nada', ..., той се прекъсва при опит за достъп до observable.duration (и observable.time_unit.*).

Когато оценявам стойността с правилната настройка, получавам обекта, описан по-горе, обратно. Ако се опитам ръчно да добавя свойствата duration/time_unit като наблюдаеми, пак просто изваждам обратно празния низ.

Как най-добре да актуализирам viewmodel/bindings/etc от моята init функция, така че да се държи така, сякаш моделът първоначално е бил в това състояние, когато съм го инициализирал?


person MaxK    schedule 06.11.2013    source източник
comment
В изгледа изглежда имате type() наблюдаем, който знае дали стойността е продължителност или низ. Не можете ли да използвате това в customBinding и да правите различни неща, ако type() == 'string'?   -  person pax162    schedule 07.11.2013
comment
Бих могъл, да, и това е част от моя резервен план. Ако трябва, мога да имам множество наблюдаеми от тип стойност (durationValue, stringValue, dateValue и т.н.) и стойността да бъде изчислена, която хваща подходящата въз основа на типа, но ако мога да разбера този проблем, мога да избегна дублирането на всичко това код (може би леко модифициран) на редица места и просто го обработете от обвързванията, просто обвързвайки към наблюдаваната стойност.   -  person MaxK    schedule 07.11.2013
comment
Какъв би бил кодът, който се опитвате да не дублирате?   -  person pax162    schedule 07.11.2013
comment
Това персонализирано обвързване се използва на много различни места/модели на изгледи. Вече имам 2 места, където това е проблем. Използвайки заобиколното решение за тази vm, ще трябва да направя превключване на типа и да върна подходящата обвързана стойност, но тя ще бъде различна за всяка vm. Да мога да заобиколя това би ми позволило просто да се свържа с vm и да заобиколя изчислената логика във всяка vm.   -  person MaxK    schedule 07.11.2013
comment
Но дори и да откриете типа на viewModel, няма ли да трябва да имате превключвател в customBinding, за да правите различни неща според типа? Съжалявам, ако не разбирам правилно проблема, тук е малко късно.   -  person pax162    schedule 07.11.2013


Отговори (1)


Персонализираното обвързване не е решението на вашия проблем. Knockout ще обвърже всичко, което срещне в DOM, следователно с какъвто и тип обект да инициализирате стойността си, другият ще се провали. Приложих нещо подобно, където наблюдаема в моя изглед-модел съхранява множество „типове“ обекти, към които ще ви трябват „специфични за типа“ части от потребителския интерфейс. Ето как се справих с проблема:

Премахнете всички екземпляри на if: type() == '<type>' и внедрете всяка част от HTML като шаблон.

Сега преработете процеса на вземане на решения. Използвайте изчислена наблюдаема, за да решите кой шаблон да се показва въз основа на 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';
       }
    });

Сега просто добавете контейнер за шаблон...

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

Може да искате да адаптирате това, за да отговаря на спецификата на вашето приложение, но това е по-подреден подход и ще се мащабира много по-добре, ако искате да съхранявате нарастващо разнообразие от типове обекти.

person Michael Papworth    schedule 07.11.2013