Knockout: Переключаем проверку ввода на русский язык или Knockout.Validation Localize (Globalize)
Сайтостроение | создано: 04.03.2013 | опубликовано: 04.03.2013 | обновлено: 13.01.2024 | просмотров: 11392
Если вы используете 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 “по-русски”)
То что и требовалось!