Search code examples
angularjsformsng-messages

AngularJS set form value from controller with ngMessages


I try to use forms with angular 1.5.9 but can't figure out a way to make it work for the following requirements:

  1. Access form data from the controller (bound with controllerAs)
  2. Don't use the $scope to bind the form
  3. Be able to set default values from the controller
  4. Be able to change values from the controller
  5. Be able to access data from the controller
  6. Having validation working with ngMessages

I've made a commented jsbin to illustrate the problem and what I've tried, you can find it here : http://jsbin.com/guzejelula/edit?html,js,output.

Every requirements up here is satisfied in the jsbin except the 4th, illustrated by :

this.setLastName = function(name) { 
    // When doing this while the field is empty, the "required"
    // error disappear but the input stays empty.
    this.userForm.lastName.$setViewValue(name);
    this.userForm.lastName.$validate();  
}

What am I missing ?


Solution

  • From what I can tell, I think you are binding to the incorrect model.

    $ctrl.userForm is your form controller instance. The "name" attribute on ngModel exposes the form control (the input) instance to the form controller instance. So, $ctrl.userForm.lastName is not going to be the raw model value, it'll be an object with ngModelController functions.

    In order to pre-populate the form controller correctly, ensure the ng-model expression has the correct initial value. Currently, your ng-model is bound to $scope.lastName. That's why it's rendering empty instead of "toto." To fix it, you could add $scope.lastName = "toto" to your controller, but since you are avoiding $scope usage, just define this.lastName = 'toto' and use ng-model="$ctrl.lastName".

    From the button, you just need to do this.lastName = name. No need to call $render and $validate yourself because Angular does it on behalf of you because it detects a change in model value.

    In case it helps, I just wrote an article for what's happening under the hood in ngModelController if you're interested.

    Hope it helps!

    var app = angular.module('jsbin', ['ngMessages']);
    
    app.controller('DemoCtrl', function() {
      
      /**
       * Here I would like to set a defaut state form the form.
       * 
       * This object will be replaced by the FormController instance
       * as defined in the view by "<form name="$ctrl.userForm">" 
       * with no regards for its current content.
       */
    
      this.lastName = 'toto';
      this.formResults = {};
      
      /**
       * I would like to set the value of one or multiple fields
       * in the controller.
       * Here I try to set "lastName" with no success.
       *
       * I excepted something like:
       * this.userForm.setValues({
       *   lastName : 'Doe', 
       *   firstName: 'John'
       * });
       * 
       * or 
       *
       * this.userForm.setValue('lastName', 'value here');
       *
       * ...
       */
      this.setLastName = function(name) { 
        // When doing this while the field is empty, the "required"
        // error disappear but the input stays empty.
        this.lastName = name;
      }
      
      
      /**
       * Here a clean way to extract current values of the form would be handy..
       * Something like : 
       * this.userForm.getValues(); 
       */
      this.submit = function(form){
        this.formResults = {};
        if (form.$valid) {
          var values = {};
          angular.forEach(form, function (value, key) {
            if (typeof(value) === 'object' && value.hasOwnProperty('$modelValue')) {
                values[key] = value.$modelValue;
            } 
          });
          this.formResults = values;
        }
      };
    });
    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <title>Angular JS</title>
    </head>
    <body ng-app="jsbin">
      <div ng-controller="DemoCtrl as $ctrl">
        
        <form name="$ctrl.userForm" novalidate>
          <label>Last Name:</label>
          <input name="lastName" ng-model="$ctrl.lastName" required="" minlength="2" type="text">
          <ng-messages for="$ctrl.userForm.lastName.$error">
              <ng-message when="minlength">Too small!</ng-message>
              <ng-message when="required">Required!</ng-message>
          </ng-messages>
          <button ng-click="$ctrl.submit($ctrl.userForm)">Submit</button>
        </form>
        
        <p>
          <button ng-click="$ctrl.setLastName('Test')">Set value to "lastName"</button>
        </p>
        
        <h2>Results</h2>
        {{$ctrl.formResults|json}}
    
      </div>
      <script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.9/angular.js"></script>
      <script src="//cdnjs.cloudflare.com/ajax/libs/angular-messages/1.5.9/angular-messages.js"></script>
    </body>
    </html>