29.12.2017

Записываемые Computed переменные — Knockout.js


Новички возможно захотят пропустить этот раздел — записываемые computed переменные являются достаточно продвинутой техникой и не нужны в большинстве ситуаций

Обычно, computed переменные содержат значение, которое вычисляется из других observable переменных и следовательно, является досупным только для чтения. Что может удивить, так это то, что существует возможность сделать эти computed переменные записываемыми. Вам нужно всего лишь предоставить собственную callback-функцию , которая делает что-то разумное с записываемыми значениями.

Вы можете использовать записываемые computed переменные таким же способом, как и обычные observable переменные, со своей собственной логикой, перехватывающей все чтения и записи. Как и observable переменные, вы можете вписывать значения во множество observable или computed свойств на объекте модели, используя цепочки вызовов. Например, myViewModel.fullName(‘Итан Маркотт’).age(50).

Записываемые computed переменные это мощный инструмент с широким спектром применения.

Пример 1: Разбор данных пользователя

Возвращаемся к классическому примеру “имя + фамилия = полное имя”, вы можете развернуть порядок работы алгоритма с конца на начало: создать записываемые computed переменные fullName, так, что пользователь может напрямую редактировать полное имя, и введенные им значения будут разобраны и присвоены соответствующим исходным observable переменным firstName и lastName. В этом примере, callback-функция для write отслеживает ввод значения, разбивая текст на составляющие — “firstName” и “lastName”, и записывая эти значения в исходные observable-переменные.

See the Pen Knockout Пример 1: Разбор данных пользователя (writable observable) by Kirill (@skv1991) on CodePen.

Исходный код: Представление

<div>Имя: <span data-bind="text: firstName"></span></div>
<div>Фамилия: <span data-bind="text: lastName"></span></div>
<div class="heading">Привет, <input data-bind="textInput: fullName"/></div>

Исходный код: Модель представления

function MyViewModel() {
    this.firstName = ko.observable('Вася');
     this.lastName = ko.observable('Пупкин');
 
     this.fullName = ko.pureComputed({
       read: function () {
           return this.firstName() + " " + this.lastName();
       },
       write: function (value) {
            var lastSpacePos = value.lastIndexOf(" ");
            if (lastSpacePos > 0) { // Игнорируем значения без символа пробел
                this.firstName(value.substring(0, lastSpacePos)); // Обновляем "firstName"
                this.lastName(value.substring(lastSpacePos + 1)); // Обновляем "lastName"
            }
        },
        owner: this
    });
}
ko.applyBindings(new MyViewModel());

Этот пример является полной противоположностью Hello World, в нашем примере имя и фамилия напрямую рне редактируются, но комбинированное полное имя можно редактировать.

Кед предыдущей модели представления демонстрирует синтаксис с одним параметром для инициализации computed переменной. Смотри справку по computed переменным для полного списка доступных опций.

Пример 2: Выбор/отмена выбора для всех элементов

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

See the Pen Knockout Пример 2: Пример 2: Выбор/отмена выбора для всех элементов (writable observable) by Kirill (@skv1991) on CodePen.

Исходный код: Представление

<div class="heading">
    <input type="checkbox" data-bind="checked: selectedAllProduce" title="Выбрать все/ни одной"/> Производство
</div>
<div data-bind="foreach: produce">
    <label>
        <input type="checkbox" data-bind="checkedValue: $data, checked: $parent.selectedProduce"/>
        <span data-bind="text: $data"></span>
    </label>
</div>

Исходный код: Модель представления

function MyViewModel() {
    this.produce = [ 'Яблоки', 'Бананы', 'Сельдерей', 'Кукуруза', 'Апельсины', 'Шпинат' ];
    this.selectedProduce = ko.observableArray([ 'Кукуруза', 'Апельсины' ]);
    this.selectedAllProduce = ko.pureComputed({
        read: function () {
            // Сравнение длинны массивов это быстрый и аккуратный способ, когда только элементы из
            // основного массива были добавлены в массив выбранных продуктов.
            return this.selectedProduce().length === this.produce.length;
        },
        write: function (value) {
            this.selectedProduce(value ? this.produce.slice(0) : []);
        },
        owner: this
    });
}
ko.applyBindings(new MyViewModel());

Пример 3: Конвертер значений

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

See the Pen Knockout Пример 3: Конвертер значений (writable observable) by Kirill (@skv1991) on CodePen.

Исходный код: Представление

<div>Укажите ставку: <input data-bind="value: formattedPrice"/></div>
<div>(Необработанное значнение: <span data-bind="text: price"></span>)</div>

Исходный код: Модель представления

function MyViewModel() {
    this.price = ko.observable(25.99);

    this.formattedPrice = ko.pureComputed({
        read: function () {
            return '$' + this.price().toFixed(2);
        },
        write: function (value) {
            // Отрезаем нежелательные символы, парсим как float, затем записываем 
            // необработанные данные назад в исходную observable переменную "price"
            value = parseFloat(value.replace(/[^\.\d]/g, ""));
            this.price(isNaN(value) ? 0 : value); // Записываем в исходное хранилище
        },
        owner: this
    });
}

ko.applyBindings(new MyViewModel());

Теперь, когда пользователь вводит новое значение, текстовое поле обновляется, показывая форматированное значение с символом валюты и двумя знаками после запятой, вне зависимости от того, в каком формате пользователь ввел значение. Это дает пользователю хороший опыт по взаимодействию с интерфейсом, потому что пользователь видит как программа понимает введенные им данные в качестве цены. Он знает, что не может ввести более двух знаков после запятой, потому что, если он укажет большее число знаков, то лишние будут удалены. Аналогично, он не может указать отрицательные значения, потому что callback-функция метода write вырежет любой символ минуса.

Пример 4: Фильтрация и валидация введенных данных

В примере 1 было показано, как записываемые computed переменные могут эффективно производить фильтрацию входных данных, выбирая, какие значения не записывать в исходную observable-переменную, если значение не удовлетворяет конкретным условиям. Игнорирует значение полного имени, написанное без пробела.

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

See the Pen Knockout Пример 4: Фильтрация и валидация введенных данных (writable observable) by Kirill (@skv1991) on CodePen.

Исходный код: Представление

<div>Введите цифровое значение: <input data-bind="textInput: attemptedValue"/></div>
<div class="error" data-bind="visible: !lastInputWasValid()">Это не число!</div>
<div>(Принимаемые значения: <span data-bind="text: acceptedNumericValue"></span>)</div>

Исходный код: Модель представления

function MyViewModel() {
    this.acceptedNumericValue = ko.observable(123);
    this.lastInputWasValid = ko.observable(true);

    this.attemptedValue = ko.pureComputed({
        read: this.acceptedNumericValue,
        write: function (value) {
            if (isNaN(value))
                this.lastInputWasValid(false);
            else {
                this.lastInputWasValid(true);
                this.acceptedNumericValue(value); // Записываем в исходное хранилище
            }
        },
        owner: this
    });
}

ko.applyBindings(new MyViewModel());

Теперь, acceptedNumericValue будет содержать только числа, и любые другие значения будут запускать отображение сообщения валидации вместо обновления значения acceptedNumericValue.

Примечание: Для таких тривиальных задач, как проверка вводимых значений на число, этот подход слишком избыточен. Будет намного проще использовать плагин jQuery Validation и его класс number на элементах <input>. Knockout и jQuery Validation отлично работают вместе, что показано в примере редактора таблицы. Однако, предыдущий пример показывает более общий механизм для фильтрации и валидации со своей собственной логикой для контроля того, какой тип взаимодействия с пользователем должен появится на экране, что вполне применимо, если ваша задача более сложная, нежели нативная обработка вводимых значений через jQuery Validation.

 


Эксклюзивно для сайта skv1991.ru

Перепечатка и копирование строго запрещены.

Оригинал страницы

Опубликовано: 29/12/2017

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *