Код просмотра:
<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' />
Код viewmodel:
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'
принимает значение true. Однако после проверки 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>
и появится значок.
Я все еще немного новичок в нокауте и, возможно, неправильно вызываю свойство модели представления как функцию, когда это предполагается. Я все еще немного не понимаю, когда использовать скобки ()
, а когда их опускать. Есть ли другой способ написать вычисляемую наблюдаемую 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;
});
Тем не менее, это похоже на то, что я борюсь с нокаутом. Разве это не должно «просто работать»?