forked from angular/code.angularjs.org
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmvc.html
138 lines (125 loc) · 5.33 KB
/
mvc.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
<h1><code ng:non-bindable=""></code>
<span class="hint"></span>
</h1>
<div><p>MVC allows for a clean and 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.</p>
<p>The model is a set of objects and primitives that are referenced from the Scope ($scope) object.
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.</p>
<h3>Source</h3>
<div source-edit="" source-edit-deps="angular.js script.js" source-edit-html="index.html-13" source-edit-css="" source-edit-js="script.js-12" source-edit-unit="" source-edit-scenario="scenario.js-14"></div>
<div class="tabbable"><div class="tab-pane" title="index.html">
<pre class="prettyprint linenums" ng-set-text="index.html-13" ng-html-wrap=" angular.js script.js"></pre>
<script type="text/ng-template" id="index.html-13">
<h4>Tic-Tac-Toe</h4>
<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>
</script>
</div>
<div class="tab-pane" title="script.js">
<pre class="prettyprint linenums" ng-set-text="script.js-12"></pre>
<script type="text/ng-template" id="script.js-12">
function TicTacToeCntl($scope, $location) {
$scope.cellStyle= {
'height': '20px',
'width': '20px',
'border': '1px solid black',
'text-align': 'center',
'vertical-align': 'middle',
'cursor': 'pointer'
};
$scope.reset = function() {
$scope.board = [
['', '', ''],
['', '', ''],
['', '', '']
];
$scope.nextMove = 'X';
$scope.winner = '';
setUrl();
};
$scope.dropPiece = function(row, col) {
if (!$scope.winner && !$scope.board[row][col]) {
$scope.board[row][col] = $scope.nextMove;
$scope.nextMove = $scope.nextMove == 'X' ? 'O' : 'X';
setUrl();
}
};
$scope.reset();
$scope.$watch(function() { return $location.search().board;}, readUrl);
function setUrl() {
var rows = [];
angular.forEach($scope.board, function(row) {
rows.push(row.join(','));
});
$location.search({board: rows.join(';') + '/' + $scope.nextMove});
}
function grade() {
var b = $scope.board;
$scope.winner =
row(0) || row(1) || row(2) ||
col(0) || col(1) || col(2) ||
diagonal(-1) || diagonal(1);
function row(row) { return same(b[row][0], b[row][1], b[row][2]);}
function col(col) { return same(b[0][col], b[1][col], b[2][col]);}
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 : '';};
}
function readUrl(value) {
if (value) {
value = value.split('/');
$scope.nextMove = value[1];
angular.forEach(value[0].split(';'), function(row, col){
$scope.board[col] = row.split(',');
});
grade();
}
}
}
</script>
</div>
<div class="tab-pane" title="End to end test">
<pre class="prettyprint linenums" ng-set-text="scenario.js-14"></pre>
<script type="text/ng-template" id="scenario.js-14">
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();
}
</script>
</div>
</div><h3>Demo</h3>
<div class="well doc-example-live" ng-embed-app="" ng-set-html="index.html-13" ng-eval-javascript="script.js-12"></div>
<h2>Things to notice</h2>
<ul>
<li>The controller is defined in JavaScript and has no reference to the rendering logic.</li>
<li>The controller is instantiated by Angular and injected into the view.</li>
<li>The controller can be instantiated in isolation (without a view) and the code will still execute.
This makes it very testable.</li>
<li>The HTML view is a projection of the model. In the above example, the model is stored in the
board variable.</li>
<li>All of the controller's properties (such as board and nextMove) are available to the view.</li>
<li>Changing the model changes the view.</li>
<li>The view can call any controller function.</li>
<li>In this example, the <code>setUrl()</code> and <code>readUrl()</code> 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 <a href="api/ng.$rootScope.Scope#$watch"><code>$watch()</code></a> to set up a listener that invokes <code>readUrl()</code> when needed.</li>
</ul></div>