AngularJS - проверка на стороне сервера и формы на стороне клиента

Я пытаюсь понять, как сделать следующее:

Каков приемлемый способ объявления формы. Я понимаю, что вы просто объявляете форму в HTML и добавляете такие директивы ng-model:

ng-model="item.name"

Что отправить на сервер. Я могу просто отправить объект объекта на сервер как JSON и интерпретировать его. Затем я могу выполнить проверку на объекте. Если это не удается, я бросаю ошибку JSON и отправляю обратно, что именно? Есть ли принятый способ сделать это? Как я могу с высокой точностью выталкивать ошибки проверки с сервера на клиент?

Мне действительно нужен пример, но документы Angulars довольно трудно понять.

Изменить: Кажется, я неправильно сформулировал свой вопрос.

Я знаю, как проверять клиентскую сторону и как обрабатывать ошибки/успех, как обещания. То, что я хочу знать, является принятым способом связывания сообщений об ошибках со стороны сервера SERVER. Скажем, у меня есть имя пользователя и пароль. Я не хочу опросить сервер для имен пользователей, а затем использовать Angular для определения дубликата. Я хочу отправить имя пользователя на сервер, не проверять, нет ли другой учетной записи с тем же именем, а затем отправить форму. Если возникает ошибка, как мне отправить его обратно?

Как насчет нажатия на данные на сервере как есть (ключи и значения) с полем ошибок, добавленным так:

{
  ...data...

  "errors": [
    {
      "context": null,
      "message": "A detailed error message.",
      "exceptionName": null
    }
  ]
}

Затем привязка к DOM.

Ответ 1

В последнее время я тоже общался с этим, и я вывел эту демонстрацию. Я думаю, что он делает то, что вам нужно.

Настройте свою форму в соответствии с нормами с любыми проверками на стороне клиента, которые вы хотите использовать:

<div ng-controller="MyCtrl">
    <form name="myForm" onsubmit="return false;">
        <div>
            <input type="text" placeholder="First name" name="firstName" ng-model="firstName" required="true" />
            <span ng-show="myForm.firstName.$dirty && myForm.firstName.$error.required">You must enter a value here</span>
            <span ng-show="myForm.firstName.$error.serverMessage">{{myForm.firstName.$error.serverMessage}}</span>
        </div>
        <div>
            <input type="text" placeholder="Last name" name="lastName" ng-model="lastName"/>
            <span ng-show="myForm.lastName.$error.serverMessage">{{myForm.lastName.$error.serverMessage}}</span>
        </div>
        <button ng-click="submit()">Submit</button>
    </form>
</div>

Отметьте также, что я добавил serverMessage для каждого поля:

<span ng-show="myForm.firstName.$error.serverMessage">{{myForm.firstName.$error.serverMessage}}</span>

Это настраиваемое сообщение, возвращаемое с сервера, и оно работает так же, как и любое другое сообщение об ошибке (насколько я могу судить).

Вот контроллер:

function MyCtrl($scope, $parse) {
      var pretendThisIsOnTheServerAndCalledViaAjax = function(){
          var fieldState = {firstName: 'VALID', lastName: 'VALID'};
          var allowedNames = ['Bob', 'Jill', 'Murray', 'Sally'];

          if (allowedNames.indexOf($scope.firstName) == -1) fieldState.firstName = 'Allowed values are: ' + allowedNames.join(',');
          if ($scope.lastName == $scope.firstName) fieldState.lastName = 'Your last name must be different from your first name';

          return fieldState;
      };
      $scope.submit = function(){
          var serverResponse = pretendThisIsOnTheServerAndCalledViaAjax();

          for (var fieldName in serverResponse) {
              var message = serverResponse[fieldName];
              var serverMessage = $parse('myForm.'+fieldName+'.$error.serverMessage');

              if (message == 'VALID') {
                  $scope.myForm.$setValidity(fieldName, true, $scope.myForm);
                  serverMessage.assign($scope, undefined);
              }
              else {
                  $scope.myForm.$setValidity(fieldName, false, $scope.myForm);
                  serverMessage.assign($scope, serverResponse[fieldName]);
              }
          }
      };
}

Я притворяюсь, что вы вызываете сервер в pretendThisIsOnTheServerAndCalledViaAjax, вы можете заменить его на вызов ajax, точка - это просто возвращает состояние проверки для каждого поля. В этом простом случае я использую значение VALID, чтобы указать, что поле действительно, любое другое значение рассматривается как сообщение об ошибке. Вам может понадобиться нечто более сложное!

После того, как у вас есть состояние проверки с сервера, вам просто нужно обновить состояние в вашей форме.

Вы можете получить доступ к форме из области видимости, в этом случае форма называется myForm, поэтому $scope.myForm получает форму. (Источник для контроллера формы здесь, если вы хотите прочитать, как он работает).

Затем вы хотите сообщить форме, является ли поле действительным/недействительным:

$scope.myForm.$setValidity(fieldName, true, $scope.myForm);

или

$scope.myForm.$setValidity(fieldName, false, $scope.myForm);

Нам также необходимо установить сообщение об ошибке. Прежде всего, получите доступ к аксессуру для поля, используя $parse. Затем назначьте значение с сервера.

var serverMessage = $parse('myForm.'+fieldName+'.$error.serverMessage');
serverMessage.assign($scope, serverResponse[fieldName]);

Надеюсь, что поможет

Ответ 2

У меня есть аналогичное решение, такое как Derek, описанное в блоге codetunes. TL; ДР:

  • отображает ошибку аналогично решению Дерека:

    <span ng-show="myForm.fieldName.$error.server">{{errors.fieldName}}</span>
    

  • добавить директиву, которая очистит ошибку, когда пользователь изменит ввод:

    <input type="text" name="fieldName" ng-model="model.fieldName" server-error />
    
    angular.module('app').directive 'serverError', ->
      {
        restrict: 'A'
        require: '?ngModel'
        link: (scope, element, attrs, ctrl) ->
          element.on 'change', ->
            scope.$apply ->
              ctrl.$setValidity('server', true)
      }
    
  • Обработать ошибку, передав сообщение об ошибке в область действия и сообщив, что форма имеет ошибку:

    errorCallback = (result) ->
      # server will return something like:
      # { errors: { name: ["Must be unique"] } }
      angular.forEach result.data.errors, (errors, field) ->
        # tell the form that field is invalid
        $scope.form[field].$setValidity('server', false)
        # keep the error messages from the server
        $scope.errors[field] = errors.join(', ') 
    

Надеюсь, это было бы полезно:)

Ответ 3

Ну, ответ, который дал Дерек Экинс, очень приятно работать. Но: Если вы отключите кнопку отправки с помощью ng-disabled="myForm.$invalid" - кнопка не будет автоматически возвращаться к включенному, поскольку состояние ошибки на сервере, похоже, не изменяется. Даже если вы снова редактируете ВСЕ поля в форме, чтобы соответствовать действительным входам (на основе проверки на стороне клиента).

Ответ 4

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

<form ng-submit="onSubmit(); return false">

И затем в вашей функции $scope.onSubmit():

$scope.onSubmit = function() {
  var data = {
    'name' : $scope.item.name
  };
  $http.post(url, data)
    .success(function() {
    })
    .failure(function() {

    });
};

Вы также можете проверить данные, настроив необходимые атрибуты.

Ответ 5

Если вы выберете ngResource, это будет выглядеть как

var Item = $resource('/items/');
$scope.item = new Item();
$scope.submit = function(){
  $scope.item.$save(
    function(data) {
        //Yahooooo :)
    }, function(response) {
        //oh noooo :(
        //I'm not sure, but your custom json Response should be stick in response.data, just inspect the response object 
    }
  );
};

Самое главное, что ваш код HTTP-ответа должен быть 4xx, чтобы ввести обратный вызов сбоя.

Ответ 6

По состоянию на июль 2014 года AngularJS 1.3 добавила новые функции проверки формы. Это включает в себя ngMessages и asyncValidators, чтобы вы могли теперь выполнить проверку на стороне сервера на поле перед отправкой формы.

Angular 1.3 Учебное пособие по проверке формы:

Литература:

Ответ 7

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

https://github.com/webadvanced/ng-remote-validate

Особенности:

  • Падение в решении для проверки Ajax любого ввода текста или пароля

  • Работы с Угловыми строками в валидации и кэше доступны в формеName.inputName. $error.ngRemoteValidate

  • Отправляет запросы сервера (по умолчанию 400 мс) и может быть установлен с помощью ng-remote-throttle="550"

  • Разрешает определение метода HTTP (по умолчанию POST) с помощью ng-remote-method="GET" Пример использования формы пароля для изменения, в которой пользователь должен ввести свой текущий пароль, а также новый пароль.:

    Сменить пароль

      Текущий             необходимые             Неверный текущий пароль. Введите свой текущий пароль.   
    <label for="newPassword">New</label>
    <input type="password"
           name="newPassword"
           placeholder="New password"
           ng-model="password.new"
           required>
    
    <label for="confirmPassword">Confirm</label>
    <input ng-disabled=""
           type="password"
           name="confirmPassword"
           placeholder="Confirm password"
           ng-model="password.confirm"
           ng-match="password.new"
           required>
    <span ng-show="changePasswordForm.confirmPassword.$error.match">
        New and confirm do not match
    </span>
    
    <div>
        <button type="submit" 
                ng-disabled="changePasswordForm.$invalid" 
                ng-click="changePassword(password.new, changePasswordForm);reset();">
            Change password
        </button>
    </div>
    

Ответ 8

В качестве варианта

// ES6 form controller class

class FormCtrl {
 constructor($scope, SomeApiService) {
   this.$scope = $scope;
   this.someApiService = SomeApiService;
   this.formData = {};
 }

 submit(form) {
   if (form.$valid) {
     this.someApiService
         .save(this.formData)
         .then(() => {
           // handle success

           // reset form
           form.$setPristine();
           form.$setUntouched();

           // clear data
           this.formData = {};
         })
         .catch((result) => {
           // handle error
           if (result.status === 400) {
             this.handleServerValidationErrors(form, result.data && result.data.errors)
           } else {// TODO: handle other errors}
         })
   }
 }

 handleServerValidationErrors(form, errors) {
  // form field to model map
  // add fields with input name different from name in model
  // example: <input type="text" name="bCategory" ng-model="user.categoryId"/>
  var map = {
    categoryId: 'bCategory',
    // other
  };

  if (errors && errors.length) {
    // handle form fields errors separately
    angular.forEach(errors, (error) => {
      let formFieldName = map[error.field] || error.field;
      let formField = form[formFieldName];
      let formFieldWatcher;

      if (formField) {
        // tell the form that field is invalid
        formField.$setValidity('server', false);

        // waits for any changes on the input
        // and when they happen it invalidates the server error.
        formFieldWatcher = this.$scope.$watch(() => formField.$viewValue, (newValue, oldValue) => {
          if (newValue === oldValue) {
            return;
          }

          // clean up the server error
          formField.$setValidity('server', true);

          // clean up form field watcher
          if (formFieldWatcher) {
            formFieldWatcher();
            formFieldWatcher = null;
          }
        });
      }
    });

  } else {
    // TODO: handle form validation
    alert('Invalid form data');
  }
}