Cookbook: Advanced Form

Work in Progress This page is currently being revised. It might be incomplete or contain inaccuracies.

Here we extend the basic form example to include common features such as reverting, dirty state detection, and preventing invalid form submission.

   <script>
   UserForm.$inject = ['$invalidWidgets'];
   function UserForm($invalidWidgets){
     this.$invalidWidgets = $invalidWidgets;
     this.state = /^\w\w$/;
     this.zip = /^\d\d\d\d\d$/;
     this.master = {
       name: 'John Smith',
       address:{
         line1: '123 Main St.',
         city:'Anytown',
         state:'AA',
         zip:'12345'
       },
       contacts:[
         {type:'phone', value:'1(234) 555-1212'}
       ]
     };
     this.cancel();
   }

   UserForm.prototype = {
     cancel: function(){
       this.form = angular.copy(this.master);
     },

     save: function(){
       this.master = this.form;
       this.cancel();
     }
   };
   </script>
   <div ng:controller="UserForm">

     <label>Name:</label><br/>
     <input type="text" name="form.name" ng:required/> <br/><br/>

     <label>Address:</label><br/>
     <input type="text" name="form.address.line1" size="33" ng:required/> <br/>
     <input type="text" name="form.address.city" size="12" ng:required/>,
     <input type="text" name="form.address.state" size="2" ng:required ng:validate="regexp:state"/>
     <input type="text" name="form.address.zip" size="5" ng:required
ng:validate="regexp:zip"/><br/><br/>

     <label>Contacts:</label>
     [ <a href="" ng:click="form.contacts.$add()">add</a> ]
     <div ng:repeat="contact in form.contacts">
       <select name="contact.type">
         <option>email</option>
         <option>phone</option>
         <option>pager</option>
         <option>IM</option>
       </select>
       <input type="text" name="contact.value" ng:required/>
        [ <a href="" ng:click="form.contacts.$remove(contact)">X</a> ]
     </div>
   <button ng:click="cancel()" ng:disabled="{{master.$equals(form)}}">Cancel</button>
   <button ng:click="save()" ng:disabled="{{$invalidWidgets.visible() ||
master.$equals(form)}}">Save</button>

   <hr/>
   Debug View:
   <pre>form={{form}}
   master={{master}}</pre>
   </div>
  it('should enable save button', function(){
    expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
    input('form.name').enter('');
    expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
    input('form.name').enter('change');
    expect(element(':button:contains(Save)').attr('disabled')).toBeFalsy();
    element(':button:contains(Save)').click();
    expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
  });
  it('should enable cancel button', function(){
    expect(element(':button:contains(Cancel)').attr('disabled')).toBeTruthy();
    input('form.name').enter('change');
    expect(element(':button:contains(Cancel)').attr('disabled')).toBeFalsy();
    element(':button:contains(Cancel)').click();
    expect(element(':button:contains(Cancel)').attr('disabled')).toBeTruthy();
    expect(element(':input[name=form.name]').val()).toEqual('John Smith');
  });

Things to notice