Knockout: Переключаем проверку ввода на русский язык или Knockout.Validation Localize (Globalize)
Сайтостроение | создано: 04.03.2013 | опубликовано: 04.03.2013 | обновлено: 13.01.2024 | просмотров: 11009
Если вы используете Knockoutjs, то наверное уже не раз приходилось делать проверку данных, которые вводит пользователь. А как вы проверяли ввод даты и дробных чисел? В этот статье настроим валидацию Knockout.Validations на работу "по-русски".
Подготовим проект к экспериментам
Создадим новый проект для теста. Обновим установленные пакеты, и установим пару-тройку новых. Первый пакет будет jssite, он должен быть вам уже знаком. После него установим еще один пакет kolite, о котором чуть позже. Установим Knockout.Validations (надо его тоже включить в набор скриптов пакета jssite). А Knockoutjs уже установился вместе с пакетом jssite.
А еще я удалил сразу же файл BundleConfig.cs и всё что с ним связано. Причины можно почитать в другой статье.
Так как я использовал для создания нового проекта MvcApplication шаблон Basic (появляется после установки ASP.NET and Web Tools 2012.2 Released), то мой шаблон выглядит так _Layout.cshtml:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title> <link href="~/Content/all.min.css" rel="stylesheet" /> <script src="~/Scripts/modernizr-2.6.2.min.js"></script> </head> <body> @RenderBody() <script src="~/Scripts/jquery-1.9.1.min.js"></script> <script src="~/Scripts/amplify.min.js"></script> <script src="~/Scripts/underscore.min.js"></script> <script src="~/Scripts/moment.min.js"></script> <script src="~/Scripts/toastr.min.js"></script> <script src="~/Scripts/knockout-2.2.1.js"></script> <script src="~/Scripts/knockout.validation.js"></script> <script src="~/Scripts/knockout.mapping-latest.js"></script> <script src="~/Scripts/knockout.dirtyFlag.js"></script> <script src="~/Scripts/knockout.command.js"></script> <script src="~/Scripts/knockout.activity.js"></script> <script src="~/Scripts/app/site.bindingHandlers.js"></script> <script src="~/Scripts/app/site.core.js"></script> <script src="~/Scripts/app/site.services.js"></script> <script src="~/Scripts/app/site.controls.js"></script> @RenderSection("scripts", required: false) </body> </html>
Контролер
По умолчанию в моем шаблоне нет контролера, хотя в маршрутах (RouteConfog.cs) прописан “Home”, его-то я и создал. А также создал представление (View) для одного единственного метода этого контролера. Пока оно вот такое:
@{ ViewBag.Title = "Index"; } <h2>Index</h2>
JavaScript ViewModel’ы
Теперь создаем сущность для теста (person), это тот объект, который мы будем проверять на правильность ввода на форме:
function Person(last, first, second, birth, weight) { return { lastName: ko.observable(last), firstName: ko.observable(first), secondName: ko.observable(second), birthDate: ko.observable(birth), weight: ko.observable(weight) }; }
Теперь создаем ViewModel для страницы Index.cshtml:
$(function (parameters) { "use strict"; site.vm.viewModel = function () { var data = [ new Person("Суходрищев", "Дормидонт", "Евлампиевич", "03/03/1983", "92.3"), new Person("Тихобздеев", "Гавриил", "Афанасьевич", "13.03.1976", "90,8") ], meta = new site.controls.Metadata( "Тестирование валидации Knockout", "Проверяем как работает валидация Knockout, в том числе локализацию Knockout на русский язык: цифры, даты и т.д.", "http://www.calabonga.net"), clock = new site.controls.Clock(), person = data[0], person2 = data[1], errors = ko.validatedObservable(person), errors2 = ko.validatedObservable(person2), saveCommand = ko.asyncCommand({ execute: function (complete) { alert("Saved!"); complete(); }, canExecute: function (isExecuting) { return !isExecuting && errors.isValid; } }), saveCommand2 = ko.asyncCommand({ execute: function (complete) { alert("Saved 2!"); complete(); }, canExecute: function (isExecuting) { return !isExecuting && errors2.isValid; } }); return { meta: meta, clock: clock, person: person, person2: person2, saveCommand: saveCommand, saveCommand2: saveCommand2 }; }(); ko.applyBindings(site.vm.viewModel); });
Пояснения…
Строка 3: включаем “строгость”.
Строки 7-10: создаем два объекта Person, один валидный для EN локализации, другой валиден для RU валидации (смотреть надо на дату и вес).
Строки 12-16: метаданные для главной странице, кстати, класс метаданных “переехал” в jssite в namespace “controls”. В строке 17: создаем, как уже повелось, объект Clock. Если при первом старте часы пойдут – скрипты установлены правильно!
Строки 17-18: создаем две переменных для хранение на форме двух Person.
Строки 22-30 и строки 31-39: создаем asyncCommand (как раз из того самого пакета kolite) для более полной реализации паттерна MVVM на форме.
Строки 41-48: Выставляем публичные свойства.
Строка 51: Привязываем данные к форме.
Форма (представление)
Форма больше не будет меняться, поэтому приведу ее целиком:
@{ ViewBag.Title = "Index"; } <h2>Index</h2> <div data-bind="text: clock.time" id="clock"></div> <h2 data-bind="text: meta.title"></h2> <p data-bind="text: meta.description"></p> <table> <thead> <tr> <th style="width:50%">Globalization EN</th> <th style="width:50%">Globalization RU</th> </tr> </thead> <tbody> <tr> <td data-bind="css: { 'valid': person.isValid(), 'invalid': !person.isValid() }"> <p><span data-bind="text: person.isValid()"></span></p> <div data-bind="with: person"> <div class="editor-label"> <label>Фамилия:</label> </div> <div class="editor-field"> <input type="text" data-bind="value: lastName" /> </div> <div class="editor-label"> <label>Имя:</label> </div> <div class="editor-field"> <input type="text" data-bind="value: firstName" /> </div> <div class="editor-label"> <label>Отчество:</label> </div> <div class="editor-field"> <input type="text" data-bind="value: secondName" /> </div> <div class="editor-label"> <label>Дата рождения:</label> </div> <div class="editor-field"> <input type="text" data-bind="value: birthDate" /> </div> <div class="editor-label"> <label>Вес:</label> </div> <div class="editor-field"> <input type="text" data-bind="value: weight" /> </div> </div> <p> <button data-bind="command: saveCommand, text: 'сохранить'"></button> </p> </td> <td data-bind="css: { 'valid': person2.isValid(), 'invalid': !person2.isValid() }"> <p><span data-bind="text: person2.isValid()"></span></p> <div data-bind="with: person2"> <div class="editor-label"> <label>Фамилия:</label> </div> <div class="editor-field"> <input type="text" data-bind="value: lastName" /> </div> <div class="editor-label"> <label>Имя:</label> </div> <div class="editor-field"> <input type="text" data-bind="value: firstName" /> </div> <div class="editor-label"> <label>Отчество:</label> </div> <div class="editor-field"> <input type="text" data-bind="value: secondName" /> </div> <div class="editor-label"> <label>Дата рождения:</label> </div> <div class="editor-field"> <input type="text" data-bind="value: birthDate" /> </div> <div class="editor-label"> <label>Вес:</label> </div> <div class="editor-field"> <input type="text" data-bind="value: weight" /> </div> </div> <p> <button data-bind="command: saveCommand2, text: 'сохранить'"></button> </p> </td> </tr> </tbody> </table> @section scripts { <script src="~/Scripts/app/site.vm.viewModel.js"></script> }
А вот как она выглядит:
(рис 1. Перед установкой локализации)
Время проверять
Но для начала руссифицируем валидацию у knockout. Для этого находим на сайте Knockout-Validation папку с названием Localization, там уже есть ru-RU.js, вот его-то и скачаем чтобы добавить в проект. Таким образом, мы включаем перевод на русский язык валидацию примитивных типов.
Теперь надо подключить локализацию, я это сделаю в файле site.core.js, там у меня находится bootstrapper и всё остальное. Вот измененный файл:
// base namespace var site = site || {}; // config module site.cfg = site.cfg || {}; // model's module site.m = site.m || {}; // viewmodel's module site.vm = site.vm || {}; // services module site.services = site.services || {}; // utilites module site.utils = site.utils || {}; // controls module site.controls = site.controls || {}; // start engine var bootstrapper = function () { var root = this, initLibs = function () { // initialization for third-party libs site.amplify = root.amplify; site.$ = root.jQuery; site.logger = root.toastr; site._ = root._; }, initValidation = function () { ko.validation.configure({ registerExtenders: true, //default is true messagesOnModified: true, //default is true insertMessages: true, //default is true parseInputAttributes: true, //default is false writeInputAttributes: true, //default is false messageTemplate: null, //default is null decorateElement: true //default is false. Applies the validationElement CSS class }); ko.validation.localize({ required: 'Необходимо заполнить это поле.', min: 'Значение должно быть больше или равно {0}.', max: 'Значение должно быть меньше или равно {0}.', minLength: 'Длина поля должна быть не меньше {0} символов.', maxLength: 'Длина поля должна быть не больше {0} символов.', pattern: 'Пожалуйста проверьте это поле.', step: 'Значение поле должно изменяться с шагом {0}', email: 'Введите в поле правильный адрес email', date: 'Пожалуйста введите правильную дату', dateISO: 'Пожалуйста введите правильную дату в формате ISO', number: 'Поле должно содержать число', digit: 'Поле должно содержать цифры', phoneUS: 'Поле должно содержать правильный номер телефона', equal: 'Значения должны быть равны', notEqual: 'Пожалуйста выберите другое значение.', unique: 'Значение должно быть уникальным.' }); }, initConfig = function () { // settings for site site.cfg.throttle = 600; site.cfg.busyIndicatorImageName = "/images/ms-loader.gif"; // pager site.cfg.pageSize = 10; site.cfg.groupSize = 10; site.cfg.pageSizes = ko.observableArray([5, 10, 20, 30, 50, 100]); }, init = function () { initLibs(); initConfig(); initValidation(); }; return { run: init }; }();
Строка 34-61: Настройка и локализация knockout.Validation, которую вызываем в методе инициализации в строке 77.
Запустив проект, я всё также вижу ту же картинку – EN – зеленый, RU – красный. Значит валидация еще не настроена.
Ключевой момент Localization или Globalize.culture
К счастью, или к сожалению, но всё уже давно придумали за нас. Уже существует сборка (если так можно выразиться для js-файла), которая реализует весь требуемый для нас функционал - globalize. На сайте хорошо расписано для чего предназначена сборка. Подходит она и для нашего случая. Качаем… добавляем в проект:
(рис. 2. Подключение глобализации)
Не забудьте прописать скрипты в шаблоне _Layout.cshtml, чтобы они загружались на всех страницах приложения, или там, где вам будет угодно. Я добавил в шаблон, для простоты.
Теперь в файле site.core.js подключаем “глобализацию”. В методе InitLibs (см. строки 26-33 предыдущего листинга) придется дописать некоторое количество строк:
initLibs = function () { //globalize Globalize.culture("ru"); // подменяем парсинг чисел с плавающей точкой // потому что в Globalize.parseFloat существует бага, // так как "точка" (.) обрабатывается как часть числа // даже если она таковой не является. // Я исправил это так: Globalize.orgParaseFloat = Globalize.parseFloat; Globalize.parseFloat = function (value) { value = String(value); var culture = this.findClosestCulture(); var seperatorFound = false; for (var i in culture.numberFormat) { if (culture.numberFormat[i] == ".") { seperatorFound = true; break; } } if (!seperatorFound) { value = value.replace(".", "NaN"); } return this.orgParaseFloat(value); }; // устанавливаем собственные валидаторы // knockout для "number" и для "data" ko.validation.rules.number.validator = function (value, validate) { return !(value) || (validate && !isNaN(Globalize.parseFloat(value))); }; ko.validation.rules.date.validator = function (value, validate) { return !(value) || (validate && Globalize.parseDate(value) != null); }; // initialization for third-party libs site.amplify = root.amplify; site.$ = root.jQuery; site.logger = root.toastr; site._ = root._; }
Строка 4: подключает локализацию к javascript.
Обратите внимание на то, что в Globalize.parseFloat существует некоторая оказия, дело в том, что “точка” (.) принимается библиотекой как часть числа, даже если она не является его частью. Строки 12-14 исправляют эту оплошность.
Так же, надо не только подключить Globalize, исправив багу, но еще и немного переопределить правила проверки валидности у самого knockoutjs. И здесь уже придется написать код самостоятельно. Я написал проверку для DateTime и Number:
ko.validation.rules.number.validator = function (value, validate) { return !(value) || (validate && !isNaN(Globalize.parseFloat(value))); }; ko.validation.rules.date.validator = function (value, validate) { return !(value) || (validate && Globalize.parseDate(value) != null); };
Других правил мне пока не требуется… Запускаем! УРА!!!
(рис. 3. Knockout.Validation “по-русски”)
То что и требовалось!