| Index: chrome/common/extensions/docs/templates/articles/app_codelab5_data.html
|
| diff --git a/chrome/common/extensions/docs/templates/articles/app_codelab5_data.html b/chrome/common/extensions/docs/templates/articles/app_codelab5_data.html
|
| index bf2f26a88b3ca3f9e5511fcc6b3490a5146f6d4e..d8f4fc666e899ff5137d16901569ace43bdadc2b 100644
|
| --- a/chrome/common/extensions/docs/templates/articles/app_codelab5_data.html
|
| +++ b/chrome/common/extensions/docs/templates/articles/app_codelab5_data.html
|
| @@ -1,25 +1,57 @@
|
| -<h1 id="lab_5_manage_data">Lab 5 - Manage Data</h1>
|
| +<h1 id="lab_5_manage_data">Save and Fetch Data</h1>
|
|
|
| -<p>The <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab3_mvc/angularjs/withcontroller/controller.js#L2-L4">sample from Lab 3</a> uses a static array of Todos. Every time your app restarts, whatever you've changed is lost. In this section, we will save every change using <a href="http://developer.chrome.com/trunk/apps/storage.html">chrome.storage.sync</a>. This lets you store <em>small things</em> that automatically sync to the cloud if you are online and logged in to Chrome. If you are offline or unlogged, it saves locally and transparently: you don't have to handle online check and offline fallback in your application.</p>
|
| +<p>The <a href="http://developer.chrome.com/trunk/apps/app_codelab3_mvc.html">sample from Lab 3</a>
|
| +uses a static array of Todos.
|
| +Every time your app restarts,
|
| +whatever you've changed is lost.
|
| +In this section, we will save every change using
|
| +<a href="http://developer.chrome.com/trunk/apps/storage.html">chrome.storage.sync</a>.
|
| +</p>
|
|
|
| -<h2 id="you_should_also_read">You should also read</h2>
|
| -
|
| -<p><a href="http://developer.chrome.com/apps/app_storage.html">Manage Data</a> in Chrome app docs</p>
|
| +<p>
|
| +This lets you store <em>small things</em> that automatically sync to the cloud
|
| +if you are online and logged in to Chrome.
|
| +If you are offline or unlogged, it saves locally and transparently:
|
| +you don't have to handle online check and offline fallback in your application.
|
| +</p>
|
|
|
| <h2 id="save_your_todos_in_the_cloud">Save your Todos in the cloud</h2>
|
|
|
| -<p class="note"><b>Note:</b> Chrome Sync Storage is not intended to be used as a generic database. There are several restrictions on the amount of information you can save, so it is more appropriate to save settings and other small chunks of data. </p>
|
| +<p class="note"><b>Note:</b>
|
| +Chrome Sync Storage is not intended to be used as a generic database.
|
| +There are several restrictions on the amount of information you can save,
|
| +so it is more appropriate to save settings and other small chunks of data. </p>
|
| +
|
| +<h3 id="manifest">Update manifest</h3>
|
| +
|
| +<p>Request permission to use storage in your manifest.
|
| +Permissions are the same in the
|
| +<a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab5_data/angularjs/1_storage_sync/manifest.json">AngularJS manifest.json</a> and
|
| +<a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab5_data/javascript/1_storage_sync/manifest.json">JavaScript manifest.json</a>:
|
| +</p>
|
|
|
| -<ol>
|
| -<li><p>Request permission to use storage in your <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab5_data/angularjs/1_storage_sync/manifest.json">manifest.json</a>:
|
| <pre>
|
| {
|
| ... ,
|
| "permissions": ["storage"]
|
| }
|
| -</pre></p></li>
|
| -<li><p>Change your <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab5_data/angularjs/1_storage_sync/controller.js">controller.js</a> and, instead of a static list, get the Todo list from the syncable storage:
|
| -<pre>
|
| +</pre>
|
| +
|
| +<h3 id="simple-controller">Update controller</h3>
|
| +
|
| +<p>Change your controller to get the Todo list
|
| +from syncable storage instead of a static list:
|
| +<a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab5_data/angularjs/1_storage_sync/controller.js">AngularJS controller.js</a> or
|
| +<a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab5_data/javascript/1_storage_sync/controller.js">JavaScript controller.js</a>.
|
| +</p>
|
| +
|
| +<tabs data-group="source">
|
| +
|
| + <header tabindex="0" data-value="angular">Angular</header>
|
| + <header tabindex="0" data-value="js">JavaScript</header>
|
| +
|
| + <content>
|
| + <pre>
|
| // Notice that chrome.storage.sync.get is asynchronous
|
| chrome.storage.sync.get('todolist', function(value) {
|
| // The $apply is only necessary to execute the function inside Angular scope
|
| @@ -37,13 +69,69 @@ $scope.load = function(value) {
|
| {text:'learn angular', done:true},
|
| {text:'build an angular app', done:false}];
|
| }
|
| -}
|
| +}
|
|
|
| $scope.save = function() {
|
| chrome.storage.sync.set({'todolist': $scope.todos});
|
| };
|
| -</pre></li><li>In the HTML, call <code>save()</code> whenever the data changes. There are many other ways of doing this in Angular, like using <code>$watchers</code> on the scope. The one used here makes the <code>save()</code> calls explicit.
|
| -<pre>
|
| + </pre>
|
| + </content>
|
| + <content>
|
| + <pre>
|
| + /**
|
| + * Listen to changes in the model and trigger the appropriate changes in the view
|
| + **/
|
| + model.addListener(function(model, changeType, param) {
|
| + if ( changeType === 'removed' || changeType === 'archived' || changeType === 'reset') {
|
| + redrawUI(model);
|
| + } else if ( changeType === 'added' ) {
|
| + drawTodo(model.todos[param], list);
|
| + } else if ( changeType === 'stateChanged') {
|
| + updateTodo(model.todos[param]);
|
| + }
|
| + storageSave();
|
| + updateCounters(model);
|
| + });
|
| +
|
| +// If there is saved data in storage, use it. Otherwise, bootstrap with sample todos
|
| + var storageLoad = function() {
|
| + chrome.storage.sync.get('todolist', function(value) {
|
| + if (value && value.todolist) {
|
| + model.setTodos(value.todolist);
|
| + } else {
|
| + model.addTodo('learn Chrome Apps', true);
|
| + model.addTodo('build a Chrome App', false);
|
| + }
|
| + });
|
| + }
|
| +
|
| + var storageSave = function() {
|
| + chrome.storage.sync.set({'todolist': model.todos});
|
| + };
|
| + </pre>
|
| + </content>
|
| +</tabs>
|
| +
|
| +<h3 id="simple-view">Update view</h3>
|
| +
|
| +<p>In the view,
|
| +<a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab5_data/angularjs/1_storage_sync/index.html">AngularJs index.hmtl</a> or
|
| +<a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab5_data/javascript/1_storage_sync/index.html">JavaScript index.html</a>,
|
| +save the data whenever it changes.
|
| +In AngularJS,
|
| +we call <code>save()</code> explicitedly
|
| +but there are many ways of doing this.
|
| +For example,
|
| +you could also use <code>$watchers</code> on the scope.
|
| +</p>
|
| +
|
| +<tabs data-group="source">
|
| +
|
| + <header tabindex="0" data-value="angular">Angular</header>
|
| + <header tabindex="0" data-value="js">JavaScript</header>
|
| +
|
| + <content>
|
| + <pre>
|
| ...
|
| [ <a href="" ng-click="archive() || save()">archive</a> ]
|
| ...
|
| @@ -51,32 +139,70 @@ $scope.save = function() {
|
| ...
|
| <form ng-submit="addTodo() || save()">
|
| ...
|
| -</pre></li>
|
| -<li>Check the results by reloading the app: open the app, right-click and select Reload App.
|
| -You can now add Todo items, close the app, and the new items will still be there when you reopen the app.</li>
|
| -</ol>
|
| + </pre>
|
| + </content>
|
| + <content>
|
| + <pre>
|
| +<form>
|
| + <input type="text" size="30"
|
| + placeholder="add new todo here">
|
| + <input class="btn-primary" type="submit" value="add">
|
| +</form>
|
| + </pre>
|
| + </content>
|
| +
|
| +</tabs>
|
| +
|
| +<h3 id="results1">Check the results</h3>
|
|
|
| -<p class="note"><b>Note:</b> If you get stuck and want to see the app in action,
|
| -go to <code>chrome://extensions</code>, load the unpacked <a href="https://github.com/GoogleChrome/chrome-app-codelab/tree/master/lab5_data/angularjs/1_storage_sync">1_storage_sync</a> app,
|
| -and launch the app from a new tab.</p>
|
| +<p>Check the results by reloading the app:
|
| +open the app, right-click and select Reload App.
|
| +You can now add Todo items, close the app,
|
| +and the new items will still be there when you reopen the app.
|
| +</p>
|
| +
|
| +<p>
|
| +If you get stuck and want to see the app in action,
|
| +go to <code>chrome://extensions</code>,
|
| +load the unpacked app, and launch the app from a new tab:
|
| +<a href="https://github.com/GoogleChrome/chrome-app-codelab/tree/master/lab5_data/angularjs/1_storage_sync">Angular JS 1_storage_sync</a> or
|
| +<a href="https://github.com/GoogleChrome/chrome-app-codelab/tree/master/lab5_data/javascript/1_storage_sync">JavaScript 1_storage_sync</a>.
|
| +</p>
|
|
|
| <h2 id="handle_drag_and_dropped_files_and_urls">Handle drag-and-dropped files and URLs</h2>
|
|
|
| -Suppose you want to create Todos associated with local files and/or URLs. The natural way of doing this is to accept dropped items. It's simple enough to add drag-and-drop support in a Chrome app using the standard HTML5 Drag-and-Drop API.
|
| +<p>Suppose you want to create Todos associated with local files and/or URLs.
|
| +The natural way of doing this is to accept dropped items.
|
| +It's simple enough to add drag-and-drop support
|
| +in a Chrome packaged app using the standard
|
| +<a href="http://www.html5rocks.com/en/tutorials/dnd/basics/">HTML5 Drag-and-Drop API</a>.
|
| +</p>
|
|
|
| -<ol>
|
| -<li>In <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab5_data/angularjs/2_drop_files/controller.js">controller.js</a>, add code to handle the events of dragover, dragleave and drop:
|
| -<pre>
|
| +<h3 id="dd-controller">Update controller</h3>
|
| +
|
| +<p>In the controller,
|
| +add code to handle the events of dragover, dragleave, and drop:
|
| +<a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab5_data/angularjs/2_drop_files/controller.js">AngularJS controller.js</a> or
|
| +<a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab5_data/javascript/2_drop_files/controller.js">JavaScript controller.js</a>.
|
| +</p>
|
| +
|
| +<tabs data-group="source">
|
| +
|
| + <header tabindex="0" data-value="angular">Angular</header>
|
| + <header tabindex="0" data-value="js">JavaScript</header>
|
| +
|
| + <content>
|
| + <pre>
|
| var defaultDropText = "Or drop files here...";
|
| $scope.dropText = defaultDropText;
|
|
|
| -// on dragOver, we will change the style and text accordingly, depending on
|
| +// on dragOver, we will change the style and text accordingly, depending on
|
| // the data being transferred
|
| var dragOver = function(e) {
|
| e.stopPropagation();
|
| e.preventDefault();
|
| - var valid = e.dataTransfer && e.dataTransfer.types
|
| - && ( e.dataTransfer.types.indexOf('Files') >= 0
|
| + var valid = e.dataTransfer && e.dataTransfer.types
|
| + && ( e.dataTransfer.types.indexOf('Files') >= 0
|
| || e.dataTransfer.types.indexOf('text/uri-list') >=0 )
|
| $scope.$apply(function() {
|
| $scope.dropText = valid ? "Drop files and remote images and they will become Todos"
|
| @@ -123,19 +249,132 @@ $scope.$apply(function() {
|
| document.body.addEventListener("dragover", dragOver, false);
|
| document.body.addEventListener("dragleave", dragLeave, false);
|
| document.body.addEventListener("drop", drop, false);
|
| -</pre></li><li>To make all the area of the window accept the drop event and still work on the same scope, let's move the Angular scope definition from the div to the body in the <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab5_data/angularjs/2_drop_files/index.html">index.html</a> file.
|
| -Also, let's associate the body's CSS class with the Angular controller's class, so we can change the class directly in the scope and have it automatically changed in the DOM:
|
| -<pre>
|
| + </pre>
|
| + </content>
|
| + <content>
|
| + <pre>
|
| +var defaultDropText = "Or drop files here...";
|
| +
|
| +var dropText = document.getElementById('dropText');
|
| +dropText.innerText = defaultDropText;
|
| +
|
| +// on dragOver, we will change the style and text accordingly, depending on
|
| +// the data being transfered
|
| +var dragOver = function(e) {
|
| + e.stopPropagation();
|
| + e.preventDefault();
|
| + var valid = isValid(e.dataTransfer);
|
| + if (valid) {
|
| + dropText.innerText="Drop files and remote images and they will become Todos";
|
| + document.body.classList.add("dragging");
|
| + } else {
|
| + dropText.innerText="Can only drop files and remote images here";
|
| + document.body.classList.add("invalid-dragging");
|
| + }
|
| +}
|
| +
|
| +var isValid = function(dataTransfer) {
|
| + return dataTransfer && dataTransfer.types
|
| + && ( dataTransfer.types.indexOf('Files') >= 0
|
| + || dataTransfer.types.indexOf('text/uri-list') >=0 )
|
| +}
|
| +
|
| +// reset style and text to the default
|
| +var dragLeave = function(e) {
|
| + dropText.innerText=defaultDropText;
|
| + document.body.classList.remove('dragging');
|
| + document.body.classList.remove('invalid-dragging');
|
| +}
|
| +
|
| +// on drop, we create the appropriate TODOs using dropped data
|
| +var drop = function(e, model) {
|
| + e.preventDefault();
|
| + e.stopPropagation();
|
| + if (isValid(e.dataTransfer)) {
|
| + if (e.dataTransfer.types.indexOf('Files') >= 0) {
|
| + var files = e.dataTransfer.files;
|
| + for (var i = 0; i < files.length; i++) {
|
| + var text = files[i].name+', '+files[i].size+' bytes';
|
| + model.addTodo(text, false, {file: files[i]});
|
| + }
|
| + } else { // uris
|
| + var uri=e.dataTransfer.getData("text/uri-list");
|
| + model.addTodo(uri, false, {uri: uri});
|
| + }
|
| + }
|
| +
|
| + dragLeave();
|
| +}
|
| +
|
| +exports.setDragHandlers = function(model) {
|
| + document.body.addEventListener("dragover", dragOver, false);
|
| + document.body.addEventListener("dragleave", dragLeave, false);
|
| + document.body.addEventListener("drop", function(e) {
|
| + drop(e, model);
|
| + }, false);
|
| +}
|
| + </pre>
|
| + </content>
|
| +
|
| +</tabs>
|
| +
|
| +<h3 id="dd-view">Update view</h3>
|
| +
|
| +<p>If using AngularJS,
|
| +let's move the Angular scope definition from the div to the body in the
|
| +<a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab5_data/angularjs/2_drop_files/index.html">AngularJS index.html</a> file to make all the area of the window accept the drop event and still work on the same scope.
|
| +Also, let's associate the body's CSS class with the Angular controller's class,
|
| +so we can change the class directly in the scope and have it automatically changed in the DOM:
|
| +</p>
|
| +
|
| +<tabs data-group="source">
|
| +
|
| + <header tabindex="0" data-value="angular">Angular</header>
|
| +
|
| + <content>
|
| + <pre>
|
| <body ng-controller="TodoCtrl" ng-class="dropClass">
|
| <!-- remember to remove the ng-controller attribute from the div where it was before -->
|
| -</pre></li>
|
| -<li>Add a message placeholder (in <code>index.html</code>) to warn the user that some types of dragging are not allowed:
|
| -<pre>
|
| + </pre>
|
| + </content>
|
| +
|
| +</tabs>
|
| +
|
| +<p>Add a message placeholder
|
| +(in <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab5_data/angularjs/2_drop_files/index.html">AngularJS index.html</a> or
|
| +<a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab5_data/javascript/2_drop_files/index.html">JavaScript index.html</a>) to warn the user that some types of dragging are not allowed:
|
| +</p>
|
| +
|
| +<tabs data-group="source">
|
| +
|
| + <header tabindex="0" data-value="angular">Angular</header>
|
| + <header tabindex="0" data-value="js">JavaScript</header>
|
| +
|
| + <content>
|
| + <pre>
|
| <div>
|
| {{dropText}}
|
| </div>
|
| -</pre></li>
|
| -<li>Add appropriate styling for the <code>dragging</code> and <code>invalid-dragging</code> CSS classes in <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab5_data/angularjs/2_drop_files/todo.css">todo.css</a>. Here we used a green or red background color animation:
|
| + </pre>
|
| + </content>
|
| + <content>
|
| + <pre>
|
| +<div id="dropText">
|
| +</div>
|
| + </pre>
|
| + </content>
|
| +
|
| +</tabs>
|
| +
|
| +<h3 id="dd-css">Update stylesheet</h3>
|
| +
|
| +<p>Add appropriate styling for the <code>dragging</code> and
|
| +<code>invalid-dragging</code> CSS classes in the css.
|
| +<a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab5_data/angularjs/2_drop_files/todo.css">AngularJS todo.css</a> and
|
| +<a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab5_data/javascript/2_drop_files/todo.css">JavaScript todo.css</a> are the same.
|
| +Here we used a green or red background color animation:
|
| +</p>
|
| +
|
| <pre>
|
| @-webkit-keyframes switch-green {
|
| from { background-color: white;} to {background-color: rgb(163, 255, 163);}
|
| @@ -150,27 +389,44 @@ Also, let's associate the body's CSS class with the Angular controller&#
|
| .invalid-dragging {
|
| -webkit-animation: switch-red 0.5s ease-in-out 0 infinite alternate;
|
| }
|
| -</pre></p></li><li><p>Check the results by reloading the app: open the app, right-click and select Reload App.
|
| -You can now drag files into the Todo list.</p></li>
|
| -</ol>
|
| +</pre>
|
| +
|
| +<h3 id="results2">Check the results</h3>
|
|
|
| -<p class="note"><b>Note:</b> If you get stuck and want to see the app in action,
|
| -go to <code>chrome://extensions</code>, load the unpacked <a href="https://github.com/GoogleChrome/chrome-app-codelab/tree/master/lab5_data/angularjs/2_drop_files">2_drop_files</a> app,
|
| -and launch the app from a new tab.</p>
|
| +<p>Check the results by reloading the app:
|
| +open the app, right-click and select Reload App.
|
| +You can now drag files into the Todo list.</p>
|
|
|
| -<h1 id="challenge_">Challenge:</h1>
|
| +<p>
|
| +If you get stuck and want to see the app in action,
|
| +go to <code>chrome://extensions</code>, load the unpacked app,
|
| +and launch the app from a new tab:
|
| +<a href="https://github.com/GoogleChrome/chrome-app-codelab/tree/master/lab5_data/angularjs/2_drop_files">AngularJS 2_drop_files</a>
|
| +or <a href="https://github.com/GoogleChrome/chrome-app-codelab/tree/master/lab5_data/javascript/2_drop_files">JavaScript 2_drop_files</a>.
|
| +</p>
|
|
|
| -<p>The current code only saves the file reference, but it doesn't open the file. Using the <a href="http://www.html5rocks.com/en/tutorials/file/filesystem/">HTML5 Filesystem API</a>, save the file contents in a sandboxed filesystem. When the Todo item is archived, remove the corresponding file from the sandboxed filesystem. Add an "open" link on each Todo that has an associated file. When the item is clicked and the file exists in the sandboxed filesystem, use the Chrome app <a href="http://developer.chrome.com/apps/fileSystem.html">Filesystem extension</a> to request a writable FileEntry from the user. Save the file data from the sandboxed filesystem into that entry.</p>
|
| +<h2 id="challenge_">Challenge</h2>
|
| +
|
| +<p>The current code only saves the file reference, but it doesn't open the file. Using the <a href="http://www.html5rocks.com/en/tutorials/file/filesystem/">HTML5 Filesystem API</a>, save the file contents in a sandboxed filesystem. When the Todo item is archived, remove the corresponding file from the sandboxed filesystem. Add an "open" link on each Todo that has an associated file. When the item is clicked and the file exists in the sandboxed filesystem, use the Chrome packaged app <a href="http://developer.chrome.com/apps/fileSystem.html">Filesystem API</a> to request a writable FileEntry from the user. Save the file data from the sandboxed filesystem into that entry.</p>
|
|
|
| <p class="note"><b>Tip:</b> managing file entries using the raw HTML5 Filesystem API is not trivial. You might want to use a wrapper library, like Eric Bidelman's <a href="https://github.com/ebidel/filer.js">filer.js</a>.</p>
|
|
|
| -<h1 id="takeaways_">Takeaways:</h1>
|
| +<h2 id="takeaways_">Takeaways</h2>
|
|
|
| <ul>
|
| <li><p>Use <a href="http://developer.chrome.com/apps/storage.html">chrome.storage.sync</a> to save small data that you need to be sync'ed among devices, like configuration options, application state, etc. The sync is automatic, as long as the same user is logged into Chrome on all devices.</p></li>
|
| -<li><p>Chrome apps support almost all HTML5 APIs, such as drag and drop. HTML Filesystem API is also supported, with extra features from the Chrome app's <a href="http://developer.chrome.com/apps/fileSystem.html">Filesystem API extension</a>, like asking the user to pick files on her local disk for read and write. The vanilla HTML5 Filesystem API only allows access to a sandboxed filesystem.</p></li>
|
| +<li><p>Chrome packaged apps support almost all HTML5 APIs, such as drag and drop.
|
| +HTML Filesystem API is also supported, with extra features from the Chrome packaged app's
|
| +<a href="http://developer.chrome.com/apps/fileSystem.html">Filesystem API</a>,
|
| +like asking the user to pick files on her local disk for read and write.
|
| +The vanilla HTML5 Filesystem API only allows access to a sandboxed filesystem.</p></li>
|
| </ul>
|
|
|
| -<h1 id="what_39_s_next_">What's next?</h1>
|
| +<h2 id="you_should_also_read">You should also read</h2>
|
| +
|
| +<p><a href="http://developer.chrome.com/apps/app_storage.html">Manage Data</a> tutorial</p>
|
| +
|
| +<h2 id="what_39_s_next_">What's next?</h2>
|
|
|
| -<p>In <a href="app_codelab6_lifecycle.html">lab6_lifecycle</a>, you will learn the basics of the Chrome app lifecycle. </p>
|
| +<p>In <a href="app_codelab6_lifecycle.html">5 - Manage App Lifecycle</a>,
|
| +you will learn the basics of the Chrome packaged app lifecycle. </p>
|
|
|