Продолжается подписка на наши издания! Вы не забыли подписаться?

Knockout

Вступление
OK, как это использовать?
Отслеживаемые значения (Observables)
MVVM и View Model
Активация Knockout
Отслеживаемые значение (Observables)
Зависимые отслеживаемые значения (dependent observables)
Управление ‘this’
Как работает отслеживание зависимостей
Массивы отслеживаемых значений (observable arrays)
Чтение информации из observableArray
Функции observableArray.
Манипуляции с observableArray
Заключение

Вступление

Knockout – это JavaScript-библиотека, помогающая создавать богатые, быстродействующие пользовательские интерфейсы, как для отображения, так и для редактирования информации, с четкой нижележащей моделью данных. Везде, где вы используете динамически обновляемые разделы UI (то есть изменяющиеся в зависимости от действий пользователя или изменений во внешних источниках данных), Knockout может помочь реализовать их более просто.

Основные возможности:

Разработчики, использовавшие Silverlight или WPF, могут узнать в КО пример паттерна MVVM; разработчики, более знакомые с Ruby on Rails или другой технологией, использующей MVC, могут рассматривать КО как realtime-форму MVC с декларативным синтаксисом. В другом смысле КО можно рассматривать как общий способ создания UI для редактирования JSON-данных… в общем, как вам удобнее, так и рассматривайте. :)

OK, как это использовать?

Коротко: определите ваши данные как объект модели JavaScript, и затем привяжите к ним DOM-элементы и/или шаблоны.

Посмотрим на пример

Возьмем экран, на котором авиапассажиры выбирают дополнения к полетному меню. Мы хотим позволить им выбрать блюдо, а затем динамически отобразить его описание и цену. Сперва давайте объявим доступные блюда:

var availableMeals = [          
  { 
    mealName: 'Standard', description: 'Dry crusts of bread', extraCost: 0 
  },
  { 
    mealName: 'Premium', 
    description: 'Fresh bread with cheese', extraCost: 9.95 
  },
  { 
    mealName: 'Deluxe', description: 'Caviar and vintage Dr Pepper', 
    extraCost: 18.50 
  }
];

Если мы хотим отобразить эти опции в UI, мы можем привязать к ним выпадающий список (т.е. HTML-элемент <select>). Например:

<h3>Meal upgrades</h3>
<p>Make your flight more bearable by selecting a meal to match your social and economic status.</p>
Chosen meal: <select data-bind="options: availableMeals, 
                                optionsText: 'mealName'"></select>

Чтобы активировать Knockout и оживить созданные связи, добавим следующие строки сразу после объявления availableMeals:

var viewModel = 
{
  /* сейчас мы это заполним */
};
ko.applyBindings(viewModel); // Запускаем Knockout 

Подробнее о моделях представления данных и MVVM можно прочитать здесь: http://knockoutjs.com/docu­men­tation/observables.html. Пока же страница будет выглядеть так:


Отклик на выбор элемента

Теперь определим простую модель данных, чтобы описать, какой элемент выбран. Добавим следующее свойство к существующей модели представления:

var viewModel = 
{ 
  chosenMeal: ko.observable(availableMeals[0])
};

Что за ko.observable, спросите вы? Это фундаментальная концепция в Knockout. Это значение, за которым UI может "следить" и откликаться на его изменения. В данном случае мы говорим, что UI может видеть значение, представляющее выбранное блюдо, и что исходно оно равно первому вхождению availableMeal (т.е. "стандартному" блюду).

Давайте свяжем chosenMeal с выпадающим списком. Нужно просто обновить атрибут data-bind элемента <select>, чтобы сказать, что value элемента должно читать и записывать свойство модели chosenMeal:

Chosen meal: <select data-bind="options: availableMeals, 
                                optionsText: 'mealName',
                                value: chosenMeal"></select>

Технически теперь оно читает и записывает свойство chosenMeal, но мы не увидим, что хоть что-то происходит, пока не дополним UI. Давайте отобразим описание и цену выбранного блюда:

<p>
    You've chosen: 
    <b data-bind="text: chosenMeal().description"></b>
    (price: <span data-bind='text: chosenMeal().extraCost'></span>)
</p>


Эта часть UI будет динамически обновляться при выборе различных элементов выпадающего списка:

Еще раз об отслеживании зависимостей

Еще одно: лучше бы показывать цену в формате денежных единиц. Определим для этого JavaScript-функцию:

function formatPrice(price) 
{
  return price == 0 ? "Free" : "$" + price.toFixed(2);
}

…и обновим связывание для его использования…

 (price: <span data-bind='text: formatPrice(chosenMeal().extraCost)'></span>)

…после чего UI будет выглядеть несколько лучше:


Форматирование цены показывает, что в связываниях можно использовать произвольный JavaScript, и КО все-таки сможет определить, от каких наблюдаемых элементов зависят связывания. Именно так он узнает, какие части UI надо обновлять при изменениях модели. Он не перерисовывает UI полностью и постоянно – только те части, в которых изменились наблюдаемые элементы.

Можно создать вложенные цепочки наблюдаемых элементов, вычисляемых по другим наблюдаемым элементам (например, подсчитывать сумму в зависимости от указанного количества). Когда в цепочке что-то изменяется, зависимости с этого места пересчитываются, и все ассоциированные части UI обновляются. Вам не придется объявлять ассоциации между наблюдаемыми элементами явно; фреймворк выводит их во время исполнения кода.

Предыдущий пример очень прост и не показывает многих возможностей КО, делающих его эффективным. Подробней о наблюдаемых элементах и о массивах наблюдаемых элементов сказано ниже.

Конкурирует ли КО с jQuery (или Prototype и т.п.), или работает совместно с ними?

Все любят jQuery! Это выдающаяся замена неуклюжему и нелогичному DOM API, который мы вынуждены были использовать раньше. jQuery – это прекрасный низкоуровневый способ манипулировать элементами и обработчиками событий на Web-странице. Я, разумеется, использую jQuery для низкоуровневых DOM-мани­пу­ляций. КО решает другую проблему.

Как только UI становится нетривиальным, и обзаводится несколькими пересекающимися поведениями, при использовании одного лишь jQuery все становится запутанным и дорогим в поддержке. Рассмотрим пример: нужно выводить список элементов, указывая число элементов списка, и делать доступной кнопку "Add", только если элементов меньше пяти. В jQuery нет концепции нижележащей модели данных, так что получать число элементов придется, выводя его из количества элементов TR в таблице или числа DIV в соответствующем CSS-классе. Возможно, число элементов выводится в каком-то SPAN, и вам нужно помнить о том, что текст этого SPAN нужно обновить, когда пользователь добавит элемент. Нужно также не забыть отключить кнопку "Add", когда количество TR достигнет 5. Позже, если вас попросят реализовать также кнопку "Delete", вам придется выяснять, какие DOM-элементы изменять по нажатию на нее.

В чем отличия Knockout?

КО все серьезно упрощает. Он позволяет масштабировать сложность, не опасаясь несоответствий. Просто представьте элементы как JavaScript-массив, и затем используйте шаблон для трансформации этого массива в таблицу или набор элементов DIV. При изменениях массива UI также обновится (вам не придется выяснять, как и куда вставлять новые TR). Остальной UI останется без изменений. Например, можно декларативно связать SPAN, выводящий число элементов (и использовать где угодно на странице, не только в шаблоне):

There are <span data-bind="text: myItems().count"></span> items

Вот оно! Вам не нужно писать код для его обновления; он обновится сам, когда изменится массив myItems. Аналогично, чтобы сделать доступной или недоступной кнопку "Add" в зависимости от числа элементов, достаточно написать:

<button data-bind="enable: myItems().count < 5">Add</button>

Позже, когда вас попросят реализовать функциональность кнопки "Delete", вам не придется выяснять, с какими частями UI она должна взаимодействовать; вы просто заставите ее изменить нижележащую модель данных.

Суммируя: КО не соревнуется с jQuery или аналогичными DOM API. КО дает альтернативный, высокоуровневый способ связать модель данных с UI. Сам КО не зависит от jQuery, но вы, конечно, можете использовать jQuery вместе с ним, и это зачастую полезно, если вы хотите делать такие вещи, как анимированные переходы.

Отслеживаемые значения (Observables)

Knockout строится на трех основных концепциях:

  1. Observables (отслеживаемые значения) и отслеживание зависимостей
  2. Декларативные связывания
  3. Шаблоны

На этой странице рассказывается о первой из трех. Но, прежде чем приступить, разрешите мне рассказать о паттерне MVVM и концепции модели представления (view model).

MVVM и View Model

Model-View-View Model (MVVM) – это паттерн проектирования, применяемый при создании пользовательских интерфейсов. Он описывает, как можно сохранить простоту потенциально сложного UI, разделив его на три части:

Заметьте, что это не сам UI: здесь нет никаких кнопок или стилей отображения. Это также и не модель с сохранением данных – она содержит несохраненные данные, с которыми работает пользователь. При использовании КО модели представления – это чистые JavaScript-объекты, которые не содержат никаких сведений об HTML. Поддержание абстрактности модели представления – это способ сохранения ее простоты, позволяющий управлять сложным поведением без риска заблудиться.

При использовании КО представление – это просто HTML-документ с декларативной привязкой к модели представления. Альтернативой является использование шаблонов, генерирующих HTML, который использует данные из модели представления.

Чтобы создать модель представления с помощью КО, просто объявите JavaScript-объект, например:

var myViewModel = 
{
  personName: 'Bob',
  personAge: 123
};

Затем вы можете создать очень простое представление этой модели представления, используя декларативное связывание. Например, следующая разметка выводит значение personName:

The name is <span data-bind="text: personName"></span>

Активация Knockout

Атрибут data-bind не является родным для HTML, но это не страшно (он напрямую совместим с HTML 5, и не вызывает проблем в HTML 4, хотя валидатор укажет, что это неопознанный атрибут). Но, поскольку браузер не знает, что он значит, вам нужно активировать Knockout, чтобы этот атрибут заработал.

Чтобы активировать Knockout, добавьте следующую строку в блок <script>:

ko.applyBindings(myViewModel);

Вы можете поместить блок <script> внизу HTML-документа, или поместить его в начало и обернуть его содержимое в DOM-обработчик, например, функцию $ jQuery.

Вот и все! Теперь ваше представление будет выглядеть так, будто вы написали следующий HTML:

The name is <span>Bob</span>

Если вы интересуетесь, что делают параметры ko.applyBindings, то:

Отслеживаемые значение (Observables)

ОК, вы уже видели, как создать базовую модель представления, и как отобразить одно из ее свойств, используя связывание. Но одним из ключевых достоинств КО является автоматическое обновление UI при изменении модели представления. Откуда КО знает, какая часть модели представления изменилась? Ответ: нужно объявить свойства модели как отслеживаемые значения (observables), поскольку эти особые JavaScript-объекты могут оповещать подписчиков об изменениях и автоматически отслеживать зависимости.

Перепишем, например, предыдущий объект модели представления следующим образом:

var myViewModel = 
{
  personName: ko.observable('Bob'),
  personAge: ko.observable(123)
};

Вам вообще не придется изменять представление – будет работать тот же синтаксис data-bind. Разница в том, что теперь он будет отслеживать изменения, и, при наличии таковых, обновлять представление автоматически.

Чтение и запись отслеживаемых значений

Не все браузеры поддерживают JavaScript-методы get/set (да-да, я об IE), поэтому для совместимости объекты ko.observable – на самом деле функции.

Смысл отслеживаемых значений в том, что за ними можно следить, т.е., другой код может сказать, что он хочет получать оповещения об изменениях. Это то, чем многие из встроенных связываний КО занимаются внутренне. Так что, если написать data-bind="text: person­Name", связывание text подписывается на получение оповещений об изменениях personName (если оно, как сейчас, является отслеживаемым значением).

При изменении значения имени на 'Mary' с помощью вызова myViewModel.personName('Mary'), связывание text автоматически обновит содержимое текста соответствующего DOM-элемента. Таким образом изменения модели представления автоматически отображаются в представлении.

Явная подписка на отслеживаемые значения

Опытные пользователи, если они хотят зарегистрировать собственную подписку и получать оповещения об изменениях отслеживаемых элементов, могут вызвать функцию subscribe. Например:

myViewModel.personName.subscribe(function(newValue) {
    alert("The person's new name is " + newValue);
});

Функция subscribe показывает, как много частей КО работают скрытно. При желании можно отменить подписку: сперва нужно захватить ее в переменную, а затем вызвать функцию dispose, т.е.:

var subscription = myViewModel.personName.subscribe(function(newValue) { /* do stuff */ });
// ...затем...
subscription.dispose(); // оповещения больше не нужны

Большую часть времени вам не нужно делать это, поскольку встроенные связывания и система шаблонов заботятся об управлении подписками.

Зависимые отслеживаемые значения (dependent observables)

Что, если у вас есть отслеживаемое значение для firstName и еще одно – для lastName, а вы хотите выводить полное имя? В этом случае пригодятся зависимые отслеживаемые значения (dependent observables), которые оповещают о своем изменении, когда изменяется любая из их зависимостей.

Например, если есть следующая модель представления:

var viewModel = 
{
  firstName: ko.observable('Bob'),
  lastName: ko.observable('Smith')
};

…можно добавить зависимое отслеживаемое значение для получения полного имени:

viewModel.fullName = ko.dependentObservable(function() 
{
  return this.firstName() + " " + this.lastName();
}, viewModel);

Теперь можно привязать к нему элементы UI, то есть:

The name is <span data-bind="text: fullName"></span>

…и они будут обновляться при изменениях firstName или lastName (функция, формирующая полное имя, будет вызываться при каждом изменении ее зависимостей, и возвращаемое значение будет передаваться наблюдателям – элементам UI или другим зависимым отслеживаемым значениям).

Управление ‘this’

Если вы интересуетесь, что значит второй параметр ko.dependentObservable (в предыдущем коде это viewModel), то он определяет значение this при вычислении зависимого отслеживаемого значения. Без этого нельзя ссылаться на this.firstName() или this.lastName(). Опытные JavaScript-программисты сочтут это очевидным, но если вы не знакомы с JavaScript, это может показаться странным (такие языки, как C# и Java, никогда не ожидают от программиста задания значения this, но JavaScript ведет себя именно так, поскольку его функции по умолчанию не являются частью какого-либо объекта).

К сожалению, у литералов JavaScript-объекта нет способа сослаться на самих себя, так что вы должны добавить зависимые observables в объекты модели представления, написав myViewModelObject.myDependent­Ob­servable = ..., и их нельзя объявить встроено. Другими словами, нельзя написать так:

var viewModel = 
{
  myDependentObservable: ko.dependentObservable(function() 
  {
    ...
  }, /* отсюда нельзя сослаться на viewModel, так что это не работает */)
}

…вместо этого нужно писать:

var viewModel = 
{
  // Добавьте сюда другие свойства, если хотите
};
viewModel.myDependentObservable = ko.dependentObservable(function() 
{
  ...
}, viewModel); // OK

До тех пор, пока вы знаете, чего ожидать, это не проблема.

Цепочки зависимостей работают

Конечно, можно создавать целые цепочки зависимых отслеживаемых значений. Например, возможно иметь:

Изменения items или selectedIndexes распространяются по цепи зависимых observables, которые, в свою очередь, обновят связанный с ними UI.

Как работает отслеживание зависимостей

Начинающим это знать ни к чему, но более опытные разработчики захотят узнать, почему я столько раз повторяю заявления о том, что КО автоматически отслеживает зависимости и обновляет нужные части UI…

На самом деле это очень просто и довольно красиво. Алгоритм отслеживания выглядит примерно так:

  1. При объявлении зависимого observable, KO немедленно вызывает функцию-вычислитель для получения его исходного значения.
  2. Пока функция-вычислитель работает, KO ведет лог всех observables (или зависимых observables), значения которых читает вычислитель.
  3. После окончания работы вычислителя, КО создает подписки на каждый из задействованных observables (или зависимых observables). Функция обратного вызова подписки снова запускает вычислитель, возвращая процесс к пункту 1 (удаляя все старые, ненужные подписки).
  4. КО оповещает подписчиков о новых значениях отслеживаемых значений.

Таким образом, КО не выявляет зависимости при первом запуске вычислителя – он их выявляет каждый раз заново. Это значит, например, что зависимости могут динамически изменяться: зависимость А может определять зависимости от В или С. Это положение изменится, когда вы измените либо A, либо текущий выбор изменений В или С. Объявлять зависимости не нужно: они выводятся из исполняемого кода во время исполнения.

Еще один фокус состоит в том, что декларативные связывания (включающие результаты применения шаблонов) просто реализуются как зависимые observables. Так, если шаблон читает значение observable, связывание этого шаблона становится зависимым от этого observable, и перевычисляется при его изменениях. Вложенные шаблоны работают автоматически: если шаблон X отрисовывает шаблон Y, который читает отслеживаемое значение Z, то при изменении Z напрямую затрагивается только Y, и он будет единственной перерисовываемой частью экрана.

Массивы отслеживаемых значений (observable arrays)

Если нужно отслеживать изменения одного объекта и откликаться на них, используются отслеживаемые значения. Если же нужно отслеживать изменения коллекции, используется observableArray. Это полезно во многих сценариях, где выводятся на экран или редактируются множество значений, и повторяющиеся разделы UI появляются и исчезают по мере добавления и удаления элементов.

Пример

// исходно пустой массив
var myObservableArray = ko.observableArray([]); 
// добавляем значение и оповещаем наблюдателей
myObservableArray.push('Some value');

Чтобы посмотреть, как связать observableArray с UI и позволить пользователю изменять его, см. пример простого списка.

Ключевой момент: observableArray отслеживает наличие объектов в массиве, а не состояние этих объектов

Простое помещение объекта в observableArray не делает все его свойства отслеживаемыми. Конечно, можно сделать эти свойства отслеживаемыми, если хочется, но это отдельный вопрос. observableArray просто отслеживает, какие объекты в нем содержатся, и оповещает подписчиков, если объекты удаляются или добавляются.

Чтение информации из observableArray

observableArray представляет собой observable, чье значение – массив (кроме того, у observableArray есть некоторые дополнительные возможности, о которых я вскоре расскажу). Таким образом, можно получить нижележащий JavaScript-массив, вызвав observableArray как функцию без параметров. После этого можно считать информацию из этого нижележащего массива. Например:

alert('The length of the array is ' + myObservableArray().length);
alert('The first element is ' + myObservableArray()[0]);

Технически для работы с этим массивом можно использовать любую из JavaScript-функций работы с массивами, но обычно есть лучший выбор. observableArray имеет собственные эквивалентные функции, которые будут более полезны, так как:

  1. Они работают во всех браузерах (например, родная JavaScript-функция indexOf не работает в IE 8 и более ранних, а КО-версия indexOf работает везде).
  2. Для функций, изменяющих содержимое массива, например, push и splice, методы КО автоматически включают механизм отслеживания зависимостей, так что все зарегистрированные подписчики оповещаются об изменениях, и UI автоматически обновляется.
  3. Синтаксис более удобен. Чтобы вызвать КО-метод push, достаточно написать myObservable­Ar­ray.push(...). Это несколько лучше, чем вызывать метод нижележащего массива с помощью my­ObservableArray().push(...).

Функции observableArray.

indexOf

Функция indexOf возвращает индекс первого элемента массива, равного параметру. Например, myObser­vableArray.indexOf('Blah') возвратит начинающийся с нуля индекс первого вхождения массива, равного Blah, или -1, если нужное значение не найдено.

slice

Функция slice – это observableArray-эквивалент Java­Script-функции slice (т.е. она возвращает вхождения массива от заданного стартового да заданного конечного индекса). Вызов myObservableArray.slice(...) эквивалентен вызову такого же метода нижележащего массива (т.e., myObservableArray().slice(...)).

Манипуляции с observableArray

observableArray предоставляет знакомый набор функций для изменения содержимого массива и уведомления слушателей.

pop, push, shift, unshift, reverse, sort, splice

Исполнение всех этих функций эквивалентно исполнению родных JavaScript-функций работы с массивами, с последующим уведомлением слушателей об изменениях:

По умолчанию используется сортировка по алфавиту (для строк) или по возрастанию (для чисел).

Можно передать функцию, управляющую сортировкой массива. Эта функция должна принимать любые два объекта из массива и возвращать отрицательное значение, если первый аргумент меньше, положительное – если второй элемент меньше, или 0, если объекты равны. Например, чтобы отсортировать массив объектов ‘person’ по фамилиям, можно написать

myObservableArray.sort(function(left, right) 
{ 
  return left.lastName == right.lastName ? 0 : (
    left.lastName < right.lastName ? -1 : 1) 
})

remove и removeAll NormalBullets1

У observableArray есть еще несколько полезных методов, которых нет у обычных JavaScript-массивов:

destroy и destroyAll

Функции destroy и destroyAll в основном служат для удобства разработчиков, использующих Ruby on Rails:

Зачем же нужно это свойство _destroy? Как уже говорилось, на самом деле это интересно только Rails-разработчикам. В Rails принято соглашение, что когда вы передаете граф JSON-объектов, фреймворк автоматически конвертирует его в граф ActiveRecord-объектов, после чего сохраняет в БД. Он знает, какие объекты уже есть в БД, и генерирует корректные выражения INSERT или UPDATE. Чтобы заставить фреймворк удалить запись, нужно пометить ее свойством _destroy со значением true.

Заметьте, что когда КО отрисовывает шаблон foreach, он автоматически скрывает объекты, помеченные _destroy со значением true. Таким образом, у вас появляется нечто вроде кнопки “delete”, вызывающей метод destroy(someItem), который приведет к немедленному исчезновению указанного элемента из видимого UI. Позже, когда вы передадите граф JSON-объектов Rails, эти элементы будут удалены из БД.

Встроенные связывания

Связывание "visible"

Назначение

Связывание visible делает ассоциированные DOM-элементы скрытыми или видимыми в соответствии со значением, передаваемым связыванию.

Пример

<div data-bind="visible: shouldShowMessage">
    You will see this message only when "shouldShowMessage" holds a true value.
</div>
 
<script type="text/javascript">
    var viewModel = {
        shouldShowMessage: ko.observable(true) // сообщение исходно видимо
Message initially visible
    };
    viewModel.shouldShowMessage(false); // ... теперь оно скрыто
    viewModel.shouldShowMessage(true); // ... теперь оно опять видимо
</script>

Параметры

Когда значение этого параметр похоже на false (т.е. булево значение false, или числовое значение 0, или null, или undefined), связывание устанавливает yourElement.style.display в none, что приводит к его скрытию. Это имеет приоритет перед любым другим стилем, определенным в CSS.

Когда значение этого параметр похоже на true (т.е. булево значение true, или не равный null объект, или массив), связывание удаляет значение yourElement.style.display, и элемент становится видимым.

При этом будет применен стиль отображения, определенный в CSS (так что правила CSS наподобие display:table-row прекрасно работают с этим связыванием).

ПРИМЕЧАНИЕ

Примечание: используйте функции и выражения для управления видимостью элемента

В качестве значения параметра можно использовать также JavaScript-функции или произвольные JavaScript-выражения. В этом случае КО исполнит функцию или вычислит значение выражения и использует результат для определения видимости элемента.

Например:

<div data-bind="visible: myValues().length > 0">
   Это сообщение видимо, если у 'myValues' есть хотя бы один член. 
You will see this message only when 'myValues' has at least one member.
</div>
 
<script type="text/javascript">
    var viewModel = {
        myValues: ko.observableArray([]) // Исходно пусто, так что сообщение скрыто
Initially empty, so message hidden
    };
    viewModel.myValues.push("some value"); // теперь видимо
Now visible
</script>

Зависимости

Только от библиотеки Knockout.

Связывание "text"

Назначение

Связывание text заставляет ассоциированный DOM-элемент вывести текстовое значение параметра.

Обычно оно применяется для элементов типа <span> или <em>, традиционно отображающих текст, но технически его можно использовать с любыми элементами.

Пример

Сегодняшнее сообщение: <span data-bind="text: myMessage"></span>
 
<script type="text/javascript">
    var viewModel = {
        myMessage: ko.observable() // Исходно пусто
    };
    viewModel.myMessage("Hello, world!"); // появляется текст
</script>

Параметры

ПРИМЕЧАНИЕ

Примечание 1: использование функций и выражений для определения значений текста

Если вы хотите программно определять текст, можно создать отслеживаемую функцию и использовать ее для определения того, какой текст выводить.

Например:

The item is <span data-bind="text: priceRating"></span> today.
 
<script type="text/javascript">
    var viewModel = 
    {
        price: ko.observable(24.95)
    };
    viewModel.priceRating = ko.dependentObservable(function() 
    {
        return this.price() > 50 ? "expensive" : "affordable";
    }, viewModel);
</script>
ПРИМЕЧАНИЕ

Заметьте, что текст будет меняться с “expensive” на “affordable” при изменении price.

Однако можно и не создавать зависимое отслеживаемое значение ради такого простого случая. Можно передать связыванию text произвольное JavaScript-вы­ра­жение. Например:

The item is <span data-bind="text: price() > 50 ? 'expensive' : 'affordable'"></span> today.
ПРИМЕЧАНИЕ

Это даст точно тот же результат без создания зависимого отслеживаемого значения priceRating.

Примечание 2: об HTML-кодировании

Поскольку это связывание задает текстовое значение, используя innerText или textContent (а не innerHTML), не стоит использовать в нем какой-либо HTML или скрипт. Например, если написать:

viewModel.myMessage("<i>Hello, world!</i>");
ПРИМЕЧАНИЕ

…это не выведет текст курсивом, но отобразит обычный текст вместе с угловыми скобками.

В Knockout на текущий момент нет связывания html (для задания HTML-контента), но несложно создать его, если оно понадобится.

Примечание 3: об ошибке с пробелом в IE 6

В IE 6 есть странная ошибка, из-за которой он иногда игнорирует пробел, следующий непосредственно за пустым span. Напрямую это не относится к Knockout, но если вы захотите написать что-то вроде:

Welcome, <span data-bind="text: userName"></span> to our web site.
ПРИМЕЧАНИЕ

…и IE 6 не покажет пробела перед словами "to our web site", проблему можно устранить, поместив любой текст в <span>, т.e.:

Welcome, <span data-bind="text: userName">&nbsp;</span> to our web site.    
ПРИМЕЧАНИЕ

В других браузерах и в более новых версиях IE этой ошибки нет.

Зависимости

Только от библиотеки Knockout.

Связывание "css"

Назначение

Связывание css добавляет или убирает один или несколько CSS-классов к ассоциированному DOM-элементу. Это пригодится, например, чтобы выделить какое-нибудь значение красным, если оно станет отрицательным.

ПРИМЕЧАНИЕ

если вы хотите использовать не CSS-класс, а атрибут style, см. связывание "style".

Пример

<div data-bind="css: { profitWarning: currentProfit() < 0 }">
   Profit Information
</div>
 
<script type="text/javascript">
  var viewModel = 
  {

    // Положительное значение, так что исходно 
    // класс "profitWarning" не применяется
    currentProfit: ko.observable(150000) 
  };
  viewModel.currentProfit(-50); // Приводит к применению класса "profitWarning"
</script>

Это применит CSS-класс profitWarning, если значение currentProfit упадет ниже нуля, и удалит этот класс, когда оно станет выше нуля.

Параметры

Нужно передать JavaScript-объект, имена свойств которого – это ваши CSS-классы, а их значения равны true или false, в зависимости от того, должен ли класс применяться в текущее время.

Одновременно можно задать несколько CSS-классов. Например, если у вашей модели представления есть свойство isSevere,

<div data-bind="css: { profitWarning: currentProfit() < 0, majorHighlight: isSevere }">

Не-булевы значения вольно трактуются как булевы. Например, 0 и null рассматриваются как false, а 21 и не-null-объекты расцениваются как true.

Если параметр ссылается на отслеживаемое значение, связывание будет добавлять или удалять CSS-класс при изменениях этого значения. Если параметр не ссылается на отслеживаемое значение, он добавит или удалит класс только один раз.

Как обычно, в качестве значений параметра можно использовать произвольные JavaScript-выражения и функции.

ПРИМЕЧАНИЕ

Примечание: применение CSS-классов, чьи имена не являются легальными именами JavaScript-переменных

Если вы хотите применить CSS-класс my-class, вы не можете написать:

<div data-bind="css: { my-class: someValue }">...</div>
ПРИМЕЧАНИЕ

…поскольку my-class не является легальным идентификатором в этом месте. Решение простое: просто оберните имя идентификатора в кавычки, чтобы оно стало строковым литералом. Это корректно для литерала JavaScript-объекта (технически, согласно спецификации JSON, вы в любом случае обязаны так поступать, но на практике это не нужно). Например:

<div data-bind="css: { 'my-class': someValue }">...</div>

Зависимости

Только от библиотеки Knockout.

Связывание "style"

Назначение

Связывание style добавляет или удаляет одно или несколько значений стилей ассоциированного DOM-стиля. Это пригодится, например, чтобы выделить какое-то значение красным, если оно стало отрицательным, или изменить ширину столбца для отражения изменения числового значения.

ПРИМЕЧАНИЕ

если вы хотите не указывать явно значения стиля, а использовать CSS-класс, используйте связывание "css".

Пример

<div data-bind="style: { color: currentProfit() < 0 ? 'red' : 'black' }">
   Profit Information
</div>
 
<script type="text/javascript">
  var viewModel = 
  {
    currentProfit: ko.observable(150000) // Положительное значение, 
                                         // исходно – черное 
  };
  viewModel.currentProfit(-50); // Меняет цвет DIV на красный
</script>

Этот код изменяет значение свойства style.color элемента на red, когда значение currentProfit падает ниже нуля, и на black, когда оно поднимается выше нуля.

Параметры

Содержит JavaScript-объект, в котором имена свойств соответствуют именам стилей, а значения соответствуют значениям стилей.

Можно задать несколько значений стиля одновременно. Например, если у модели представления есть свойство isSevere:

<div data-bind="style: 
{ 
  color: currentProfit() < 0 ? 'red' : 'black', 
  fontWeight: isSevere() ? 'bold' : '' 
}">
  ...
</div>

Еcли параметр ссылается на отслеживаемое значение, связывание будет обновлять стили при изменениях отслеживаемого значения. В обратном случае стили будут заданы однократно.

Как обычно, в качестве значения параметра можно использовать произвольные JavaScript-функции и выражения.

Примечание: применение стилей, чьи имена не являются корректными именами JavaScript-переменных

Если вы хотите использовать стили font-weight или text-decoration, или вообще любой стиль, чье имя не является корректным JavaScript-идентификатором (например, содержит дефис), для этого стиля нужно использовать JavaScript-имя. Например:

Зависимости

Только от библиотеки Knockout.

Связывание "click"

Назначение

Связывание click добавляет обработчик события, вызывающий выбранную вами JavaScript-функцию при щелчке по ассоциированному DOM-элементу. Чаще всего оно используется с элементами типа button, input и a, но может работать с любым отображаемым на экране DOM-элементом.

Пример

<div>
    You've clicked <span data-bind="text: numberOfClicks"></span> times
    <button data-bind="click: incrementClickCounter">Click me</button>
</div>
 
<script type="text/javascript">
    var viewModel = 
    {
      numberOfClicks : ko.observable(0),
      incrementClickCounter : function() 
      {
        var previousCount = this.numberOfClicks();
        this.numberOfClicks(previousCount + 1);
      }
    };
</script>

При каждом щелчке по кнопке будет вызываться функция incrementClickCounter() модели представления, которая, в свою очередь, будет обновлять состояние модели представления, что вызовет обновление UI.

Параметры

Функция, с которой связывается событие click элемента.

Можно ссылаться на любую JavaScript-функцию – она не обязана быть функцией модели представления. Можно сослаться на функцию любого объекта, написав click: someObject.someFunction.

Функции модели представления слегка отличаются тем, что на них можно ссылаться по имени, т.е. можно писать click: incrementClickCounter, а не click: viewModel.incrementClickCounter (хотя технически это тоже корректно).

ПРИМЕЧАНИЕ

Примечание 1: передача параметров функции-обработчику щелчка

Простейший способ передачи параметров – обернуть обработчик в безымянную функцию (лямбду), как в этом примере:

<button data-bind="click: function() 
{ 
  viewModel.myFunction('param1', 'param2') 
}">
    Click me
</button>
ПРИМЕЧАНИЕ

Теперь КО будет вызывать лямбду, не передавая никаких параметров (потому что у нее нет никаких параметров), и затем лямбда вызовет view­Model.my­Function(), передавая параметры 'param1' и 'param2'.

Примечание 2: действия по умолчанию

По умолчанию Knockout не позволяет обработчику события предпринимать действия по умолчанию. Это значит, что если вы используете связывание click для тега a (ссылка), браузер вызовет функцию-обработчик вместо перехода по указанному в ссылке href. Это полезно, поскольку при использовании связывания click обычно ссылка используется как часть UI, управляющего моделью представления, а не как обычная гиперссылка на другую страницу.

Однако если вы хотите разрешить обработку щелчка по умолчанию, просто возвратите true из функции-обработчика click.

Примечание 3: управление this

Начинающие могут пропустить этот раздел, поскольку в нем редко появляется необходимость. Опытные пользователи, возможно, захотят узнать об этом.

Когда КО вызывает функцию-обработчик щелчка, он присваивает this ссылку на вашу модель представления (т.е. модель представлении, переданную при активации КО вызовом ko.applyBindings). Это удобно, если вы вызываете функцию своей модели представления, поскольку в этом случае функция может ссылаться на другие свойства модели представления как на this.someOtherViewModelProperty, как в первом прмере этого раздела.

Если вы хотите, чтобы this указывал на какой-то другой объект (обычно потому что вы вызываете функцию другого объекта модели), есть две простых возможности. Обе отлично работают:

1. Если вы используете синтаксис лямбд, как в примечании 1 выше, вам не надо беспокоиться о this, поскольку литерал функции может использовать произвольный JavaScript-код для вызова любых других функций с помощью обычных правил вызова JavaScript.

2. Если вы не используете синтаксис лямбд, а просто напрямую ссылаетесь на объект функции, как в примере incrementClickCount выше, вы можете использовать bind, чтобы заставить обратный вызов установить this на любой объект по вашему выбору. Например:

<button data-bind="click: someObject.someFunction.bind(someObject)">
    Click me
</button>
ПРИМЕЧАНИЕ

Если вы пишете на C# или Java, вы можете поинтересоваться, почему во втором варианте нужно использовать bind для привязки функции, особенно когда вы уже ссылаетесь на нее как на someObject.someFunction. Причина в том, что в JavaScript сами функции не являются частью классов – это отдельные объекты. Более одного объекта могут содержать ссылку на тот же самый объект функции someFunction, так какой же из них при вызове объекта функции должен быть this? Рантайм не знает этого, если не привязать объект функции к указанному объекту. КО по умолчанию считает this моделью представления, но это можно переопределить, используя bind.

Не требуется использовать bind, если вы используете синтаксис лямбд, как в варианте 1, поскольку JavaScript-код someObject.someFunction() означает “вызвать someFunction, установив this на someObject”.

Зависимости

Только от библиотеки Knockout.

Связывание "submit"

Назначение

Связывание submit добавляет обработчик события, вызывающий выбранную вами JavaScript-функцию при отправке ассоциированного DOM-элемента. Обычно оно используется только с элементами form.

При использовании связывания submit на форме Knockout запрещает для этой формы действие браузера submit, используемое по умолчанию. Другими словами, браузер вызовет вашу функцию-обработчик, но не отправит форму на сервер. Это правильно, поскольку когда вы используете связывание submit, вы, как правило, используете форму как интерфейс к вашей модели представления, а не как обычную HTML-форму. Если вы хотите позволить отправить форму как обычную HTML-форму, просто возвратите true из обработчика submit.

Пример

<form data-bind="submit: doSomething">
    ... содержание формы ...
    <button type="submit">Submit</button>
</div>
 
<script type="text/javascript">
    var viewModel = 
    {
        doSomething : function(formElement) 
        {
            // ... теперь делаем что-то 
        }
    };
</script>

Как показано в этом примере, KO передает элемент формы как параметр функции-обработчику. Вы можете игнорировать этот параметр, если хотите, но в качестве примера того, когда бывает полезно иметь ссылку на этот элемент, см. документацию на ko.postJson (http://knockoutjs.com/documentation/post-json.html).

Почему просто не применить обработчик click к кнопке submit?

Вместо использования submit можно было бы использовать click для кнопки submit. Но у submit есть преимущество – оно также перехватывает альтернативные способы отправки формы, такие как нажатие клавиши enter после ввода в текстовое окно.

Параметры

Основной параметр

Дополнительные параметры отсутствуют.

ПРИМЕЧАНИЕ

О том, как передать дополнительные параметры функции-обработчику, и об управлении this при вызове функций, сказано в разделе, посвященном связыванию click. Все, сказанное о click, применимо и к submit.

Зависимости

Только от библиотеки Knockout.

Связывание "enable"

Назначение

Связывание enable включает ассоциированный DOM-элемент только когда значение параметра – true. Это полезно для таких элементов формы как input, select и textarea.

Пример

<p>
    <input type='checkbox' data-bind="value: hasCellphone" />
    I have a cellphone
</p>
<p>
    Your cellphone number:
    <input type='text' data-bind="value: cellphoneNumber, enable: hasCellphone" />
</p>
 
<script type="text/javascript">
    var viewModel = 
    {
        hasCellphone : ko.observable(false),
        cellphoneNumber: ""
    };
</script>

В этом примере текстовое окно “Your cellphone number” исходно будет отключено. Оно будет задействовано только если пользователь пометит переключатель “I have a cellphone”.

Параметры

Основной параметр

Дополнительные параметры отсутствуют

ПРИМЕЧАНИЕ

Примечание: использование произвольных JavaScript-выражений

Вы не ограничены ссылками на переменные – можно ссылаться на произвольные выражения. Например:

<button 
  data-bind="enabled: parseAreaCode(viewModel.cellphoneNumber()) != '555'">
    Do something
</button>

Зависимости

Только от библиотеки Knockout.

Связывание "disable"

Назначение

Связывание disable отключает ассоциированный DOM-элемент только когда значение параметра – true. Это полезно для таких элементов формы как input, select и textarea.

Это связывание – зеркальное отражение связывания enable. Поэтому вся информация из предыдущего раздела применима и к этому связыванию.

Связывание "value"

Назначение

Связывание value связывает значение ассоциированного DOM-элемента со свойством модели представления. Оно используется с такими элементами форм, как <input>, <select> и <textarea>.

Когда пользователь редактирует значение ассоциированного элемента формы, оно обновляет значение в представлении модели. Аналогично, при изменении значения в модели представлении, оно обновляет значение элемента формы на экране.

ПРИМЕЧАНИЕ

При работе с переключателями и радиокнопками используйте не связывание value, а связывание cheсked.

Пример

<p>Login name: <input data-bind="value: userName" /></p>
<p>Password: <input type="password" data-bind="value: userPassword" /></p>
 
<script type="text/javascript">
    var viewModel = 
    {
        userName: ko.observable(""),        // исходно пустое
        userPassword: ko.observable("abc"), // заполняем
    };
</script>

Параметры

Основной параметр:

Дополнительный параметр valueUpdate определяет, какое событие браузера КО должен использовать для отслеживания изменений. Следующие строковые значения используются наиболее часто:

Из всех этих возможностей "afterkeydown" – это лучший выбор, если нужно обновлять модель представления в реальном времени.

Например:

<p>
Your value: 
<input data-bind="value: someValue, valueUpdate: 'afterkeydown'" />
</p>
<p>You have typed: <span data-bind="text: someValue"></span>
</p> <!—обновление в реальном времени -->
 
<script type="text/javascript">
    var viewModel = 
    {
      someValue: ko.observable("edit me")
    };
</script>
ПРИМЕЧАНИЕ

Примечание 1: работа с выпадающими списками (т.е. узлами SELECT)

В Knockout есть поддержка выпадающих списков (i.e., SELECT nodes). Связывание value работает совместно со связыванием options, чтобы позволит вам читать и записывать значения, являющиеся произвольными JavaScript-объектами, а не строковыми значениями. Это очень полезно, если вы хотите позволить пользователям выбирать из набора объектов модели. Пример этого можно найти в разделе, посвященном связыванию option.

Аналогично, если вы хотите создать список с возможностью множественного выбора, см связывание selectedOption.

Примечание 2: обновление отслеживаемых и неотслеживаемых значений свойств

Если вы используете value для связи элемента с отслеживаемым свойством, КО может создать двунаправленное связывание, чтобы изменение любой из сторон отражалось на другой стороне.

Однако если вы используете value, чтобы связать элемент с неотслеживаемым свойством (т.е. обычной строкой или произвольным JavaScript-выражением), КО делает следующее:

При ссылке на простое свойство, т.е. на обычное свойство модели представления, КО установит исходное значение элемента формы в значение свойства, и при редактировании элемента формы запишет изменения обратно в свойство. КО не может определить, когда изменяется свойство (поскольку оно не является отслеживаемым), так что это однонаправленное связывание.

При ссылке на что-то, не являющееся обычным свойством, например, на сложное JavaScript-выражение или под-свойство, КО задаст исходному значению элемента формы это значение, но не сможет записать обратно изменения при редактировании элемента формы пользователем. В данном случае будет только единожды задано значение, реального связывания не будет.

Например:

<!-- двунаправленное связывание -->
<p>First value: <input data-bind="value: firstValue" /></p>
<!-- однонаправленное связывание -->
<p>Second value: <input data-bind="value: secondValue" /></p> 
<!-- нет связывания -->
<p>Third value: <input data-bind="value: secondValue.length" /></p>  
 
<script type="text/javascript">
    var viewModel = 
    {
        firstValue: ko.observable("hello"), // Отслеживаемое
        secondValue: "hello, again"         // Неотслеживаемое
    };
</script>

Зависимости

Только от библиотеки Knockout.

Связывание "checked"

Назначение

Связывание checked связывает помечаемый элемент формы, то есть переключатель (<input type='checkbox'>) или радиокнопку (<input type='radio'>), со свойством модели рпедставления.

Когда пользователь помечает ассоциированный элемент формы, связывание обновляет свойство модели представления. Аналогично, при изменении значения в модели представления, помечается (или наоборот) элемент экранной формы.

Пример с переключателем

<p>Send me spam: <input type="checkbox" data-bind="checked: wantsSpam" /></p>
 
<script type="text/javascript">
    var viewModel = 
    {
        wantsSpam: ko.observable(true) // Исходно помечен
    };
     
    // ... then later ...
    viewModel.wantsSpam(false); // Переключатель становится не помеченным
</script>

Пример добавления радиокнопки

<p>Send me spam: <input type="checkbox" data-bind="checked: wantsSpam" /></p>
<div data-bind="visible: wantsSpam">
  Preferred flavor of spam:
  <div>
    <input type="radio" name="flavorGroup" 
           value="cherry" data-bind="checked: spamFlavor" /> 
    Cherry
  </div>
  <div>
    <input type="radio" name="flavorGroup" 
              value="almond" data-bind="checked: spamFlavor" /> 
    Almond
  </div>
  <div>
    <input type="radio" name="flavorGroup" 
           value="msg" data-bind="checked: spamFlavor" /> 
    Monosodium Glutamate
  </div>
</div>
 
<script type="text/javascript">
    var viewModel = 
    {
        wantsSpam: ko.observable(true),
        spamFlavor: ko.observable("almond") // Исходно выбрана кнопка Almond 
    };
     
    // ... затем ...
    viewModel.spamFlavor("msg"); // Теперь выбрана кнопка Monosodium Glutamate
</script>

Параметры

Основной параметр

КО назначает элементу состояние, соответствующее значению параметре. Любое имеющееся состояние будет переписано. Способ интерпретации параметра зависит от типа связываемого элемента:

В случае переключателей КО помечает элемент при значении параметра true, и наоборот. Не-булевы значения свободно интерпретируются. Это значит, что ненулевые значения и не пустые строки интерпретируются как true, а ноль, null, undefined и пустые строки интерпретируются как false.

Когда пользователь изменяет состояние переключателя, КО соответственно изменяет значение свойства модели.

В случае радиокнопок КО помечает элемент в том и только в том случае, когда значение параметра равно атрибуту value узла радиокнопки. Таким образом, значение параметра должно быть строковым. В предыдущем примере радиокнопка с value="almond" была помечена только когда свойство модели представления spamFlavor было равно "almond".

Когда пользователь меняет выбранную радиокнопку, КО задает свойству модели значение атрибута value выбранной радиокнопки. В предыдущем примере нажатие на радиокнопку со значением value="cherry" установит viewModel.spamFlavor в "cherry".

Конечно, это наиболее полезно, когда у вас есть несколько радиокнопок, привязанных к одному свойству модели. Чтобы обеспечить, что в любое время будет выбрана только одна из радиокнопок, нужно присвоить их атрибутам name произвольное общее значение (например, в предыдущем примере это значение flavorGroup) – это действие помещает их в группу, в которой может быть выбран только один элемент.

Если параметр - отслеживаемое значение, связывание будет изменять состояние элемента при изменениях этого значения. Если параметр не ссылается на отслеживаемое значение, оно задаст состояние только один раз.

Дополнительные параметры отсутствуют

Зависимости

Только от библиотеки Knockout.

Связывание "options"

Назначение

Связывание options управляет тем, какие пункты появятся в выпадающем списке (т.е. элементе <select>) или списке с множественным выбором (т.е., <select size='6'>).

Значение должно быть массивом (или отслеживаемым массивом). Элемент <select> будет отображать по элементу списка для каждого элемента массива.

ПРИМЕЧАНИЕ

В случае списка с множественным выбором для задания выбранных элементов, или для чтения того, какие элементы выбраны, используйте связывание selectedOptions. В случае списка с одиночным выбором для чтения и записи выбранного элемента можно использовать связывание value.

Пример 1. Выпадающий список

<p>Destination country: <select data-bind="options: availableCountries"></select></p>
 
<script type="text/javascript">
  var viewModel = 
  {
    // Это исходные пункты списка
    availableCountries : ko.observableArray(['France', 'Germany', 'Spain']) 
    };
     
    // ... затем ...
    viewModel.availableCountries.push('China'); // добавляем еще один пункт
</script>

Пример 2. Список со множественным выбором

<p>Choose some countries you'd like to visit: <select data-bind="options: availableCountries" size="5" multiple="true"></select></p>
 
<script type="text/javascript">
    var viewModel = 
    {
      availableCountries : ko.observableArray(['France', 'Germany', 'Spain'])
    };
</script>

Пример 3. Выпадающий список, содержащий произвольные JavaScript-объекты, а не строки

<p>
    Your country: 
    <select data-bind="options: availableCountries, 
                       optionsText: 'countryName', 
                       value: selectedCountry, 
                       optionsCaption: 'Choose...'">
    </select>
</p>
 
<div data-bind="visible: selectedCountry"> <!—появляется если выбрать что-то-->
    You have chosen a country with population 
  <span data-bind="text: selectedCountry() ? 
    selectedCountry().countryPopulation : 'unknown'"></span>.
</div>
 
<script type="text/javascript">
    // Конструктор для объекта с двумя свойствами
Constructor for an object with two properties
    var country = function(name, population) 
    {
        this.countryName = name;
        this.countryPopulation = population;    
    };        
 
    var viewModel = 
    {
        availableCountries : ko.observableArray([
            new country("UK", 65000000),
            new country("USA", 320000000),
            new country("Sweden", 29000000)
        ]),
        selectedCountry : ko.observable() // по умолчанию ничего не выбрано
    };
</script>

Параметры

Основной параметр

Массив (или отслеживаемый массив). Для каждого элемента КО добавляет <option> в ассоциированный узел <select>. Имевшиеся ранее значения удаляются.

Если значение параметра – это массив строк, никакие другие параметры не нужны. Элемент <select> будет отображать по пункту списка для каждого строкового значения. Однако если вы хотите позволить пользователю выбирать объекты из массива произвольных JavaScript-объектов, потребуются параметры optionsText и optionsValue, описанные ниже.

Если значение параметра – это отслеживаемое значение, связывание будет изменять содержимое списка при изменении массива. В противном случае содержимое списка будет задано один раз, и не будет обновляться.

Дополнительные параметры

В некоторых случаях назначать пункт, выбранный по умолчанию, не нужно. Но в выпадающем списке с одиночным выбором всегда выбран один пункт. Как же избежать этого? Обычное решение – вставить в список пункт-заглушку типа “Select an item”, “Please choose an option”, и назначить его выбранным по умолчанию.

Это делается просто: добавьте дополнительный параметр optionsCaption, со значением, равным выводимой строке. Например:

<select data-bind='options: myOptions, 
  optionsCaption: "Select an item...", value: myChosenValue'></select>

После этого КО вставит в список пункт с текстом “Select an item…” и значением undefined. Таким образом, если myChosenValue имеет значение undefined (которое отслеживаемые значения имеют по умолчанию), будет выбран пункт-заглушка.

В примере 3 выше показано, как можно связать options с массивом произвольных JavaScript-объектов, а не с простыми строками. В этом случае нужно выбрать, какие из свойств объектов будут выведены как текст пунктов списка. В примере 3 показано, как указать имя свойства с помощью дополнительного параметра optionsText.

Аналогично optionsText, можно передать дополнительный параметр optionsValue, чтобы указать, какие из свойств объекта должен быть использован для задания атрибута value элементов <option>, генерируемых KO.

Обычно это применяется только чтобы обеспечить, что КО может корректно поддерживать выбор в случае обновления набора доступных опций. Например, если вы многократно получаете список объектов "автомобиль" с помощью вызовов Ajax, и хотите гарантировать, что выбранный автомобиль при этом сохранится, вам может потребоваться задать значение "carId" параметра optionsValue (или другой уникальный идентификатор объекта "автомобиль"), иначе КО не всегда будет знать, какой из старых объектов "автомобиль" соответствует новому.

В случае списка с множественным выбором вы можете читать и записывать выбранные значения с помощью selectedOptions. Технически это отдельное связывание, и о нем будет сказано в следующем разделе.

ПРИМЕЧАНИЕ

Примечание: выбор сохраняется при задании/изменении опций

Когда связывание options изменяет набор опций в элементе <select>, KO по возможности оставляет неизменным выбранный пользователем пункт списка. Так, в случае выпадающего списка с одиночным выбором, ранее выбранный пункт так и останется выбранным, а в случае списка с множественным выбором выбранными останутся все ранее выбранные пункты (если, конечно, вы не удалили один или несколько из них).

Это происходит потому, что связывание options пытается быть независимым от связывания value (управляющего выбором в случае списка с одиночным выбором) и связывания selectedOptions (управляющего выбором в случае списка с множественным выбором).

Зависимости

Только от библиотеки Knockout.

Связывание "selectedOptions"

Назначение

Связывание "selectedOptions" управляет тем, какие элементы списка с множественным выбором выбраны в данный момент. Его предполагается использовать совместно с элементом <select> и связыванием options.

Когда пользователь выбирает или снимает выделение с пункта в списке со множественным выбором, добавляется или удаляется соответствующее значение массива в модели представления. Аналогично, если предположить, что это отслеживаемый массив в модели представления, то когда вы добавляете или удаляете (через push или splice) элементы массива, соответствующие пункты в UI становятся выбранными (или наоборот). Это двунаправленное связывание.

ПРИМЕЧАНИЕ

Для управления выделением в выпадающем списке с одиночным выделением используйте связывание value.

Пример

<p>
    Choose some countries you'd like to visit: 
    <select data-bind="options: availableCountries, 
                       selectedOptions: chosenCountries" 
                       size="5" multiple="true">
    </select>
</p>
 
<script type="text/javascript">
    var viewModel = 
    {
      availableCountries : ko.observableArray(['France', 'Germany', 'Spain']),
      // Исходно выбрана только Германия
      chosenCountries : ko.observableArray(['Germany']) 
    };
     
    // ... затем ...
    viewModel.chosenCountries.push('France'); // Теперь выбрана еще и Франция
</script>

Параметры

Массив или отслеживаемый массив. КО обеспечивает соответствие выделенных пунктов списка содержимому массива. Любое имевшееся ранее состояние будет переписано.

Если значение параметра – отслеживаемый массив, связывание будет обновлять выбор в элементе при изменениях массива (через push, pop или другие методы отслеживаемого массива). Если значение параметра – не отслеживаемый массив, состояние выделения элемента будет установлено один раз, и больше изменяться не будет.

Является значение параметра отслеживаемым массивом или нет, КО определит, когда пользователь выбирает элементы списка с множественным выделением или снимает с них выделение, и будет обновлять массив соответственно.

ПРИМЕЧАНИЕ

Примечание: пользователь может использовать произвольные JavaScript-объекты

В примере кода, приведенном выше, пользователь может выбирать из массива строковых значение. Но вы не ограничены строками – массив options может содержать произвольные JavaScript-объекты. Подробно эта возможность описана в разделе, посвященном связыванию option.

В этом сценарии значения, которые читаются и записываются с помощью selectedOptions – это сами объекты, а не их текстовые представления. Это в большинстве случаев приводит к гораздо более чистому и элегантному коду.

Зависимости

Только от библиотеки Knockout.

Связывание "uniqueName"

Назначение

Связывание uniqueName гарантирует, что ассоциированный DOM-элемент имеет непустой атрибут name. Если у DOM-элемента нет такого атрибута, это связывание присваивает ему таковой и задает ему уникальное строковое значение.

Это связывание используется нечасто. Оно полезно в очень редких случаях, например:

Пример

<input data-bind="value: someModelProperty, uniqueName: true" />

Параметры

Основной параметр: чтобы использовать связывание uniqueName, передайте true (или какое-то значение, приравниваемое к true), как в предыдущем примере.

Зависимости

Только от библиотеки Knockout.

Заключение

Библиотека Knockout позволяет создавать богатые, быстродействующие пользовательские интерфейсы, как для отображения, так и для редактирования информации, с четкой нижележащей моделью данных. Везде, где вы используете динамически обновляемые разделы UI (то есть изменяющиеся в зависимости от действий пользователя или изменений во внешних источниках данных), Knockout может помочь реализовать их более просто.



Ваши предложения и комментарии мы ожидаем по адресу: mag@rsdn.ru
Copyright © 1994-2002 Оптим.ру