Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(954)

Side by Side Diff: chrome/common/extensions/docs/templates/articles/app_codelab_import_todomvc.html

Issue 647763004: Clean-up Chrome Apps codelab. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 <h1 id="import-existing-app"> 1 <h1 id="import-existing-app">
2 <span class="h1-step">Step 2:</span> 2 <span class="h1-step">Step 2:</span>
3 Import an Existing Web App 3 Import an Existing Web App
4 </h1> 4 </h1>
5 5
6 <p class="note"> 6 <p class="note">
7 <strong>Want to start fresh from here?</strong> 7 <strong>Want to start fresh from here?</strong>
8 Find the previous step's code in the <a href="https://github.com/mangini/io13- codelab/archive/master.zip">reference code zip</a> under <strong><em>cheat_code > solution_for_step1</strong></em>. 8 Find the previous step's code in the <a href="https://github.com/mangini/io13- codelab/archive/master.zip">reference code zip</a> under <strong><em>cheat_code > solution_for_step1</strong></em>.
9 </p> 9 </p>
10 10
11 <p>In this step, you will learn:</p> 11 <p>In this step, you will learn:</p>
12 12
13 <ul> 13 <ul>
14 <li>How to adapt an existing web application for the Chrome Apps platform.</li > 14 <li>How to adapt an existing web application for the Chrome Apps platform.</li >
15 <li>How to make your app scripts Content Security Policy (CSP) compliant.</li> 15 <li>How to make your app scripts Content Security Policy (CSP) compliant.</li>
16 <li>How to implement local storage using the <code>chrome.storage.local</code> API.</li> 16 <li>How to implement local storage using the <a href="/apps/storage" title="Re ad 'chrome.storage.local' in the Chrome developer docs">chrome.storage.local</a> .</li>
17 </ul> 17 </ul>
18 18
19 <p> 19 <p>
20 <em>Estimated time to complete this step: 20 minutes.</em> 20 <em>Estimated time to complete this step: 20 minutes.</em>
21 <br> 21 <br>
22 To preview what you will complete in this step, <a href="#launch">jump down to the bottom of this page &#8595;</a>. 22 To preview what you will complete in this step, <a href="#launch">jump down to the bottom of this page &#8595;</a>.
23 </p> 23 </p>
24 24
25 <h2 id="todomvc">Import an existing Todo app</h2> 25 <h2 id="todomvc">Import an existing Todo app</h2>
26 26
27 <p>As a starting point, we will import the <a href="http://todomvc.com/vanilla-e xamples/vanillajs/">vanilla 27 <p>As a starting point, import the <a href="http://todomvc.com/vanilla-examples/ vanillajs/">vanilla
28 JavaScript version</a> of <a href="http://todomvc.com/">TodoMVC</a>, a common be nchmark app, into our project.</p> 28 JavaScript version</a> of <a href="http://todomvc.com/">TodoMVC</a>, a common be nchmark app, into your project.</p>
29 29
30 <p>We've included a version of the TodoMVC app in the 30 <p>We've included a version of the TodoMVC app in the
31 <a href="https://github.com/mangini/io13-codelab/archive/master.zip">reference c ode zip</a> in the <strong><em>todomvc</em></strong> folder. 31 <a href="https://github.com/mangini/io13-codelab/archive/master.zip">reference c ode zip</a> in the <strong><em>todomvc</em></strong> folder.
32 Copy all files (including folders) from <em>todomvc</em> into your project folde r.</p> 32 Copy all files (including folders) from <em>todomvc</em> into your project folde r.</p>
33 33
34 <figure> 34 <figure>
35 <img src="{{static}}/images/app_codelab/copy-todomvc.png" alt="Copy todomvc fo lder into codelab folder"> 35 <img src="{{static}}/images/app_codelab/copy-todomvc.png" alt="Copy todomvc fo lder into codelab folder">
36 </figure> 36 </figure>
37 37
38 <p>You will be asked to replace <em>index.html</em>. Go ahead and accept.</p> 38 <p>You will be asked to replace <em>index.html</em>. Go ahead and accept.</p>
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
100 &lt;script src="js/store.js">&lt;/script> 100 &lt;script src="js/store.js">&lt;/script>
101 </pre> 101 </pre>
102 102
103 <p>2. Create a file in the <strong><em>js</em></strong> folder named <strong><em >bootstrap.js</em></strong>. Move the previously inline code to be in this file: </p> 103 <p>2. Create a file in the <strong><em>js</em></strong> folder named <strong><em >bootstrap.js</em></strong>. Move the previously inline code to be in this file: </p>
104 104
105 <pre data-filename="bootstrap.js"> 105 <pre data-filename="bootstrap.js">
106 // Bootstrap app data 106 // Bootstrap app data
107 window.app = {}; 107 window.app = {};
108 </pre> 108 </pre>
109 109
110 <p>You'll still have a non-working Todo app if you reload the app now but we're getting there.</p> 110 <p>You'll still have a non-working Todo app if you reload the app now but you're getting closer.</p>
111 111
112 <h2 id="convert-storage">Convert localStorage to chrome.storage.local</h2> 112 <h2 id="convert-storage">Convert localStorage to chrome.storage.local</h2>
113 113
114 <p>If you open the DevTools Console now, the previous error should be gone. Howe ver there is a new error about <code>window.localStorage</code> not being availa ble:</p> 114 <p>If you open the DevTools Console now, the previous error should be gone. Ther e is a new error, however, about <code>window.localStorage</code> not being avai lable:</p>
115 115
116 <figure> 116 <figure>
117 <img src="{{static}}/images/app_codelab/localStorage-console-error.png" alt="T odo app with localStorage console log error"> 117 <img src="{{static}}/images/app_codelab/localStorage-console-error.png" alt="T odo app with localStorage console log error">
118 <!-- 118 <!--
119 <blockquote> 119 <blockquote>
120 > Uncaught window.localStorage is not available in packaged apps. Use <br> 120 > Uncaught window.localStorage is not available in packaged apps. Use <br>
121 > chrome.storage.local instead. store.js:21 121 > chrome.storage.local instead. store.js:21
122 </blockquote> 122 </blockquote>
123 --> 123 -->
124 </figure> 124 </figure>
125 125
126 <p><a href="http://dev.w3.org/html5/webstorage/#the-localstorage-attribute"><cod e>LocalStorage</code></a> 126 <p>Chrome Apps do not support
127 is not supported in Chrome Apps because <code>LocalStorage</code> is 127 <a href="http://dev.w3.org/html5/webstorage/#the-localstorage-attribute"><code>l ocalStorage</code></a>
128 synchronous. Synchronous access to blocking resources (I/O) in a single threaded 128 as <code>localStorage</code> is synchronous.
129 runtime could make your app become unresponsive.</p> 129 Synchronous access to blocking resources (I/O) in a single-threaded runtime
130 could make your app unresponsive.</p>
130 131
131 <p>Chrome Apps have an equivalent API that can store objects asynchronously. 132 <p>Chrome Apps have an equivalent API that can store objects asynchronously.
132 This will help avoid the sometimes costly object->string->object serialization p rocess.</p> 133 This will help avoid the sometimes costly object->string->object serialization p rocess.</p>
133 134
134 <p>To address the error message in our app, we will need to convert <code>LocalS torage</code> to 135 <p>To address the error message in our app, you need to convert <code>localStora ge</code> to
135 <code>chrome.storage.local</code>.</p> 136 <a href="/apps/storage" title="Read 'chrome.storage.local' in the Chrome develop er docs">chrome.storage.local</a>.</p>
136 137
137 <h3 id="update-permissions">Update app permissions</h3> 138 <h3 id="update-permissions">Update app permissions</h3>
138 139
139 <p>In order to use <code>chrome.storage.local</code>, we need to request the <co de>storage</code> permission. In <strong><em>manifest.json</em></strong>, add <c ode>"storage"</code> to the <code>permissions</code> array:</p> 140 <p>In order to use <code>chrome.storage.local</code>, you need to request the <c ode>storage</code> permission. In <strong><em>manifest.json</em></strong>, add < code>"storage"</code> to the <code>permissions</code> array:</p>
140 141
141 <pre data-filename="manifest.json"> 142 <pre data-filename="manifest.json">
142 "permissions": [<b>"storage"</b>], 143 "permissions": [<b>"storage"</b>],
143 </pre> 144 </pre>
144 145
145 <h3 id="get-and-set">Learn about local.storage.set() and local.storage.get()</h3 > 146 <h3 id="get-and-set">Learn about local.storage.set() and local.storage.get()</h3 >
146 147
147 <p>To save and retrieve todo items, we'll need to know a bit about the <code>set ()</code> and <code>get()</code> methods of the <code>chrome.storage</code> API. </p> 148 <p>To save and retrieve todo items, you need to know about the <code>set()</code > and <code>get()</code> methods of the <code>chrome.storage</code> API.</p>
148 149
149 <p>The <code>set()</code> method accepts an object of key-value pairs as its fir st parameter. An optional callback function is the second parameter. For example :</p> 150 <p>The <a href="/apps/storage#method-StorageArea-set" title="Read 'chrome.storag e.local.set()' in the Chrome developer docs">set()</a>
151 method accepts an object of key-value pairs as its first parameter. An optional callback function is the second parameter. For example:</p>
150 152
151 <pre> 153 <pre>
152 chrome.storage.local.set({secretMessage:'Psst!',timeSet:Date.now()}, function() { 154 chrome.storage.local.set({secretMessage:'Psst!',timeSet:Date.now()}, function() {
153 console.log("Secret message saved"); 155 console.log("Secret message saved");
154 }); 156 });
155 </pre> 157 </pre>
156 158
157 <p>The <code>get()</code> method accepts an optional first parameter for the dat astore keys you wish to retreive. A single key can be passed as a string; multip le keys can be arranged into an array of strings or a dictionary object.</p> 159 <p>The <a href="/apps/storage#method-StorageArea-get" title="Read 'chrome.storag e.local.get()' in the Chrome developer docs">get()</a> method accepts an optiona l first parameter for the datastore keys you wish to retreive. A single key can be passed as a string; multiple keys can be arranged into an array of strings or a dictionary object.</p>
158 160
159 <p>The second parameter, which is required, is a callback function. In the retur ned object, use the keys requested in the first parameter to access the stored v alues. For example:</p> 161 <p>The second parameter, which is required, is a callback function. In the retur ned object, use the keys requested in the first parameter to access the stored v alues. For example:</p>
160 162
161 <pre> 163 <pre>
162 chrome.storage.local.get(['secretMessage','timeSet'], function(data) { 164 chrome.storage.local.get(['secretMessage','timeSet'], function(data) {
163 console.log("The secret message:", data.secretMessage, "saved at:", data.timeS et); 165 console.log("The secret message:", data.secretMessage, "saved at:", data.timeS et);
164 }); 166 });
165 </pre> 167 </pre>
166 168
167 <p>If you want to <code>get()</code> everything that is currently in <code>chrom e.storage.local</code>, 169 <p>If you want to <code>get()</code> everything that is currently in <code>chrom e.storage.local</code>,
168 omit the first parameter:</p> 170 omit the first parameter:</p>
169 171
170 <pre> 172 <pre>
171 chrome.storage.local.get(function(data) { 173 chrome.storage.local.get(function(data) {
172 console.log(data); 174 console.log(data);
173 }); 175 });
174 </pre> 176 </pre>
175 177
176 <p>Unlike <code>localStorage</code>, you won't be able to inspect locally stored items using the DevTools Resources panel. However, you can interact with <code> chrome.storage</code> from the JavaScript Console like so:</p> 178 <p>Unlike <code>localStorage</code>, you won't be able to inspect locally stored items using the DevTools Resources panel. You can, however, interact with <code >chrome.storage</code> from the JavaScript Console like so:</p>
177 179
178 <figure> 180 <figure>
179 <img src="{{static}}/images/app_codelab/get-set-in-console.png" alt="Use the C onsole to debug chrome.storage"> 181 <img src="{{static}}/images/app_codelab/get-set-in-console.png" alt="Use the C onsole to debug chrome.storage">
180 </figure> 182 </figure>
181 183
182 <h3 id="preview-changes">Preview required API changes</h3> 184 <h3 id="preview-changes">Preview required API changes</h3>
183 185
184 <p>There are many remaining steps in converting the Todo app however they are al l small changes to 186 <p>Most of the remaining steps in converting the Todo app are small changes
185 the API calls. Changing all the places where <code>localStorage</code> is curren tly being used 187 to the API calls. Changing all the places where <code>localStorage</code>
186 will be time-consuming and error-prone &mdash; but required.</p> 188 is currently being used, though time-consuming and error-prone, is required.</p>
187 189
188 <p class="note"> 190 <p class="note">
189 To maximize your fun with this codelab, it'll be best if you overwrite your 191 To maximize your fun with this codelab, it'll be best if you overwrite your
190 <strong><em>store.js</em></strong>, <strong><em>controller.js</em></strong>, a nd <strong><em>model.js</em></strong> 192 <strong><em>store.js</em></strong>, <strong><em>controller.js</em></strong>, a nd <strong><em>model.js</em></strong>
191 with the ones from <strong><em>cheat_code/solution_for_step_2</em></strong> in the reference code zip. 193 with the ones from <strong><em>cheat_code/solution_for_step_2</em></strong> in the reference code zip.
192 <br><br> 194 <br><br>
193 Once you've done that, continue reading as we'll go over each of the changes i ndividually. 195 Once you've done that, continue reading as we'll go over each of the changes i ndividually.
194 </p> 196 </p>
195 197
196 <p>The key differences between <code>localStorage</code> and <code>chrome.storag e</code> come from the async nature of <code>chrome.storage</code>:</p> 198 <p>The key differences between <code>localStorage</code> and <code>chrome.storag e</code> come from the async nature of <code>chrome.storage</code>:</p>
197 199
198 <ul> 200 <ul>
199 <li> 201 <li>
200 Instead of writing to <code>localStorage</code> using simple assignment, we need to use <code>chrome.storage.local.set()</code> with optional callbacks. 202 Instead of writing to <code>localStorage</code> using simple assignment, you need to use <code>chrome.storage.local.set()</code> with optional callbacks.
201 <pre> 203 <pre>
202 var data = { todos: [] }; 204 var data = { todos: [] };
203 localStorage[dbName] = JSON.stringify(data); 205 localStorage[dbName] = JSON.stringify(data);
204 </pre> 206 </pre>
205 versus 207 versus
206 <pre> 208 <pre>
207 var storage = {}; 209 var storage = {};
208 storage[dbName] = { todos: [] }; 210 storage[dbName] = { todos: [] };
209 chrome.storage.local.set( storage, function() { 211 chrome.storage.local.set( storage, function() {
210 // optional callback 212 // optional callback
211 }); 213 });
212 </pre> 214 </pre>
213 </li> 215 </li>
214 <li> 216 <li>
215 Instead of accessing <code>localStorage[myStorageName]</code> directly, we n eed to use <code>chrome.storage.local.get(myStorageName,function(storage){...})< /code> and then parse the returned <code>storage</code> object in the callback f unction. 217 Instead of accessing <code>localStorage[myStorageName]</code> directly, you need to use <code>chrome.storage.local.get(myStorageName,function(storage){...}) </code> and then parse the returned <code>storage</code> object in the callback function.
216 <pre> 218 <pre>
217 var todos = JSON.parse(localStorage[dbName]).todos; 219 var todos = JSON.parse(localStorage[dbName]).todos;
218 </pre> 220 </pre>
219 versus 221 versus
220 <pre> 222 <pre>
221 chrome.storage.local.get(dbName, function(storage) { 223 chrome.storage.local.get(dbName, function(storage) {
222 var todos = storage[dbName].todos; 224 var todos = storage[dbName].todos;
223 }); 225 });
224 </pre> 226 </pre>
225 </li> 227 </li>
226 <li> 228 <li>
227 The use of <code>.bind(this)</code> is being used on all callbacks to ensure <code>this</code> refers to the <code>this</code> of the <code>Store</code> pro totype. (More info on bound functions can be found on the MDN docs: <a href="htt ps://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Fu nction/bind">Function.prototype.bind()</a>.) 229 The function <code>.bind(this)</code> is used on all callbacks to ensure <co de>this</code> refers to the <code>this</code> of the <code>Store</code> prototy pe. (More info on bound functions can be found on the MDN docs: <a href="https:/ /developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Functi on/bind">Function.prototype.bind()</a>.)
228 <pre> 230 <pre>
229 function Store() { 231 function Store() {
230 this.scope = 'inside Store'; 232 this.scope = 'inside Store';
231 chrome.storage.local.set( {}, function() { 233 chrome.storage.local.set( {}, function() {
232 console.log(this.scope); // outputs: 'undefined' 234 console.log(this.scope); // outputs: 'undefined'
233 }); 235 });
234 } 236 }
235 new Store(); 237 new Store();
236 </pre> 238 </pre>
237 versus 239 versus
238 <pre> 240 <pre>
239 function Store() { 241 function Store() {
240 this.scope = 'inside Store'; 242 this.scope = 'inside Store';
241 chrome.storage.local.set( {}, function() { 243 chrome.storage.local.set( {}, function() {
242 console.log(this.scope); // outputs: 'inside Store' 244 console.log(this.scope); // outputs: 'inside Store'
243 }<b>.bind(this)</b>); 245 }<b>.bind(this)</b>);
244 } 246 }
245 new Store(); 247 new Store();
246 </pre> 248 </pre>
247 </li> 249 </li>
248 </ul> 250 </ul>
249 251
250 <p>Keep these key differences in mind as we go over retrieving, saving, and remo ving todo items in the following sections.</p> 252 <p>Keep these key differences in mind as we cover retrieving, saving, and removi ng todo items in the following sections.</p>
251 253
252 <h3 id="retrieve-items">Retrieve todos items</h3> 254 <h3 id="retrieve-items">Retrieve todo items</h3>
253 255
254 Let's update the Todo app in order to retrieve todo items: 256 Let's update the Todo app in order to retrieve todo items:
255 257
256 <p>1. The <code>Store</code> constructor method takes care of initializing the T odo app with all the existing todo items from the datastore. If this is the firs t time the app has been loaded, the datastore might not exist so the method chec ks if the datastore exists first. If it doesn't, it'll create an empty array of <code>todos</code> and save it to the datastore so there are no runtime read err ors.</p> 258 <p>1. The <code>Store</code> constructor method takes care of initializing the T odo app with all the existing todo items from the datastore.
259 The method first checks if the datastore exists.
260 If it doesn't, it'll create an empty array of <code>todos</code> and save it to the datastore so there are no runtime read errors.</p>
257 261
258 <p>In <strong><em>js/store.js</em></strong>, convert the use of <code>localStora ge</code> in the constructor method to instead use 262 <p>In <strong><em>js/store.js</em></strong>, convert the use of <code>localStora ge</code> in the constructor method to instead use
259 <code>chrome.storage.local</code>:</p> 263 <code>chrome.storage.local</code>:</p>
260 264
261 <pre data-filename="store.js"> 265 <pre data-filename="store.js">
262 function Store(name, callback) { 266 function Store(name, callback) {
263 var data; 267 var data;
264 var dbName; 268 var dbName;
265 269
266 callback = callback || function () {}; 270 callback = callback || function () {};
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after
438 chrome.storage.local.set(storage, function() { 442 chrome.storage.local.set(storage, function() {
439 callback.call(this, [updateData]); 443 callback.call(this, [updateData]);
440 }.bind(this)); 444 }.bind(this));
441 } 445 }
442 }.bind(this)); 446 }.bind(this));
443 }; 447 };
444 </pre> 448 </pre>
445 449
446 <h3 id="complete-items">Mark todo items as complete</h3> 450 <h3 id="complete-items">Mark todo items as complete</h3>
447 451
448 <p>Now that we are operating on arrays, we'll need to change how we handle a use r clicking on the <b>Clear completed (#)</b> button:</p> 452 <p>Now that app is operating on arrays, you need to change how the app handles a user clicking on the <b>Clear completed (#)</b> button:</p>
449 453
450 <p>1. In <strong><em>controller.js</em></strong>, update <code>toggleAll()</code > to call <code>toggleComplete()</code> 454 <p>1. In <strong><em>controller.js</em></strong>, update <code>toggleAll()</code > to call <code>toggleComplete()</code>
451 only once with an array of todos instead of marking a todo as completed 455 only once with an array of todos instead of marking a todo as completed
452 one by one. Also delete the call to <code>_filter()</code> since we'll be adjust ing <code>toggleComplete</code>'s <code>_filter()</code>.</p> 456 one by one. Also delete the call to <code>_filter()</code> since you'll be adjus ting
457 the <code>toggleComplete</code> <code>_filter()</code>.</p>
453 458
454 <pre data-filename="controller.js"> 459 <pre data-filename="controller.js">
455 Controller.prototype.toggleAll = function (e) { 460 Controller.prototype.toggleAll = function (e) {
456 var completed = e.target.checked ? 1 : 0; 461 var completed = e.target.checked ? 1 : 0;
457 var query = 0; 462 var query = 0;
458 if (completed === 0) { 463 if (completed === 0) {
459 query = 1; 464 query = 1;
460 } 465 }
461 this.model.read({ completed: query }, function (data) { 466 this.model.read({ completed: query }, function (data) {
462 <b>var ids = [];</b> 467 <b>var ids = [];</b>
463 data.forEach(function (item) { 468 data.forEach(function (item) {
464 <strike>this.toggleComplete(item.id, e.target, true);</strike> 469 <strike>this.toggleComplete(item.id, e.target, true);</strike>
465 <b>ids.push(item.id);</b> 470 <b>ids.push(item.id);</b>
466 }.bind(this)); 471 }.bind(this));
467 <b>this.toggleComplete(ids, e.target, false);</b> 472 <b>this.toggleComplete(ids, e.target, false);</b>
468 }.bind(this)); 473 }.bind(this));
469 474
470 <strike>this._filter();</strike> 475 <strike>this._filter();</strike>
471 }; 476 };
472 </pre> 477 </pre>
473 478
474 <p>2. Now we need to update <code>toggleComplete()</code> to accept both a singl e todo or an array of todos. This includes moving <code>filter()</code> to be in side the <code>update()</code>, instead of outside.</p> 479 <p>2. Now update <code>toggleComplete()</code> to accept both a single todo or a n array of todos. This includes moving <code>filter()</code> to be inside the <c ode>update()</code>, instead of outside.</p>
475 480
476 <pre data-filename="controller.js"> 481 <pre data-filename="controller.js">
477 Controller.prototype.toggleComplete = function (<strike>id</strike> <b>ids</b>, checkbox, silent) { 482 Controller.prototype.toggleComplete = function (<strike>id</strike> <b>ids</b>, checkbox, silent) {
478 var completed = checkbox.checked ? 1 : 0; 483 var completed = checkbox.checked ? 1 : 0;
479 this.model.update(<strike>id</strike> <b>ids</b>, { completed: completed }, fu nction () { 484 this.model.update(<strike>id</strike> <b>ids</b>, { completed: completed }, fu nction () {
480 <b>if ( ids.constructor != Array ) {</b> 485 <b>if ( ids.constructor != Array ) {</b>
481 <b> ids = [ ids ];</b> 486 <b> ids = [ ids ];</b>
482 <b>}</b> 487 <b>}</b>
483 <b>ids.forEach( function(id) {</b> 488 <b>ids.forEach( function(id) {</b>
484 var listItem = $$('[data-id="' + id + '"]'); 489 var listItem = $$('[data-id="' + id + '"]');
(...skipping 15 matching lines...) Expand all
500 }<b>.bind(this)</b>); 505 }<b>.bind(this)</b>);
501 506
502 <strike>if (!silent) {</strike> 507 <strike>if (!silent) {</strike>
503 <strike> this._filter();</strike> 508 <strike> this._filter();</strike>
504 <strike>}</strike> 509 <strike>}</strike>
505 }; 510 };
506 </pre> 511 </pre>
507 512
508 <h3 id="count-items">Count todo items</h3> 513 <h3 id="count-items">Count todo items</h3>
509 514
510 <p>After switching to async storage, there is a minor bug that shows up when get ting the number of todos. We'll need to wrap the count operation in a callback f unction:</p> 515 <p>After switching to async storage, there is a minor bug that shows up when get ting the number of todos. You'll need to wrap the count operation in a callback function:</p>
511 516
512 <p>1. In <strong><em>model.js</em></strong>, update <code>getCount()</code> to a ccept a callback:</p> 517 <p>1. In <strong><em>model.js</em></strong>, update <code>getCount()</code> to a ccept a callback:</p>
513 518
514 <pre data-filename="model.js"> 519 <pre data-filename="model.js">
515 Model.prototype.getCount = function (<b>callback</b>) { 520 Model.prototype.getCount = function (<b>callback</b>) {
516 var todos = { 521 var todos = {
517 active: 0, 522 active: 0,
518 completed: 0, 523 completed: 0,
519 total: 0 524 total: 0
520 }; 525 };
(...skipping 25 matching lines...) Expand all
546 <b> </b>this.$clearCompleted.style.display = todos.completed > 0 ? 'block' : 'none'; 551 <b> </b>this.$clearCompleted.style.display = todos.completed > 0 ? 'block' : 'none';
547 <b> </b> 552 <b> </b>
548 <b> </b>this.$toggleAll.checked = todos.completed === todos.total; 553 <b> </b>this.$toggleAll.checked = todos.completed === todos.total;
549 <b> </b> 554 <b> </b>
550 <b> </b>this._toggleFrame(todos); 555 <b> </b>this._toggleFrame(todos);
551 <b>}.bind(this));</b> 556 <b>}.bind(this));</b>
552 557
553 }; 558 };
554 </pre> 559 </pre>
555 560
556 <p>We are almost there! If you reload the app now, you will be able to insert ne w 561 <p>You are almost there! If you reload the app now, you will be able to insert n ew
557 todos without any console errors.</p> 562 todos without any console errors.</p>
558 563
559 <h3 id="remove-items">Remove todos items</h3> 564 <h3 id="remove-items">Remove todos items</h3>
560 565
561 <p>Now that we can save todo items, we're close to being done! 566 <p>Now that the app can save todo items, you're close to being done!
562 However we get errors when we attempt to <em>remove</em> our todo items:</p> 567 You still get errors when you attempt to <em>remove</em> todo items:</p>
563 568
564 <figure> 569 <figure>
565 <img src="{{static}}/images/app_codelab/remove-todo-console-error.png" alt="To do app with localStorage console log error"> 570 <img src="{{static}}/images/app_codelab/remove-todo-console-error.png" alt="To do app with localStorage console log error">
566 </figure> 571 </figure>
567 572
568 <p>1. In <strong><em>store.js</em></strong>, we'll need to convert all the <code >localStorage</code> instances to use <code>chrome.storage.local</code>:</p> 573 <p>1. In <strong><em>store.js</em></strong>, convert all the <code>localStorage< /code> instances to use <code>chrome.storage.local</code>:</p>
569 574
570 <p>a) To start off, wrap everything already inside <code>remove()</code> with a <code>get()</code> callback:</p> 575 <p>a) To start off, wrap everything already inside <code>remove()</code> with a <code>get()</code> callback:</p>
571 576
572 <pre data-filename="store.js"> 577 <pre data-filename="store.js">
573 Store.prototype.remove = function (id, callback) { 578 Store.prototype.remove = function (id, callback) {
574 <b>chrome.storage.local.get(this._dbName, function(storage) {</b> 579 <b>chrome.storage.local.get(this._dbName, function(storage) {</b>
575 <b> </b>var data = JSON.parse(localStorage[this._dbName]); 580 <b> </b>var data = JSON.parse(localStorage[this._dbName]);
576 <b> </b>var todos = data.todos; 581 <b> </b>var todos = data.todos;
577 <b> </b> 582 <b> </b>
578 <b> </b>for (var i = 0; i < todos.length; i++) { 583 <b> </b>for (var i = 0; i < todos.length; i++) {
(...skipping 28 matching lines...) Expand all
607 <strike>localStorage[this._dbName] = JSON.stringify(data);</strike> 612 <strike>localStorage[this._dbName] = JSON.stringify(data);</strike>
608 <strike>callback.call(this, JSON.parse(localStorage[this._dbName]).todos);</ strike> 613 <strike>callback.call(this, JSON.parse(localStorage[this._dbName]).todos);</ strike>
609 <b>chrome.storage.local.set(storage, function() {</b> 614 <b>chrome.storage.local.set(storage, function() {</b>
610 <b> callback.call(this, todos);</b> 615 <b> callback.call(this, todos);</b>
611 <b>}.bind(this));</b> 616 <b>}.bind(this));</b>
612 }.bind(this)); 617 }.bind(this));
613 }; 618 };
614 </pre> 619 </pre>
615 620
616 <p>2. The same Read-After-Write data hazard issue previously present in the 621 <p>2. The same Read-After-Write data hazard issue previously present in the
617 <code>save()</code> method is also present when removing items so we'll need 622 <code>save()</code> method is also present when removing items so you will need
618 to update a few more places to allow for batch operations on a list of todo IDs. </p> 623 to update a few more places to allow for batch operations on a list of todo IDs. </p>
619 624
620 <p>a) Still in <em>store.js</em>, update <code>remove()</code>:</p> 625 <p>a) Still in <em>store.js</em>, update <code>remove()</code>:</p>
621 626
622 <pre data-filename="store.js"> 627 <pre data-filename="store.js">
623 Store.prototype.remove = function (id, callback) { 628 Store.prototype.remove = function (id, callback) {
624 chrome.storage.local.get(this._dbName, function(storage) { 629 chrome.storage.local.get(this._dbName, function(storage) {
625 var data = storage[this._dbName]; 630 var data = storage[this._dbName];
626 var todos = data.todos; 631 var todos = data.todos;
627 632
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
698 <figure> 703 <figure>
699 <img src="{{static}}/images/app_codelab/step2-completed.gif" alt="The finished Todo app after Step 2"> 704 <img src="{{static}}/images/app_codelab/step2-completed.gif" alt="The finished Todo app after Step 2">
700 </figure> 705 </figure>
701 706
702 <p class="note"> 707 <p class="note">
703 <strong>Troubleshooting</strong> 708 <strong>Troubleshooting</strong>
704 <br> 709 <br>
705 Remember to always check the DevTools Console to see if there are any error me ssages. 710 Remember to always check the DevTools Console to see if there are any error me ssages.
706 </p> 711 </p>
707 712
708 <h2 id="recap">Recap APIs referenced in this step</h2> 713 <h2 id="recap">For more information</h2>
709 714
710 <p>For more detailed information about some of the APIs introduced in this step, refer to:</p> 715 <p>For more detailed information about some of the APIs introduced in this step, refer to:</p>
711 716
712 <ul> 717 <ul>
713 <li> 718 <li>
714 <a href="/apps/contentSecurityPolicy" title="Read 'Content Security Policy' in the Chrome developer docs">Content Security Policy</a> 719 <a href="/apps/contentSecurityPolicy" title="Read 'Content Security Policy' in the Chrome developer docs">Content Security Policy</a>
715 <a href="#csp-compliance" class="anchor-link-icon" title="This feature menti oned in 'Make scripts Content Security Policy (CSP) compliant'">&#8593;</a> 720 <a href="#csp-compliance" class="anchor-link-icon" title="This feature menti oned in 'Make scripts Content Security Policy (CSP) compliant'">&#8593;</a>
716 </li> 721 </li>
717 <li> 722 <li>
718 <a href="/apps/declare_permissions" title="Read 'Declare Permissions' in the Chrome developer docs">Declare Permissions</a> 723 <a href="/apps/declare_permissions" title="Read 'Declare Permissions' in the Chrome developer docs">Declare Permissions</a>
719 <a href="#update-permissions" class="anchor-link-icon" title="This feature m entioned in 'Update app permissions'">&#8593;</a> 724 <a href="#update-permissions" class="anchor-link-icon" title="This feature m entioned in 'Update app permissions'">&#8593;</a>
720 </li> 725 </li>
721 <li> 726 <li>
722 <a href="/apps/storage" title="Read 'chrome.storage' in the Chrome developer docs">chrome.storage</a> 727 <a href="/apps/storage" title="Read 'chrome.storage' in the Chrome developer docs">chrome.storage</a>
723 <a href="#get-and-set" class="anchor-link-icon" title="This feature mentione d in 'Learn about local.storage.set() and local.storage.get()'">&#8593;</a> 728 <a href="#get-and-set" class="anchor-link-icon" title="This feature mentione d in 'Learn about local.storage.set() and local.storage.get()'">&#8593;</a>
724 </li> 729 </li>
725 <li> 730 <li>
726 <a href="/apps/storage#method-StorageArea-get" title="Read 'chrome.storage.l ocal.get()' in the Chrome developer docs">chrome.storage.local.get()</a> 731 <a href="/apps/storage#method-StorageArea-get" title="Read 'chrome.storage.l ocal.get()' in the Chrome developer docs">chrome.storage.local.get()</a>
727 <a href="#get-and-set" class="anchor-link-icon" title="This feature mentione d in 'Learn about local.storage.set() and local.storage.get()'">&#8593;</a>
728 <a href="#retrieve-items" class="anchor-link-icon" title="This feature menti oned in 'Retrieve todos items'">&#8593;</a> 732 <a href="#retrieve-items" class="anchor-link-icon" title="This feature menti oned in 'Retrieve todos items'">&#8593;</a>
729 </li> 733 </li>
730 <li> 734 <li>
731 <a href="/apps/storage#method-StorageArea-set" title="Read 'chrome.storage.l ocal.set()' in the Chrome developer docs">chrome.storage.local.set()</a> 735 <a href="/apps/storage#method-StorageArea-set" title="Read 'chrome.storage.l ocal.set()' in the Chrome developer docs">chrome.storage.local.set()</a>
732 <a href="/apps/storage#method-StorageArea-get" title="Read 'chrome.storage.l ocal.get()' in the Chrome developer docs">chrome.storage.local.get()</a>
733 <a href="#save-items" class="anchor-link-icon" title="This feature mentioned in 'Save todos items'">&#8593;</a> 736 <a href="#save-items" class="anchor-link-icon" title="This feature mentioned in 'Save todos items'">&#8593;</a>
734 </li> 737 </li>
735 <li> 738 <li>
736 <a href="/apps/storage#method-StorageArea-remove" title="Read 'chrome.storag e.local.remove()' in the Chrome developer docs">chrome.storage.local.remove()</a > 739 <a href="/apps/storage#method-StorageArea-remove" title="Read 'chrome.storag e.local.remove()' in the Chrome developer docs">chrome.storage.local.remove()</a >
737 <a href="#remove-items" class="anchor-link-icon" title="This feature mention ed in 'Remove todos items'">&#8593;</a> 740 <a href="#remove-items" class="anchor-link-icon" title="This feature mention ed in 'Remove todos items'">&#8593;</a>
738 </li> 741 </li>
739 <li> 742 <li>
740 <a href="/apps/storage#method-StorageArea-remove" title="Read 'chrome.storag e.local.clear()' in the Chrome developer docs">chrome.storage.local.clear()</a> 743 <a href="/apps/storage#method-StorageArea-remove" title="Read 'chrome.storag e.local.clear()' in the Chrome developer docs">chrome.storage.local.clear()</a>
741 <a href="#remove-items" class="anchor-link-icon" title="This feature mention ed in 'Drop all todo items'">&#8593;</a> 744 <a href="#remove-items" class="anchor-link-icon" title="This feature mention ed in 'Drop all todo items'">&#8593;</a>
742 </li> 745 </li>
743 </ul> 746 </ul>
744 747
745 <p>Ready to continue onto the next step? Go to <a href="app_codelab_alarms.html" >Step 3 - Add alarms and notifications &raquo;</a></p> 748 <p>Ready to continue onto the next step? Go to <a href="app_codelab_alarms.html" >Step 3 - Add alarms and notifications &raquo;</a></p>
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698