Внутренняя работа плагина unobtrusive jQuery validate в ASP.Net MVC

в 11:50, , рубрики: javascript, mvc, перевод, Песочница, метки: , ,

Это 3 часть серии «Понимание ненавязчивой валидации в ASP.Net MVC».

О чем мы поговорим в этой статье:

  • Метод «parse»
    • секция «parceElement»
      • объяснение параметра «skipAttach»
      • объяснение функции «parceElement»

    • секция «validateInfo»
      • объяснение функции «validateInfo»
      • объяснение объекта «return»

  • Адаптеры

Метод «parse»

Мы объясним цикл ненавязчивой валидации, который происходит, когда документ загружается, и поймем роль каждого компонента.
Если мы посмотрим в конец jquery.validate.unobtrusive.js мы найдем

$(function () {
  $jQval.unobtrusive.parse(document);
});

Итак, мы вызываем метод «parse()» и передаем ему «document»
Что представляет собой метод «parse()»?

{
parse: function (selector) {
///
<summary>
/// Parses all the HTML elements in the specified selector. It looks for input elements decorated
/// with the [data-val=true] attribute value and enables validation according to the data-val-*
/// attribute values.
/// </summary>
///Any valid jQuery selector.
 
$(selector).find(":input[data-val=true]").each(function () {
  $jQval.unobtrusive.parseElement(this, true);
});
 
var $forms = $(selector)
    .parents("form")
    .andSelf()
      .add($(selector).find("form"))
      .filter("form");
 
$forms.each(function () {
  var info = validationInfo(this);
  if (info) {
    info.attachValidation();
  }
});
}
}

У метода «parse()» есть две секции

1. Секция «parceElement»

parseElement(element, skipAttach)

$(selector).find(":input[data-val=true]").each(function () {
  $jQval.unobtrusive.parseElement(this, true);
});

Первое, что происходит, это мы проходим циклом по всем элементам содержащим «data-val=true» внутри селектора, который мы передаем (в нашем случае это «document»)
Затем вызываем «parseElement()» и передаем ему элемент, который мы хотим провалидировать и «true» для «skipAttach».

Пояснение параметра «skipAttach»

Может возникнуть вопрос, почему мы передаем true, а не false в «skipAttach».

«skipAttach» — это флаг, для вызова «validate()» на форме.
Если мы передаем «false», к элементу, который мы передаем, прикрепляются правила валидации и сразу же вызывается метод «validate» для массива правил и передаются дальше другие опции, необходимые для ненавязчивой валидации(Если есть какой-либо другой элемент, который они собираются парсить).
Мы не хотим этого. В первую очередь, мы хотим прикрепить правила к каждому элементу формы, и затем, когда все правила будут прикреплены, мы вызовем «validate()», основной во второй части метода «parse».

В каких случаях нам необходимо передать «true» в «skipAttach»?

Если мы хотим добавить динамический элемент в форму, которая уже была провалидирована, мы передадим «true», чтобы не проводить валидацию формы снова, так как не хотим чтобы что-то происходило(мы поговорим о динамической валидации элемента в следующей статье)

Объяснение функции «parseElement»

В основном функция «parseElement()» выполняет две вещи:

1. Когда первый раз вызывается элемент формы(ни один элемент не вызывался до этого в этой форме), функция конструирует объект опций, которые будут переданы методу «validate()». Опции используются jquery.unobtrusive как отдельная функция «errorPlacement», отдельный «errorClass» и как пустой массив правил.
Заметка: это все можно сделать в приватном методе «validationInfo(form)», который вызывается без «parseElement», и, когда он вызывается больше, чем 1 раз, он просто возвращает тот же объект. Таким образом, мы можем добавлять, изменять или вызывать в нем данные/функции.
2. Для каждого элемента, когда мы вызываем «parseElement», он понимает правила, которые записаны в этом элементе («the data-*»), используя адаптеры (дальше есть пункт с объяснением, как работают адаптеры), и затем переводит и добавляет их к массиву правил, который был создан при первом вызове.
Заметка: При каждом обращении к «parseElement», его результат будет сохранен в форме, используя метод $.data(“unobtrusiveValidation“). Таким образом, отдельные вызовы синхронизируются с одним и тем же источником данных.

2. Секция «validateInfo»

var $forms = $(selector)
  .parents("form")
  .andSelf()
  .add($(selector).find("form"))
  .filter("form");
 
$forms.each(function () {
  var info = validationInfo(this);
  if (info) {
    info.attachValidation();
  }
});
Объяснение функции «validateInfo»

Мы уже упоминали, что вызов функции «validateInfo()» конструирует объект опций метода «validate()». Опции используются jquery.unobtrusive как отдельная функция «errorPlacement», отдельный «errorClass» и пустой массив правил.
Тут мы вызываем также «validateInfo()» для каждой формы на странице. Обычно, вызывая в этом месте «valaidateInfo», мы только получаем объект, сохраненный в форме, который был уже заполнен в первой фазе. То есть мы используем ее как «getter».
Затем мы вызываем «attachValidation()», который вызывает метод «validate()», передавая ему опции, заполненные вызовом «validationInfo()».


data_validation = "unobtrusiveValidation";
 
function validationInfo(form) {
  var $form = $(form),
       result = $form.data(data_validation),
       onResetProxy = $.proxy(onReset, form);
 
  if (!result) {
    result = {
      options: {  // options structure passed to jQuery Validate's validate() method
        errorClass: "input-validation-error",
        errorElement: "span",
        errorPlacement: $.proxy(onError, form),
        invalidHandler: $.proxy(onErrors, form),
        messages: {},
        rules: {},
        success: $.proxy(onSuccess, form)
      },
      attachValidation: function () {
        $form
          .unbind("reset." + data_validation, onResetProxy)
          .bind("reset." + data_validation, onResetProxy)
          .validate(this.options);
        },
      validate: function () {  // a validation function that is called by unobtrusive Ajax
        $form.validate();
        return $form.valid();
      }
    };
    $form.data(data_validation, result);
  }
  return result;
}

Сперва мы проверяем, не вызывали ли мы уже эту функцию на форме раньше, используя $form.data(“unobtrusiveValidation“). Если уже вызывали, тогда мы ничего не делаем и возвращаем результат.

Объяснение объекта «return».

Если это первый вызов функции «validationInfo()», то мы создаем объект результата и сохраняем его на форме используя метод $.data(). Этот объект содержит 3 части:

  • Объект, который мы будем передавать методу «validate()» с пустым массивом правил и сообщений, которые мы будем конструировать позже.
  • Метод «attachValidation()», который привяжет собственное событие “reset.unobtrusiveValidation” к форме, и, после вызова «validate» на методе со всеми опциями, этот метод будет вызван, когда массив правил и сообщений будет заполнен (вызвав собственное событие «reset», мы будем вызывать метод «onReset()», который обнуляет все).
    function onReset(event) {  // 'this' is the form element
      var $form = $(this);
     
      $form.data("validator").resetForm();
      $form.find(".control-group").removeClass("error");
      $form.find(".validation-summary-errors")
        .addClass("validation-summary-valid")
        .removeClass("validation-summary-errors");
      $form.find(".field-validation-error")
        .addClass("field-validation-valid")
        .removeClass("field-validation-error")
        .removeData("unobtrusiveContainer")
          .find(">*")  // If we were using valmsg-replace, get the underlying error
          .removeData("unobtrusiveContainer");
    

    Итак, если мы хотим вызвать событие «reset», чтобы обнулить валидацию формы:
    $('form').trigger('reset.unobtrusiveValidation')

  • Отдельный метод «validate()» будет вызываться из ненавязчивого Ajax

Адаптеры

Я специально пропустил тему адаптеров, когда говорил о методе «parseElement()», потому что они достаточно сложные, чтобы выделить для них подпункт.
Мы посмотрели как генерируется Html с помощью ненавязчивой валидации, и как добавлять отдельные атрибуты валидации в нормальной jquery.validate. Чтобы связать эти две части используются адаптеры.
Итак, за что отвечают адаптеры?
Адаптеры отвечают за то, чтобы перевести Html «data-*» в формат, понятный обычному плагину jquery.validate. Если пользователь хочет добавить отдельный метод валидации с помощью ненавязчивой валидации, он должен также создать адаптер для нее. Коллекция адаптеров находится в $.validator.unobtrusive.adapters. Коллекция адаптеров состоит из всех адаптеров по умолчанию, которые определены в jquery.unobstrusive, и тех, которые определены пользователем.
Она также содержит 4 метода для добавления собственного адаптера, на которые мы посмотрим позже.

Самый основной метод:
jQuery.validator.unobtrusive.adapters.add(adapterName, [params], fn)

Мы можем считать этот метод методом $.ajax, три другие метода вспомогательные, используют этот метод.

Давайте разберем параметры:

  • adapterName: Это название адаптера, оно совпадает с «ruleName» в Html элементе(data-val-ruleName)
  • [params]: необязательный массив параметров, который метод валидации будет использовать, чтобы закончить валидацию.
  • fn: вызывается, чтобы соединить Html «data-*» с правилами и сообщениями, которые используются методом «validate()». У него есть опция параметров, которые передаются ему, как объект со следующими свойствами:
    • element: Html элемент, который валидируется;
    • form: элемент формы;
    • message: сообщение об ошибке для этого правила, извлеченное из «data-*» атрибутов этого элемента;
    • params: параметры, которые используются для валидации и их массив, извлеченный из атрибутов «data-*» Html элемента (data-val-ruleName-param1);
    • rules: массив правил jQuery для этого элемента. Чтобы добавить к этому массиву правило(-ла), мы передадим пары ключ/значение, ключ — имя правила валидации, значение — параметр, используемый для этого правила (как написано в первой статье цикла в пункте «Добавление собственных правил валидации»)
    • messages: массив сообщений jquery для этого элемента, также как и с объектом правил, мы заполняем его и он используется как объект сообщений для этого Html элемента в методе «validate».
      Заметка: этот метод не возвращает никакого результата. Массив правил и сообщений будет сохранен на самой форме с помощью $.data(“unobtrusiveValidation“). Мы можем проверить метод «parseElement», чтобы посмотреть детально, как параметры были переданы функции адаптера.

Пример:
Html

<input id="val" type="text" name="val" data-val="true" data-val-between="Must be in the right range" data-val-between-min="5" data-val-between-max="30" />

JavaScript

//The adapter
jQuery.validator.unobtrusive.adapters.add(
  'between', ['min' ,'max'], function (options) {
    options.rules['between'] =  {
      min: options.params.min,
      max: options.params.max
    };
    options.messages['between'] = options.message;
  }
);
 
//The validation method
jQuery.validator.addMethod("between", function (value, element, params) {
  params.min == 5; //true
  params.max == 30; //true
});

Что же насчет других адаптеров:

addBool
addSingleVal
addMinMax

Они достаточно простые, если вы поймете концепцию добавления собственного адаптера используя метод «add()», объясненный ранее.
Также вы можете посмотреть статью Brad Wilson «Unobtrusive Client Validation in ASP.NET MVC 3», где он объясняет тему адаптеров более подробно.

Перевод статьи Nadeem Khedr How the unobtrusive jQuery validate plugin works internally in Asp.net MVC

Автор: Juty

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js