# 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

- Cancel & save buttons are only enabled if the form is dirty â there is something to cancel or save.
- Save button is only enabled if there are no validation errors on the form.
- Cancel reverts the form changes back to original state.
- Save updates the internal model of the form.
- Debug view shows the two models. One presented to the user form and the other being the pristine copy master.