diff options
| author | Misko Hevery | 2011-04-29 15:18:27 -0700 | 
|---|---|---|
| committer | Igor Minar | 2011-06-06 22:28:38 -0700 | 
| commit | 11e9572b952e49b01035e956c412d6095533031a (patch) | |
| tree | 04dbf96802f552693d44c541c0d825a2769e3d57 /docs/content/cookbook/mvc.ngdoc | |
| parent | b6bc6c2ddf1ae1523ec7e4cb92db209cd6501181 (diff) | |
| download | angular.js-11e9572b952e49b01035e956c412d6095533031a.tar.bz2 | |
Move documentation under individual headings
Diffstat (limited to 'docs/content/cookbook/mvc.ngdoc')
| -rw-r--r-- | docs/content/cookbook/mvc.ngdoc | 125 | 
1 files changed, 125 insertions, 0 deletions
| diff --git a/docs/content/cookbook/mvc.ngdoc b/docs/content/cookbook/mvc.ngdoc new file mode 100644 index 00000000..94688547 --- /dev/null +++ b/docs/content/cookbook/mvc.ngdoc @@ -0,0 +1,125 @@ +@workInProgress +@ngdoc overview +@name Cookbook: MVC +@description + +MVC allows for a clean an testable separation between the behavior (controller) and the view +(HTML template). A Controller is just a JavaScript class which is grafted onto the scope of the +view. This makes it very easy for the controller and the view to share the model. + +The model is simply the controller's this. This makes it very easy to test the controller in +isolation since one can simply instantiate the controller and test without a view, because there is +no connection between the controller and the view. + + +<doc:example> + <doc:source> +    <script> +    function TicTacToeCntl(){ +      this.cellStyle= { +        'height': '20px', +        'width': '20px', +        'border': '1px solid black', +        'text-align': 'center', +        'vertical-align': 'middle', +        'cursor': 'pointer' +      }; +      this.reset(); +      this.$watch('$location.hashSearch.board', this.readUrl); +    } +    TicTacToeCntl.prototype = { +      dropPiece: function(row, col) { +        if (!this.winner && !this.board[row][col]) { +          this.board[row][col] = this.nextMove; +          this.nextMove = this.nextMove == 'X' ? 'O' : 'X'; +          this.setUrl(); +        } +      }, +      reset: function(){ +        this.board = [ +          ['', '', ''], +          ['', '', ''], +          ['', '', ''] +        ]; +        this.nextMove = 'X'; +        this.winner = ''; +        this.setUrl(); +      }, +      grade: function(){ +       var b = this.board; +       this.winner = +        row(0) || row(1) || row(2) || +        col(0) || col(1) || col(2) || +        diagonal(-1) || diagonal(1); +       function row(r) { return same(b[r][0], b[r][1], b[r][2]);} +       function col(c) { return same(b[0][c], b[1][c], b[2][c]);} +       function diagonal(i) { return same(b[0][1-i], b[1][1], b[2][1+i]);} +       function same(a, b, c) { return (a==b && b==c) ? a : '';}; +      }, +      setUrl: function(){ +        var rows = []; +        angular.forEach(this.board, function(row){ +          rows.push(row.join(',')); +        }); +        this.$location.hashSearch.board = rows.join(';') + '/' + this.nextMove; +      }, +      readUrl: function(value) { +        if (value) { +          value = value.split('/'); +          this.nextMove = value[1]; +          angular.forEach(value[0].split(';'), function(row, i){ +            this.board[i] = row.split(','); +          }, this); +          this.grade(); +        } else { +          this.reset(); +        } +      } +    }; +    </script> +    <h3>Tic-Tac-Toe</h3> +    <div ng:controller="TicTacToeCntl"> +    Next Player: {{nextMove}} +    <div class="winner" ng:show="winner">Player {{winner}} has won!</div> +    <table class="board"> +      <tr ng:repeat="row in board" style="height:15px;"> +        <td ng:repeat="cell in row" ng:style="cellStyle" +                ng:click="dropPiece($parent.$index, $index)">{{cell}}</td> +      </tr> +    </table> +    <button ng:click="reset()">reset board</button> +    </div> + </doc:source> + <doc:scenario> +   it('should play a game', function(){ +    piece(1, 1); +    expect(binding('nextMove')).toEqual('O'); +    piece(3, 1); +    expect(binding('nextMove')).toEqual('X'); +    piece(1, 2); +    piece(3, 2); +    piece(1, 3); +    expect(element('.winner').text()).toEqual('Player X has won!'); +   }); + +   function piece(row, col) { +     element('.board tr:nth-child('+row+') td:nth-child('+col+')').click(); +   } + </doc:scenario> +</doc:example> + + +# Things to notice + +* The controller is defined in JavaScript and has no reference to the rendering logic. +* The controller is instantiated by <angular/> and injected into the view. +* The controller can be instantiated in isolation (without a view) and the code will still execute. +  This makes it very testable. +* The HTML view is a projection of the model. In the above example, the model is stored in the +  board variable. +* All of the controller's properties (such as board and nextMove) are available to the view. +* Changing the model changes the view. +* The view can call any controller function. +* In this example, the `setUrl()` and `readUrl()` functions copy the game state to/from the URL's +  hash so the browser's back button will undo game steps. See deep-linking. This example calls +  {@link angular.Scope.$watch $watch()} to set up a listener that invokes `readUrl()` when needed. | 
