AngularJS - Server side validation and client side forms

前端 未结 8 738
忘了有多久
忘了有多久 2020-12-12 11:40

I am trying to understand how to do the following things:

What is the accepted way of declaring a form. My understanding is you just declare the form in HTML, and ad

相关标签:
8条回答
  • 2020-12-12 11:50

    I've got similar solution as Derek, described on codetunes blog. TL;DR:

    • display an error in similar way as in Derek's solution:

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

    • add directive which would clean up an error when user change the input:

      <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)
        }
      
    • Handle an error by passing the error message to the scope and telling that form has an error:

      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(', ') 
      

    Hope it would be useful :)

    0 讨论(0)
  • 2020-12-12 11:51

    I've also been playing around with this kind of thing recently and I've knocked up this demo. I think it does what you need.

    Setup your form as per normal with any particular client side validations you want to use:

    <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>
    

    Note also I have added a serverMessage for each field:

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

    This is a customisable message that comes back from the server and it works the same way as any other error message (as far as I can tell).

    Here is the controller:

    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]);
                  }
              }
          };
    }
    

    I am pretending to call the server in pretendThisIsOnTheServerAndCalledViaAjax you can replace it with an ajax call, the point is it just returns the validation state for each field. In this simple case I am using the value VALID to indicate that the field is valid, any other value is treated as an error message. You may want something more sophisticated!

    Once you have the validation state from the server you just need to update the state in your form.

    You can access the form from scope, in this case the form is called myForm so $scope.myForm gets you the form. (Source for the form controller is here if you want to read up on how it works).

    You then want to tell the form whether the field is valid/invalid:

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

    or

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

    We also need to set the error message. First of all get the accessor for the field using $parse. Then assign the value from the server.

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

    Hope that helps

    0 讨论(0)
  • 2020-12-12 12:00

    If you choose ngResource, it would look like this

    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 
        }
      );
    };
    

    The most important thing is, that your HTTP-Response code have to be a 4xx to enter the failure callback.

    0 讨论(0)
  • 2020-12-12 12:00

    I needed this in a few projects so I created a directive. Finally took a moment to put it up on GitHub for anyone who wants a drop-in solution.

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

    Features:

    • Drop in solution for Ajax validation of any text or password input

    • Works with Angulars build in validation and cab be accessed at formName.inputName.$error.ngRemoteValidate

    • Throttles server requests (default 400ms) and can be set with ng-remote-throttle="550"

    • Allows HTTP method definition (default POST) with ng-remote-method="GET" Example usage for a change password form that requires the user to enter their current password as well as the new password.:

      Change password

      Current Required Incorrect current password. Please enter your current account password.
      <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>
      
    0 讨论(0)
  • 2020-12-12 12:02

    Well, the Answer Derek Ekins gave is very nice to work on. But: If you disable the submit button with ng-disabled="myForm.$invalid" - the button will not automatically go back to enabled as the server-based error state doesn't seem to be changed. Not even if you edit ALL fields in a form again to comply with valid inputs (based on client side validation).

    0 讨论(0)
  • 2020-12-12 12:05

    By default, the form is submitted normally. If you don't provide a name property for each field in the form then it won't submit the correct data. What you can do is capture the form before it submitted and submit that data yourself via ajax.

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

    And then in your $scope.onSubmit() function:

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

    You can also validate the data by setting up required attributes.

    0 讨论(0)
提交回复
热议问题