OLD | NEW |
1 <h1 id="add-images"> | 1 <h1 id="add-images"> |
2 <span class="h1-step">Step 5:</span> | 2 <span class="h1-step">Step 5:</span> |
3 Add Images From the Web | 3 Add Images From the Web |
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_step4</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_step4</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 load resources from outside your app and add them to the DOM throug
h XHR and ObjectURLs.</li> | 14 <li>How to load resources from outside your app and add them to the DOM throug
h XHR and ObjectURLs.</li> |
15 </ul> | 15 </ul> |
16 | 16 |
17 <p> | 17 <p> |
18 <em>Estimated time to complete this step: 20 minutes.</em> | 18 <em>Estimated time to complete this step: 20 minutes.</em> |
19 <br> | 19 <br> |
20 To preview what you will complete in this step, <a href="#launch">jump down to
the bottom of this page ↓</a>. | 20 To preview what you will complete in this step, <a href="#launch">jump down to
the bottom of this page ↓</a>. |
21 </p> | 21 </p> |
22 | 22 |
23 <h2 id="csp-compliance">Learn how CSP affects the use of external web resources<
/h2> | 23 <h2 id="csp-compliance">How CSP affects the use of external resources</h2> |
24 | 24 |
25 <p>The Chrome Apps platform forces your app to be fully compliant with Content | 25 <p>The Chrome Apps platform forces your app to be fully compliant with Content |
26 Security Policies (CSP). This includes not being able to directly load DOM | 26 Security Policies (CSP). You can't directly load DOM |
27 resources like images, fonts, and CSS from outside of your packaged app.</p> | 27 resources like images, fonts, and CSS from outside of your Chrome App package.</
p> |
28 | 28 |
29 <p>If you want to show an external image in your app, you need to request it via
<a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest">XMLHt
tpRequest</a>, | 29 <p>If you want to show an external image in your app, you need to request it via
<a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest">XMLHt
tpRequest</a>, |
30 transform it into a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Bl
ob">Blob</a>, and create an <a href="https://developer.mozilla.org/en-US/docs/We
b/API/URL.createObjectURL">ObjectURL</a>. This ObjectURL can then be | 30 transform it into a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Bl
ob">Blob</a>, and create an <a href="https://developer.mozilla.org/en-US/docs/We
b/API/URL.createObjectURL">ObjectURL</a>. This <code>ObjectURL</code> can be add
ed to the DOM |
31 added to the DOM because it refers to an in-memory item in the context of the | 31 because it refers to an in-memory item in the context of the app.</p> |
32 app.</p> | |
33 | 32 |
34 <h2 id="show-images">Show thumbnail images for todo items</h2> | 33 <h2 id="show-images">Show thumbnail images for todo items</h2> |
35 | 34 |
36 <p>Let's change our app to look for image URLs in a todo item. If the URL | 35 <p>Let's change our app to look for image URLs in a todo item. |
37 looks like an image (e.g. ends with .png, .jpg, .svg, or .gif), we will apply th
e | 36 If the URL looks like an image (for example, ends with .png, .jpg, .svg, or .gif
), |
38 process mentioned above in order to show an image thumbnail next to the URL.</p> | 37 apply the process mentioned above in order to show an image thumbnail next to th
e URL.</p> |
39 | 38 |
40 <h3 id="update-permissions">Update permissions</h3> | 39 <h3 id="update-permissions">Update permissions</h3> |
41 | 40 |
42 <p>In a Chrome App you can make XMLHttpRequest calls to any URL as long as you | 41 <p>In a Chrome App, you can make XMLHttpRequest calls to any URL as long as you |
43 whitelist its domain in the manifest. Since we won't know | 42 whitelist its domain in the manifest. Since you won't know |
44 beforehand what image URL the user will type, | 43 beforehand what image URL the user will type, |
45 we will ask permission to make requests to <code>"<all_urls>"</code>.</p> | 44 ask permission to make requests to <code>"<all_urls>"</code>.</p> |
46 | 45 |
47 <p>In <strong><em>manifest.json</em></strong>, request the "<all_urls>" pe
rmission:</p> | 46 <p>In <strong><em>manifest.json</em></strong>, request the "<all_urls>" pe
rmission:</p> |
48 | 47 |
49 <pre data-filename="manifest.json"> | 48 <pre data-filename="manifest.json"> |
50 "permissions": ["storage", "alarms", "notifications", | 49 "permissions": ["storage", "alarms", "notifications", |
51 "webview"<b>, "<all_urls>"</b>], | 50 "webview"<b>, "<all_urls>"</b>], |
52 </pre> | 51 </pre> |
53 | 52 |
54 <h3 id="object-urls">Create and clear ObjectURLs</h3> | 53 <h3 id="object-urls">Create and clear ObjectURLs</h3> |
55 | 54 |
56 In <strong><em>controller.js</em></strong>, add a <code>_createObjectURL()</code
> method to create ObjectURLs from a Blob: | 55 In <strong><em>controller.js</em></strong>, add a <code>_createObjectURL()</code
> method to create ObjectURLs from a Blob: |
57 | 56 |
58 <pre data-filename="controller.js"> | 57 <pre data-filename="controller.js"> |
59 Controller.prototype._createObjectURL = function(blob) { | 58 Controller.prototype._createObjectURL = function(blob) { |
60 var objURL = URL.createObjectURL(blob); | 59 var objURL = URL.createObjectURL(blob); |
61 this.objectURLs = this.objectURLs || []; | 60 this.objectURLs = this.objectURLs || []; |
62 this.objectURLs.push(objURL); | 61 this.objectURLs.push(objURL); |
63 return objURL; | 62 return objURL; |
64 }; | 63 }; |
65 </pre> | 64 </pre> |
66 | 65 |
67 <p>ObjectURLs hold memory so, when you no longer need the ObjectURL, you | 66 <p>ObjectURLs hold memory, so when you no longer need the ObjectURL, you |
68 should revoke them. Add this | 67 should revoke them. Add this |
69 <code>_clearObjectURL()</code> method to <em>controller.js</em> to handle that:<
/p> | 68 <code>_clearObjectURL()</code> method to <em>controller.js</em> to handle that:<
/p> |
70 | 69 |
71 <pre data-filename="controller.js"> | 70 <pre data-filename="controller.js"> |
72 Controller.prototype._clearObjectURL = function() { | 71 Controller.prototype._clearObjectURL = function() { |
73 if (this.objectURLs) { | 72 if (this.objectURLs) { |
74 this.objectURLs.forEach(function(objURL) { | 73 this.objectURLs.forEach(function(objURL) { |
75 URL.revokeObjectURL(objURL); | 74 URL.revokeObjectURL(objURL); |
76 }); | 75 }); |
77 this.objectURLs = null; | 76 this.objectURLs = null; |
(...skipping 16 matching lines...) Expand all Loading... |
94 img.setAttribute('data-src', imageUrl); | 93 img.setAttribute('data-src', imageUrl); |
95 img.className = 'icon'; | 94 img.className = 'icon'; |
96 var objURL = this._createObjectURL(xhr.response); | 95 var objURL = this._createObjectURL(xhr.response); |
97 img.setAttribute('src', objURL); | 96 img.setAttribute('src', objURL); |
98 element.appendChild(img); | 97 element.appendChild(img); |
99 }.bind(this); | 98 }.bind(this); |
100 xhr.send(); | 99 xhr.send(); |
101 }; | 100 }; |
102 </pre> | 101 </pre> |
103 | 102 |
104 <p>On XHR load, this method will create an ObjectURL from the XHR's response, | 103 <p>On XHR load, this method creates an <code>ObjectURL</code> from the XHR's res
ponse, |
105 and add an <code><img></code> element with this ObjectURL to the DOM.</p> | 104 and adds an <code><img></code> element with this <code>ObjectURL</code> to
the DOM.</p> |
106 | 105 |
107 <h3 id="parse-urls">Parse for image URLs in todo items</h3> | 106 <h3 id="parse-urls">Parse for image URLs in todo items</h3> |
108 | 107 |
109 <p>Now add a <code>_parseForImageURLs()</code> method that finds all links not y
et processed and checks them | 108 <p>Now add a <code>_parseForImageURLs()</code> method that finds all links not y
et processed and checks them |
110 for images. For each URL that looks like an image, execute <code>_requestRemoteI
mageAndAppend()</code>: | 109 for images. For each URL that looks like an image, execute <code>_requestRemoteI
mageAndAppend()</code>: |
111 | 110 |
112 <pre data-filename="controller.js"> | 111 <pre data-filename="controller.js"> |
113 Controller.prototype._parseForImageURLs = function () { | 112 Controller.prototype._parseForImageURLs = function () { |
114 // remove old blobs to avoid memory leak: | 113 // remove old blobs to avoid memory leak: |
115 this._clearObjectURL(); | 114 this._clearObjectURL(); |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
174 ... | 173 ... |
175 label.innerHTML = this._parseForURLs(value); | 174 label.innerHTML = this._parseForURLs(value); |
176 <b>this._parseForImageURLs();</b> | 175 <b>this._parseForImageURLs();</b> |
177 } else if (value.length === 0) { | 176 } else if (value.length === 0) { |
178 ... | 177 ... |
179 } | 178 } |
180 </pre> | 179 </pre> |
181 | 180 |
182 <h3 id="css">Constrain the displayed image dimensions</h3> | 181 <h3 id="css">Constrain the displayed image dimensions</h3> |
183 | 182 |
184 <p>Finally, in <strong></em>bower_components/todomvc-common/base.css</strong></e
m>, add a CSS rule to limit | 183 <p>Finally, in <strong><em>bower_components/todomvc-common/base.css</strong></em
>, |
185 the size of the image:</p> | 184 add a CSS rule to limit the size of the image:</p> |
186 | 185 |
187 <pre data-filename="base.css"> | 186 <pre data-filename="base.css"> |
188 .thumbnail img[data-src] { | 187 .thumbnail img[data-src] { |
189 max-width: 100px; | 188 max-width: 100px; |
190 max-height: 28px; | 189 max-height: 28px; |
191 } | 190 } |
192 </pre> | 191 </pre> |
193 | 192 |
194 <h2 id="launch">Launch your finished Todo app</h2> | 193 <h2 id="launch">Launch your finished Todo app</h2> |
195 | 194 |
196 <p>You are done Step 5! Reload your app and add a todo item with a URL | 195 <p>You are done Step 5! Reload your app and add a todo item with a URL |
197 to an image hosted online. Some URLs you could use: <strong>http://goo.gl/nqHMF#
.jpg</strong> or <strong>http://goo.gl/HPBGR#.png</strong>.</p> | 196 to an image hosted online. Some URLs you could use: <strong>http://goo.gl/nqHMF#
.jpg</strong> or <strong>http://goo.gl/HPBGR#.png</strong>.</p> |
198 | 197 |
199 <p>The result should be something like this:</p> | 198 <p>The result should be something like this:</p> |
200 | 199 |
201 <figure> | 200 <figure> |
202 <img src="{{static}}/images/app_codelab/step5-completed.gif" alt="The Todo app
with image thumbnails"/> | 201 <img src="{{static}}/images/app_codelab/step5-completed.gif" alt="The Todo app
with image thumbnails"/> |
203 </figure> | 202 </figure> |
204 | 203 |
205 <p class="note"><strong>Tip</strong>: For real-world situations, when you need t
o control | 204 <p class="note"><strong>Tip</strong>: For real-world situations, when you need t
o control |
206 offline cache and dozens of simultaneous resource downloads, we have created | 205 offline cache and dozens of simultaneous resource downloads, we have created |
207 <a href="https://github.com/GoogleChrome/apps-resource-loader#readme">a helper l
ibrary</a> | 206 <a href="https://github.com/GoogleChrome/apps-resource-loader#readme">a helper l
ibrary</a> |
208 to handle some common use cases.</p> | 207 to handle some common use cases.</p> |
209 | 208 |
210 <h2 id="recap">Recap APIs referenced in this step</h2> | 209 <h2 id="recap">For more information</h2> |
211 | 210 |
212 <p>For more detailed information about some of the APIs introduced in this step,
refer to:</p> | 211 <p>For more detailed information about some of the APIs introduced in this step,
refer to:</p> |
213 | 212 |
214 <ul> | 213 <ul> |
215 <li> | 214 <li> |
216 <a href="/apps/contentSecurityPolicy" title="Read 'Content Security Policy'
in the Chrome developer docs">Content Security Policy</a> | 215 <a href="/apps/contentSecurityPolicy" title="Read 'Content Security Policy'
in the Chrome developer docs">Content Security Policy</a> |
217 <a href="#csp-compliance" class="anchor-link-icon" title="This feature menti
oned in 'Learn how CSP affects the use of external web resources'">↑</a> | 216 <a href="#csp-compliance" class="anchor-link-icon" title="This feature menti
oned in 'Learn how CSP affects the use of external web resources'">↑</a> |
218 </li> | 217 </li> |
219 <li> | 218 <li> |
220 <a href="/apps/declare_permissions" title="Read 'Declare Permissions' in the
Chrome developer docs">Declare Permissions</a> | 219 <a href="/apps/declare_permissions" title="Read 'Declare Permissions' in the
Chrome developer docs">Declare Permissions</a> |
221 <a href="#update-permissions" class="anchor-link-icon" title="This feature m
entioned in 'Update permissions'">↑</a> | 220 <a href="#update-permissions" class="anchor-link-icon" title="This feature m
entioned in 'Update permissions'">↑</a> |
222 </li> | 221 </li> |
223 </ul> | 222 </ul> |
224 | 223 |
225 <p>Ready to continue onto the next step? Go to <a href="app_codelab_filesystem.h
tml">Step 6 - Export todos to the filesystem »</a></p> | 224 <p>Ready to continue onto the next step? Go to <a href="app_codelab_filesystem.h
tml">Step 6 - Export todos to the filesystem »</a></p> |
OLD | NEW |