Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 <h1 id="lab_3_model_view_controller">Lab 3 - Model-View-Controller</h1> | 1 <h1 id="lab_3_model_view_controller">Create MVC</h1> |
| 2 | 2 |
| 3 <p>Whenever your application grows beyond a single script with a few dozen lines , it gets | 3 <p>Whenever your application grows beyond a single script with a few dozen lines , |
| 4 harder and harder to manage without a good separation of roles among app compone nts. One of the most common | 4 it gets harder and harder to manage without a good separation |
| 5 models for structuring a complex application, no matter what language, is the Mo del-View-Controller (MVC) and | 5 of roles among app components. |
| 6 its variants, like Model-View-Presentation (MVP).</p> | 6 One of the most common models for structuring a complex application, |
| 7 no matter what language, | |
| 8 is the Model-View-Controller (MVC) and its variants, | |
| 9 like Model-View-Presentation (MVP).</p> | |
| 7 | 10 |
| 8 <p>There are several frameworks to help apply <a href="http://developer.chrome.c om/trunk/apps/app_frameworks.html">MVC concepts</a> to a Javascript application, and most of them, | 11 <p>There are several frameworks to help apply |
| 9 as long as they are CSP compliant, can be used in a Chrome App. We will use the <a href="http://angularjs.org/">AngularJS</a> framework in parts of this tutoria l, with a special focus on the framework in this section.</p> | 12 <a href="http://developer.chrome.com/trunk/apps/app_frameworks.html">MVC concept s</a> |
| 13 to a Javascript application, and most of them, | |
| 14 as long as they are CSP compliant, can be used in a Chrome packaged app. | |
|
Renato Mangini (chromium)
2013/04/09 17:32:32
... Packaged App (upper case)
mkearney1
2013/04/10 17:59:58
We do not capitalize packaged apps throughout the
| |
| 15 In this lab, | |
| 16 we'll add an MVC model using both pure JavaScript and | |
| 17 the <a href="http://angularjs.org/">AngularJS</a> framework. | |
| 18 Most of the AngularJS code from this section was copied, | |
| 19 with small changes, from the AngularJS Todo tutorial.</p> | |
| 10 | 20 |
| 11 <h2 id="you_should_also_read">You should also read</h2> | 21 <p class="note"><b>Note:</b> |
| 22 Chrome packaged apps don't enforce any specific framework or programming sty le. | |
|
Renato Mangini (chromium)
2013/04/09 17:32:32
... Packaged Apps (upper case)
mkearney1
2013/04/10 17:59:58
Same as above.
On 2013/04/09 17:32:32, Renato Man
| |
| 23 </p> | |
| 12 | 24 |
| 13 <ul> | 25 <h2 id="simple">Create a Simple View</h2> |
| 14 <li><p><a href="http://developer.chrome.com/trunk/apps/angular_framework.html">B uild Apps with AngularJS</a> tutorial</p></li> | |
| 15 <li><p><a href="http://angularjs.org/">AngularJS Todo</a> tutorial</p></li> | |
| 16 </ul> | |
| 17 | 26 |
| 18 <p class="note"><b>Note:</b> Chrome apps don't enforce any specific framewo rk or programming style. This section and additional parts of this tutorial use the AngularJS framework. Most of the code from this section was copied, with sma ll changes, from the AngularJS Todo tutorial. </p> | 27 <h3 id="basic-mvc">Add MVC basics</h3> |
| 19 | 28 |
| 20 <h2 id="create_a_simple_view_using_angularjs">Create a simple view using Angular JS</h2> | 29 <p>If using AngularJS, download the |
| 30 <a href="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.min.js">An gular script</a> | |
| 31 and save it as | |
| 32 <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab3_mvc /angularjs/simpleview/angular.min.js">angular.min.js</a>.</p> | |
| 21 | 33 |
| 22 <ol> | 34 <p>If using JavaScript, |
| 23 <li><p>Download the <a href="http://ajax.googleapis.com/ajax/libs/angularjs/1.0. 2/angular.min.js">Angular script</a> and save it as <a href="https://github.com/ GoogleChrome/chrome-app-codelab/blob/master/lab3_mvc/angularjs/simpleview/angula r.min.js">angular.min.js</a>.</p></li> | 35 you will need to add a very simple controller with basic MVC functionalities: |
| 24 <li><p>Change your <a href="https://github.com/GoogleChrome/chrome-app-codelab/b lob/master/lab3_mvc/angularjs/simpleview/index.html">index.html</a> to use a sim ple Angular sample: | 36 <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab3_mvc /javascript/simpleview/controller.js">JavaScript controller.js</a></p> |
| 25 <pre> | 37 |
| 38 <h3 id="update-view">Update view</h3> | |
| 39 | |
| 40 <p>Change the <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/m aster/lab3_mvc/angularjs/simpleview/index.html">AngularJS index.html</a> or | |
| 41 <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab3_mvc /javascript/simpleview/index.html">JavaScript index.html</a> to use a simple sam ple: | |
| 42 </p> | |
| 43 | |
| 44 <tabs data-group="source"> | |
| 45 | |
| 46 <header tabindex="0" data-value="angular">Angular</header> | |
| 47 <header tabindex="0" data-value="js">JavaScript</header> | |
| 48 | |
| 49 <content> | |
| 50 <pre> | |
| 51 <!doctype html> | |
| 26 <html ng-app ng-csp> | 52 <html ng-app ng-csp> |
| 27 <head> | 53 <head> |
| 28 <script src="angular.min.js"></script> | 54 <script src="angular.min.js"></script> |
| 55 <link rel="stylesheet" href="todo.css"> | |
| 56 </head> | |
| 57 <body> | |
| 58 <h2>Todo</h2> | |
| 59 <div> | |
| 60 <ul> | |
| 61 <li> | |
| 62 %7B%7BtodoText%7D%7D | |
|
Renato Mangini (chromium)
2013/04/09 17:32:32
{{todoText}}
mkearney1
2013/04/10 17:59:58
Done.
| |
| 63 </li> | |
| 64 </ul> | |
| 65 <input type="text" ng-model="todoText" size="30" | |
| 66 placeholder="type your todo here"> | |
| 67 </div> | |
| 68 </body> | |
| 69 </html> | |
| 70 </pre> | |
| 71 </content> | |
| 72 <content> | |
| 73 <pre> | |
| 74 <!doctype html> | |
| 75 <html> | |
| 76 <head> | |
| 29 <link rel="stylesheet" href="todo.css"> | 77 <link rel="stylesheet" href="todo.css"> |
| 30 </head> | 78 </head> |
| 31 <body> | 79 <body> |
| 32 <h2>Todo</h2> | 80 <h2>Todo</h2> |
| 33 <div> | 81 <div> |
| 34 <ul> | 82 <ul> |
| 35 <li> | 83 <li id="todoText"> |
| 36 {{todoText}} | |
| 37 </li> | 84 </li> |
| 38 </ul> | 85 </ul> |
| 39 <input type="text" ng-model="todoText" size=" 30" | 86 <input type="text" id="newTodo" size="30" |
| 40 placeholder="type your todo here"> | 87 placeholder="type your todo here"> |
| 41 </div> | 88 </div> |
| 89 <script src="controller.js"></script> | |
| 42 </body> | 90 </body> |
| 43 </html> | 91 </html> |
| 44 </pre></p></li> | 92 </pre> |
| 45 <li><p>Add a simple stylesheet: <a href="https://github.com/GoogleChrome/chrome- app-codelab/blob/master/lab3_mvc/angularjs/simpleview/todo.css">todo.css</a> | 93 </content> |
| 94 </tabs> | |
| 95 | |
| 96 <p class="note"><b>Note:</b> The ng-csp directive tells Angular to run in a &qu ot;content security mode". You don't need this directive when using Ang ular v1.1.0+. We've included it here so that the sample works regardless of the Angular version in use.</p> | |
|
Renato Mangini (chromium)
2013/04/09 17:32:32
<code>ng-csp</code>
mkearney1
2013/04/10 17:59:58
Done.
| |
| 97 | |
| 98 <h3 id="stylesheet">Add stylesheet</h3> | |
| 99 | |
| 100 <p><a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab3_ mvc/angularjs/simpleview/todo.css">AngularJS todo.css</a> and | |
| 101 <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab3_mvc /javascript/simpleview/todo.css">JavaScript todo.css</a> are the same: | |
| 102 </p> | |
| 103 | |
| 46 <pre> | 104 <pre> |
| 47 body { | 105 body { |
| 48 font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; | 106 font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; |
| 49 } | 107 } |
| 50 | 108 |
| 51 ul { | 109 ul { |
| 52 list-style: none; | 110 list-style: none; |
| 53 } | 111 } |
| 54 | 112 |
| 55 button, input[type=submit] { | 113 button, input[type=submit] { |
| 56 background-color: #0074CC; | 114 background-color: #0074CC; |
| 57 background-image: linear-gradient(top, #08C, #05C); | 115 background-image: linear-gradient(top, #08C, #05C); |
| 58 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); | 116 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); |
| 59 text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); | 117 text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); |
| 60 color: white; | 118 color: white; |
| 61 } | 119 } |
| 62 | 120 |
| 63 .done-true { | 121 .done-true { |
| 64 text-decoration: line-through; | 122 text-decoration: line-through; |
| 65 color: grey; | 123 color: grey; |
| 66 } | 124 } |
| 67 </pre></li><li>Check the results by reloading the app: open the app, right-click and select Reload App.</li> | 125 </pre> |
| 68 </ol> | |
| 69 | 126 |
| 70 <p class="note"><b>Note:</b> The ng-csp directive tells Angular to run in a &qu ot;content security mode". You don't need this directive when using Ang ular v1.1.0+. We've included it here so that the sample works regardless of the Angular version in use.</p> | 127 <h3 id="check1">Check the results</h3> |
| 71 | 128 |
| 72 <h2 id="add_a_controller">Add a Controller</h2> | 129 <p> |
| 130 Check the results by reloading the app: open the app, right-click and select Rel oad App.</li> | |
| 131 </p> | |
| 73 | 132 |
| 74 The previous sample, although interesting, is not exactly useful. Let's tran sform it into a real Todo list, instead of a simple Todo item. We will create a controller (controller.js) and make some small changes in the index.html: | 133 <h2 id="real-todo">Create real Todo list</h2> |
| 75 | 134 |
| 76 <ol> | 135 <p> |
| 77 <li>Add the <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/mas ter/lab3_mvc/angularjs/withcontroller/controller.js">controller.js</a> file: | 136 The previous sample, although interesting, is not exactly useful. |
| 78 <pre> | 137 Let's transform it into a real Todo list, instead of a simple Todo item. |
| 138 </p> | |
| 139 | |
| 140 <h3 id="controller">Add controller</h3> | |
| 141 | |
| 142 <p> | |
| 143 Whether using pure JavaScript or AngularJS, | |
| 144 the controller manages the Todo list: | |
| 145 <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab3_mvc /angularjs/withcontroller/controller.js">AngularJS controller.js</a> or | |
| 146 <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab3_mvc /javascript/withcontroller/controller.js">JavaScript controller.js</a>. | |
| 147 </p> | |
| 148 | |
| 149 <tabs data-group="source"> | |
| 150 | |
| 151 <header tabindex="0" data-value="angular">Angular</header> | |
| 152 <header tabindex="0" data-value="js">JavaScript</header> | |
| 153 | |
| 154 <content> | |
| 155 <pre> | |
| 79 function TodoCtrl($scope) { | 156 function TodoCtrl($scope) { |
| 80 $scope.todos = [ | 157 $scope.todos = [ |
| 81 {text:'learn angular', done:true}, | 158 {text:'learn angular', done:true}, |
| 82 {text:'build an angular Chrome packaged app', done:false}]; | 159 {text:'build an angular Chrome packaged app', done:false}]; |
| 83 | 160 |
| 84 $scope.addTodo = function() { | 161 $scope.addTodo = function() { |
| 85 $scope.todos.push({text:$scope.todoText, done:false}); | 162 $scope.todos.push({text:$scope.todoText, done:false}); |
| 86 $scope.todoText = ''; | 163 $scope.todoText = ''; |
| 87 }; | 164 }; |
| 88 | 165 |
| 89 $scope.remaining = function() { | 166 $scope.remaining = function() { |
| 90 var count = 0; | 167 var count = 0; |
| 91 angular.forEach($scope.todos, function(todo) { | 168 angular.forEach($scope.todos, function(todo) { |
| 92 count += todo.done ? 0 : 1; | 169 count += todo.done ? 0 : 1; |
| 93 }); | 170 }); |
| 94 return count; | 171 return count; |
| 95 }; | 172 }; |
| 96 | 173 |
| 97 $scope.archive = function() { | 174 $scope.archive = function() { |
| 98 var oldTodos = $scope.todos; | 175 var oldTodos = $scope.todos; |
| 99 $scope.todos = []; | 176 $scope.todos = []; |
| 100 angular.forEach(oldTodos, function(todo) { | 177 angular.forEach(oldTodos, function(todo) { |
| 101 if (!todo.done) $scope.todos.push(todo); | 178 if (!todo.done) $scope.todos.push(todo); |
| 102 }); | 179 }); |
| 103 }; | 180 }; |
| 104 } | 181 } |
| 105 </pre></p></li><li><p>Change <a href="https://github.com/GoogleChrome/chrome-app -codelab/blob/master/lab3_mvc/angularjs/withcontroller/index.html">index.html</a > file: | 182 </pre> |
| 106 <pre> | 183 </content> |
| 184 <content> | |
| 185 <pre> | |
| 186 (function(exports) { | |
| 187 | |
| 188 var nextId = 1; | |
| 189 | |
| 190 var TodoModel = function() { | |
| 191 this.todos = {}; | |
| 192 this.listeners = []; | |
| 193 } | |
| 194 | |
| 195 TodoModel.prototype.clearTodos = function() { | |
| 196 this.todos = {}; | |
| 197 this.notifyListeners('removed'); | |
| 198 } | |
| 199 | |
| 200 TodoModel.prototype.archiveDone = function() { | |
| 201 var oldTodos = this.todos; | |
| 202 this.todos={}; | |
| 203 for (var id in oldTodos) { | |
| 204 if ( ! oldTodos[id].isDone ) { | |
| 205 this.todos[id] = oldTodos[id]; | |
| 206 } | |
| 207 } | |
| 208 this.notifyListeners('archived'); | |
| 209 } | |
| 210 | |
| 211 TodoModel.prototype.setTodoState = function(id, isDone) { | |
| 212 if ( this.todos[id].isDone != isDone ) { | |
| 213 this.todos[id].isDone = isDone; | |
| 214 this.notifyListeners('stateChanged', id); | |
| 215 } | |
| 216 } | |
| 217 | |
| 218 TodoModel.prototype.addTodo = function(text, isDone) { | |
| 219 var id = nextId++; | |
| 220 this.todos[id]={'id': id, 'text': text, 'isDone': isDone}; | |
| 221 this.notifyListeners('added', id); | |
| 222 } | |
| 223 | |
| 224 TodoModel.prototype.addListener = function(listener) { | |
| 225 this.listeners.push(listener); | |
| 226 } | |
| 227 | |
| 228 TodoModel.prototype.notifyListeners = function(change, param) { | |
| 229 var this_ = this; | |
| 230 this.listeners.forEach(function(listener) { | |
| 231 listener(this_, change, param); | |
| 232 }); | |
| 233 } | |
| 234 | |
| 235 exports.TodoModel = TodoModel; | |
| 236 | |
| 237 })(window); | |
| 238 | |
| 239 | |
| 240 window.addEventListener('DOMContentLoaded', function() { | |
| 241 | |
| 242 var model = new TodoModel(); | |
| 243 var form = document.querySelector('form'); | |
| 244 var archive = document.getElementById('archive'); | |
| 245 var list = document.getElementById('list'); | |
| 246 var todoTemplate = document.querySelector('#templates > [data-name="list"]' ); | |
| 247 | |
| 248 form.addEventListener('submit', function(e) { | |
| 249 var textEl = e.target.querySelector('input[type="text"]'); | |
| 250 model.addTodo(textEl.value, false); | |
| 251 textEl.value=null; | |
| 252 e.preventDefault(); | |
| 253 }); | |
| 254 | |
| 255 archive.addEventListener('click', function(e) { | |
| 256 model.archiveDone(); | |
| 257 e.preventDefault(); | |
| 258 }); | |
| 259 | |
| 260 model.addListener(function(model, changeType, param) { | |
| 261 if ( changeType === 'removed' || changeType === 'archived') { | |
| 262 redrawUI(model); | |
| 263 } else if ( changeType === 'added' ) { | |
| 264 drawTodo(model.todos[param], list); | |
| 265 } else if ( changeType === 'stateChanged') { | |
| 266 updateTodo(model.todos[param]); | |
| 267 } | |
| 268 updateCounters(model); | |
| 269 }); | |
| 270 | |
| 271 var redrawUI = function(model) { | |
| 272 list.innerHTML=''; | |
| 273 for (var id in model.todos) { | |
| 274 drawTodo(model.todos[id], list); | |
| 275 } | |
| 276 }; | |
| 277 | |
| 278 var drawTodo = function(todoObj, container) { | |
| 279 var el = todoTemplate.cloneNode(true); | |
| 280 el.setAttribute('data-id', todoObj.id); | |
| 281 container.appendChild(el); | |
| 282 updateTodo(todoObj); | |
| 283 var checkbox = el.querySelector('input[type="checkbox"]'); | |
| 284 checkbox.addEventListener('change', function(e) { | |
| 285 model.setTodoState(todoObj.id, e.target.checked); | |
| 286 }); | |
| 287 } | |
| 288 | |
| 289 var updateTodo = function(model) { | |
| 290 var todoElement = list.querySelector('li[data-id="'+model.id+'"]'); | |
| 291 if (todoElement) { | |
| 292 var checkbox = todoElement.querySelector('input[type="checkbox"]'); | |
| 293 var desc = todoElement.querySelector('span'); | |
| 294 checkbox.checked = model.isDone; | |
| 295 desc.innerText = model.text; | |
| 296 desc.className = "done-"+model.isDone; | |
| 297 } | |
| 298 } | |
| 299 | |
| 300 var updateCounters = function(model) { | |
| 301 var count = 0; | |
| 302 var notDone = 0; | |
| 303 for (var id in model.todos) { | |
| 304 count++; | |
| 305 if ( ! model.todos[id].isDone ) { | |
| 306 notDone ++; | |
| 307 } | |
| 308 } | |
| 309 document.getElementById('remaining').innerText = notDone; | |
| 310 document.getElementById('length').innerText = count; | |
| 311 } | |
| 312 | |
| 313 updateCounters(model); | |
| 314 | |
| 315 }); | |
| 316 </pre> | |
| 317 </content> | |
| 318 </tabs> | |
| 319 | |
| 320 <h3 id="index">Update view</h3> | |
| 321 | |
| 322 <p>Change the <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/m aster/lab3_mvc/angularjs/withcontroller/index.html">AngularJS index.html</a> or | |
| 323 <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab3_mvc /javascript/withcontroller/index.html">JavaScript index.html</a>: | |
| 324 </p> | |
| 325 | |
| 326 <tabs data-group="source"> | |
| 327 | |
| 328 <header tabindex="0" data-value="angular">Angular</header> | |
| 329 <header tabindex="0" data-value="js">JavaScript</header> | |
| 330 | |
| 331 <content> | |
| 332 <pre> | |
| 107 <html ng-app ng-csp> | 333 <html ng-app ng-csp> |
| 108 <head> | 334 <head> |
| 109 <script src="angular.min.js"></script> | 335 <script src="angular.min.js"></script> |
| 110 <script src="controller.js"></script> | 336 <script src="controller.js"></script> |
| 111 <link rel="stylesheet" href="todo.css"> | 337 <link rel="stylesheet" href="todo.css"> |
| 112 </head> | 338 </head> |
| 113 <body> | 339 <body> |
| 114 <h2>Todo</h2> | 340 <h2>Todo</h2> |
| 115 <div ng-controller="TodoCtrl"> | 341 <div ng-controller="TodoCtrl"> |
| 116 <span>{{remaining()}} of {{todos.lengt h}} remaining</span> | 342 <span>{{remaining()}} of {{todos.lengt h}} remaining</span> |
| 117 [ <a href="" ng-click="archive()">archive</a& gt; ] | 343 [ <a href="" ng-click="archive()">archive</a& gt; ] |
| 118 <ul> | 344 <ul> |
| 119 <li ng-repeat="todo in todos"> | 345 <li ng-repeat="todo in todos"> |
| 120 <input type="checkbox" ng-model="todo.done"> | 346 <input type="checkbox" ng-model="todo.done"> |
| 121 <span class="done-{{todo.done}}">& #123;{todo.text}}</span> | 347 <span class="done-{{todo.done}}">& #123;{todo.text}}</span> |
| 122 </li> | 348 </li> |
| 123 </ul> | 349 </ul> |
| 124 <form ng-submit="addTodo()"> | 350 <form ng-submit="addTodo()"> |
| 125 <input type="text" ng-model="todoText" size=" ;30" | 351 <input type="text" ng-model="todoText" size=" ;30" |
| 126 placeholder="add new todo here"> | 352 placeholder="add new todo here"> |
| 127 <input class="btn-primary" type="submit" value=&q uot;add"> | 353 <input class="btn-primary" type="submit" value=&q uot;add"> |
| 128 </form> | 354 </form> |
| 129 </div> | 355 </div> |
| 130 </body> | 356 </body> |
| 131 </html> | 357 </html> |
| 132 </pre></p></li> | 358 </pre> |
| 133 <li><p>Check the results by reloading the app: open the app, right-click and sel ect Reload App.</p></li> | 359 </content> |
| 134 </ol> | 360 <content> |
| 361 <pre> | |
| 362 <!doctype html> | |
| 363 <html> | |
| 364 <head> | |
| 365 <link rel="stylesheet" href="todo.css"> | |
| 366 </head> | |
| 367 <body> | |
| 368 <h2>Todo</h2> | |
| 369 <div> | |
| 370 <span><span id="remaining"></span> of <span id="length"></span> remaining</span> | |
| 371 [ <a href="" id="archive">archive</a> ] | |
| 372 <ul class="unstyled" id="list"> | |
| 373 </ul> | |
| 374 <form> | |
| 375 <input type="text" size="30" | |
| 376 placeholder="add new todo here"> | |
| 377 <input class="btn-primary" type="submit" value=&q uot;add"> | |
| 378 </form> | |
| 379 </div> | |
| 380 | |
| 381 <!-- poor man's template --> | |
| 382 <div id="templates" style="display: none;"> | |
| 383 <li data-name="list"> | |
| 384 <input type="checkbox"> | |
| 385 <span></span> | |
| 386 </li> | |
| 387 </div> | |
| 388 | |
| 389 <script src="controller.js"></script> | |
| 390 </body> | |
| 391 </html> | |
| 392 </pre> | |
| 393 </content> | |
| 394 </tabs> | |
| 135 | 395 |
| 136 <p>Note how the data, stored in an array inside the controller, binds to the vie w and is automatically updated when it is changed by the controller.</p> | 396 <p>Note how the data, stored in an array inside the controller, binds to the vie w and is automatically updated when it is changed by the controller.</p> |
| 137 | 397 |
| 138 <h1 id="takeaways_">Takeaways:</h1> | 398 <h3 id="check2">Check the results</h3> |
| 399 | |
| 400 <p> | |
| 401 Check the results by reloading the app: open the app, right-click and select Rel oad App.</li> | |
| 402 </p> | |
| 403 | |
| 404 <h2 id="takeaways_">Takeaways</h2> | |
| 139 | 405 |
| 140 <ul> | 406 <ul> |
| 141 <li><p>Chrome apps are <a href="http://developer.chrome.com/apps/offline_apps.ht ml">offline first</a>, so the recommended way to include third-party scripts is to download and package them inside your app.</p></li> | 407 <li><p>Chrome packaged apps are |
| 142 <li><p>You can use any framework you want, as long as it complies with Content S ecurity Policies and other restrictions that Chrome apps are enforced to follow. </p></li> | 408 <a href="http://developer.chrome.com/apps/offline_apps.html">offline first</a>, |
| 143 <li><p>MVC frameworks make your life easier. Use them, specially if you want to build a non-trivial application.</p></li> | 409 so the recommended way to include third-party scripts is to download |
| 410 and package them inside your app.</p></li> | |
| 411 <li><p>You can use any framework you want, | |
| 412 as long as it complies with Content Security Policies | |
| 413 and other restrictions that Chrome packaged apps are enforced to follow.</p></li > | |
| 414 <li><p>MVC frameworks make your life easier. | |
| 415 Use them, specially if you want to build a non-trivial application.</p></li> | |
| 144 </ul> | 416 </ul> |
| 145 | 417 |
| 146 <h1 id="what_39_s_next_">What's next?</h1> | 418 <h2 id="you_should_also_read">You should also read</h2> |
| 147 | 419 |
| 148 <p>Eventually in <a href="app_codelab4_testing.html">lab4_testing</a>, you will test your app. | 420 <ul> |
| 149 Right now this lab is a work-in-progress. You can skip ahead to <a href="app_cod elab5_data.html">lab5_data</a>.</p> | 421 <li><p><a href="http://developer.chrome.com/trunk/apps/angular_framework.html">B uild Apps with AngularJS</a> tutorial</p></li> |
| 422 <li><p><a href="http://angularjs.org/">AngularJS Todo</a> tutorial</p></li> | |
| 423 </ul> | |
| 424 | |
| 425 <h2 id="what_39_s_next_">What's next?</h2> | |
| 426 | |
| 427 <p>In <a href="app_codelab5_data.html">4 - Save and Fetch Data</a>, | |
| 428 you will modify your Todo list app so that Todo items are saved.</p> | |
| OLD | NEW |