Я делаю эту привязку нокаута неправильно? - PullRequest
0 голосов
/ 13 марта 2012

Код код:

<ul data-bind="foreach: BackColorOptions">
    <li data-bind="css: { selected: Selected }">
        <label>
            <input type="radio" name="BackColorOption" 
                data-bind="value: Color, checked: $root.BackColor" />
        </label>
    </li>
</ul>
@{
    var jsonModel = new System.Web.Script.Serialization.
        JavaScriptSerializer().Serialize(Model);
}
<input type="hidden" id="JsonModel" value='@jsonModel' />

модель представления код:

var initialData = $.parseJSON($('#JsonModel').val());

function BackColorOption(data, parent) {
    var self = this;
    self.parent = parent;
    self.Text = ko.observable(data.Text);
    self.Color = ko.computed(function () {
        return '#' + self.Text().toLowerCase();
    });
    self.Selected = ko.computed(function () {
        var backColor = self.parent.BackColor();
        if (backColor) {
            return backColor.toLowerCase() == self.Color;
        }
        return false;
    });
}

function TestViewModel() {
    var self = this;

    self.BackColor = ko.observable(initialData.BackColor);

    var mappedBackColorOptions = $.map(initialData.BackColorOptions, 
        function (item) {
            return new BackColorOption(item, self);
        }
    );
    self.BackColorOptions = ko.observableArray(mappedBackColorOptions);

}

ko.applyBindings(new TestViewModel());

Модель код:

string BackColor { get; set; }
SelectListItem[] BackColorOptions
{
    get
    {
        return new[] 
        { 
            new SelectListItem{Text = "cc0000"},
            new SelectListItem{Text = "ff9900"},
            new SelectListItem{Text = "dddd33"},
            new SelectListItem{Text = "009900"},
            new SelectListItem{Text = "00cccc"},
            new SelectListItem{Text = "0066ff"},
            new SelectListItem{Text = "9900ff"},
            new SelectListItem{Text = "ff00ff"},
        };
    }
}

Приведенный выше код работает должным образом в IE (8) и Chrome (17), но не в FF (10.0.2). Я в основном пытаюсь сделать селектор цвета, похожий на ярлыки проблемы GitHub. Представление отображает набор переключателей, по которым вы можете выбрать цвет. Когда радио проверяется, я добавляю selected css class к родителю <li>. Класс css приводит к появлению значка галочки над <li>.

В Firefox выбранный класс CSS применяется только после того, как пользователь прошел и проверил каждую радиокнопку хотя бы один раз. Я отладил и обнаружил, что это из-за того, как вычисляемая наблюдаемая self.Color оценивается в замыкании BackColorOption. Перед первой проверкой радио, typeof(self.Color) == 'function' оценивается как истинное. Однако после проверки typeof(self.Color) == 'string' оценивается как true.

Это typeof(self.Color) поведение одинаково как для Firebug, так и для js-отладчика Chrome. Однако проблема в FF из-за этой строки в self.Selected, вычисляемой наблюдаемой в закрытии BackColorOption:

return backColor.toLowerCase() == self.Color;

Chrome & IE по-прежнему возвращают true, даже если self.Color является функцией вместо строки. Однако Firefox этого не делает. Когда self.Color является функцией, она возвращает false. Вот почему вы должны проверить каждую радиостанцию ​​хотя бы один раз, прежде чем класс css будет добавлен в <li> и появится значок.

Я все еще немного новичок в нокауте, и, возможно, не совсем правильно вызывать свойство viewmodel как функцию, когда это необходимо. Мне все еще немного неясно, когда использовать скобки () и когда их опускать. Есть ли другой способ написать вычисляемую наблюдаемую self.Selected, которая зависит от вычисляемой наблюдаемой self.Color (в замыкании BackColorOption)?

Обновление 1

Мне удалось заставить это работать в FF 10.0.2 со следующим:

self.Selected = ko.computed(function () {
    var backColor = self.parent.BackColor();
    var selfColor = self.Color;
    if (typeof (selfColor) === 'function')
        selfColor = self.Color();
    if (backColor) {
        return backColor.toLowerCase() === selfColor;
    }
    return false;
});

Однако мне кажется, что я борюсь с нокаутом. Разве это не должно "просто работать"?

1 Ответ

2 голосов
/ 13 марта 2012

Обвязка value в KO не совсем подходит для переключателей и флажков. В вашей ситуации ваши радиокнопки должны иметь атрибут value, чтобы при нажатии на них это значение можно было использовать для обновления вашей TestViewModel.BackColor наблюдаемой.

Обычно с переключателями вы не хотите, чтобы атрибут value изменялся (если вообще когда-либо) после рендеринга html.

Итак, я изменил ваш HTML-шаблон с использования привязки value вместо использования привязки attr (атрибут html). Теперь это просто установка атрибута value html ваших радиокнопок. Привязка checked обеспечивает синхронизацию вашей наблюдаемой TestViewModel.BackColor с любым значением value переключателя, который отмечен.

Смотрите эту скрипку: http://jsfiddle.net/m2KQ2/

Кроме того, строка в вашей функции BackColorOption содержит опечатку:

self.Selected = ko.computed(function () {
    var backColor = self.parent.BackColor();
    if (backColor) {
        return backColor.toLowerCase() == self.Color; //<-- should be Color();
    }
    return false;
});
...