| OLD | NEW |
| 1 <meta name="doc-family" content="apps"> | 1 <meta name="doc-family" content="apps"> |
| 2 <h1>Build Apps with AngularJS</h1> | 2 <h1>Build Apps with AngularJS</h1> |
| 3 <!--Article written by Eric Bidelman--> | 3 <!--Article written by Eric Bidelman--> |
| 4 <p> | 4 <p> |
| 5 This guide gets you started building Chrome Apps | 5 This guide gets you started building Chrome Apps |
| 6 with the <a href="http://angularjs.org/">AngularJS</a> MVC framework. | 6 with the <a href="http://angularjs.org/">AngularJS</a> MVC framework. |
| 7 To illustrate Angular in action, | 7 To illustrate Angular in action, |
| 8 we'll be referencing an actual app built using the framework, | 8 we'll be referencing an actual app built using the framework, |
| 9 the Google Drive Uploader. | 9 the Google Drive Uploader. |
| 10 The <a href="https://github.com/GoogleChrome/chrome-app-samples/tree/master/gdri
ve">source code</a> | 10 The <a href="https://github.com/GoogleChrome/chrome-app-samples/tree/master/samp
les/gdrive">source code</a> |
| 11 is available on GitHub. | 11 is available on GitHub. |
| 12 </p> | 12 </p> |
| 13 | 13 |
| 14 <h2 id="first">About the app</h2> | 14 <h2 id="first">About the app</h2> |
| 15 | 15 |
| 16 <img src="{{static}}/images/uploader.png" | 16 <img src="{{static}}/images/uploader.png" |
| 17 width="296" | 17 width="296" |
| 18 height="347" | 18 height="347" |
| 19 style="float: right; padding-left: 5px" | 19 style="float: right; padding-left: 5px" |
| 20 alt="Google Drive Uploader"> | 20 alt="Google Drive Uploader"> |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 121 | 121 |
| 122 <h2 id="three">Creating the event page</h2> | 122 <h2 id="three">Creating the event page</h2> |
| 123 | 123 |
| 124 <p> | 124 <p> |
| 125 All Chrome Apps require a background script/page | 125 All Chrome Apps require a background script/page |
| 126 to launch the app and respond to system events. | 126 to launch the app and respond to system events. |
| 127 </p> | 127 </p> |
| 128 | 128 |
| 129 <p> | 129 <p> |
| 130 In its | 130 In its |
| 131 <a href="https://github.com/GoogleChrome/chrome-app-samples/blob/master/gdrive/j
s/background.js">background.js</a> | 131 <a href="https://github.com/GoogleChrome/chrome-app-samples/blob/master/gdrive/j
s/background.js">background.js</a> |
| 132 script, | 132 script, |
| 133 Drive Uploader opens a 500x600px window to the main page. | 133 Drive Uploader opens a 500x600px window to the main page. |
| 134 It also specifies a minimum height and width for the window | 134 It also specifies a minimum height and width for the window |
| 135 so the content doesn't become too crunched: | 135 so the content doesn't become too crunched: |
| 136 </p> | 136 </p> |
| 137 | 137 |
| 138 <pre data-filename="background.js"> | 138 <pre data-filename="background.js"> |
| 139 chrome.app.runtime.onLaunched.addListener(function(launchData) { | 139 chrome.app.runtime.onLaunched.addListener(function(launchData) { |
| 140 chrome.app.window.create('../main.html', { | 140 chrome.app.window.create('../main.html', { |
| 141 id: "GDriveExample", | 141 id: "GDriveExample", |
| 142 bounds: { | 142 bounds: { |
| 143 width: 500, | 143 width: 500, |
| 144 height: 600 | 144 height: 600 |
| 145 }, | 145 }, |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 197 | 197 |
| 198 <p> | 198 <p> |
| 199 In | 199 In |
| 200 <a href="https://github.com/GoogleChrome/chrome-app-samples/blob/master/gdrive/j
s/app.js">app.js</a>, | 200 <a href="https://github.com/GoogleChrome/chrome-app-samples/blob/master/gdrive/j
s/app.js">app.js</a>, |
| 201 this button is hooked up to <code>window.close()</code>. | 201 this button is hooked up to <code>window.close()</code>. |
| 202 </p> | 202 </p> |
| 203 | 203 |
| 204 <h2 id="four">Designing the app the Angular way</h2> | 204 <h2 id="four">Designing the app the Angular way</h2> |
| 205 | 205 |
| 206 <p> | 206 <p> |
| 207 Angular is an MVC framework, so we need to define the app in such a way that a | 207 Angular is an MVC framework, so we need to define the app in such a way that a |
| 208 model, view, and controller logically fall out of it. Luckily, this is trivial w
hen using Angular. | 208 model, view, and controller logically fall out of it. Luckily, this is trivial w
hen using Angular. |
| 209 </p> | 209 </p> |
| 210 | 210 |
| 211 <p> | 211 <p> |
| 212 The View is the easiest, so let's start there. | 212 The View is the easiest, so let's start there. |
| 213 </p> | 213 </p> |
| 214 | 214 |
| 215 <h3 id="view">Creating the view</h3> | 215 <h3 id="view">Creating the view</h3> |
| 216 | 216 |
| 217 <p> | 217 <p> |
| 218 <a href="https://github.com/GoogleChrome/chrome-app-samples/blob/master/gdrive/m
ain.html">main.html</a> | 218 <a href="https://github.com/GoogleChrome/chrome-app-samples/blob/master/gdrive/m
ain.html">main.html</a> |
| 219 is the "V" in MVC; where we define HTML templates to render data into. | 219 is the "V" in MVC; where we define HTML templates to render data into. |
| 220 In Angular, templates are simple blocks of HTML with some special sauce. | 220 In Angular, templates are simple blocks of HTML with some special sauce. |
| 221 </p> | 221 </p> |
| 222 | 222 |
| 223 <p> | 223 <p> |
| 224 Ultimately we want to display the user's list of files. | 224 Ultimately we want to display the user's list of files. |
| 225 For that, a simple <ul> list makes sense. | 225 For that, a simple <ul> list makes sense. |
| 226 The Angular bits are highlighted in bold: | 226 The Angular bits are highlighted in bold: |
| 227 </p> | 227 </p> |
| 228 | 228 |
| 229 <pre data-filename="main.html"> | 229 <pre data-filename="main.html"> |
| 230 <ul> | 230 <ul> |
| 231 <li <strong>data-ng-repeat="doc in docs"</strong>> | 231 <li <strong>data-ng-repeat="doc in docs"</strong>> |
| 232 <img data-ng-src=<strong>"{{doc.icon}}"</strong>> <a href=<s
trong>"{{doc.alternateLink}}"</strong>><strong>{{doc.title}}
</strong></a> | 232 <img data-ng-src=<strong>"{{doc.icon}}"</strong>> <a href=<s
trong>"{{doc.alternateLink}}"</strong>><strong>{{doc.title}}
</strong></a> |
| 233 <strong>{{doc.size}}</strong> | 233 <strong>{{doc.size}}</strong> |
| 234 <span class="date"><strong>{{doc.updatedDate}}</strong></spa
n> | 234 <span class="date"><strong>{{doc.updatedDate}}</strong></spa
n> |
| 235 </li> | 235 </li> |
| 236 </ul> | 236 </ul> |
| (...skipping 259 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 496 <p> | 496 <p> |
| 497 <code>fetchDocs()</code> uses Angular's <code>$http</code> service | 497 <code>fetchDocs()</code> uses Angular's <code>$http</code> service |
| 498 to retrieve the main feed over XHR. | 498 to retrieve the main feed over XHR. |
| 499 The oauth access token is included | 499 The oauth access token is included |
| 500 in the <code>Authorization</code> header | 500 in the <code>Authorization</code> header |
| 501 along with other custom headers and parameters. | 501 along with other custom headers and parameters. |
| 502 </p> | 502 </p> |
| 503 | 503 |
| 504 <p> | 504 <p> |
| 505 The <code>successCallback</code> processes the API response and | 505 The <code>successCallback</code> processes the API response and |
| 506 creates a new doc object for each entry in the feed. | 506 creates a new doc object for each entry in the feed. |
| 507 </p> | 507 </p> |
| 508 | 508 |
| 509 <p> | 509 <p> |
| 510 If you run <code>fetchDocs()</code> right now, | 510 If you run <code>fetchDocs()</code> right now, |
| 511 everything works and the list of files shows up: | 511 everything works and the list of files shows up: |
| 512 </p> | 512 </p> |
| 513 | 513 |
| 514 <img src="{{static}}/images/listoffiles.png" | 514 <img src="{{static}}/images/listoffiles.png" |
| 515 width="580" | 515 width="580" |
| 516 height="680" | 516 height="680" |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 633 | 633 |
| 634 <strong>// 'https://ssl.gstatic.com/doc_icon_128.png' -> 'doc_icon_128.png
' | 634 <strong>// 'https://ssl.gstatic.com/doc_icon_128.png' -> 'doc_icon_128.png
' |
| 635 doc.iconFilename = doc.icon.substring(doc.icon.lastIndexOf('/') + 1);</str
ong> | 635 doc.iconFilename = doc.icon.substring(doc.icon.lastIndexOf('/') + 1);</str
ong> |
| 636 | 636 |
| 637 // If file exists, it we'll get back a FileEntry for the filesystem URL. | 637 // If file exists, it we'll get back a FileEntry for the filesystem URL. |
| 638 // Otherwise, the error callback will fire and we need to XHR it in and | 638 // Otherwise, the error callback will fire and we need to XHR it in and |
| 639 // write it to the FS. | 639 // write it to the FS. |
| 640 <strong>var fsURL = fs.root.toURL() + FOLDERNAME + '/' + doc.iconFilename; | 640 <strong>var fsURL = fs.root.toURL() + FOLDERNAME + '/' + doc.iconFilename; |
| 641 window.webkitResolveLocalFileSystemURL(fsURL, function(entry) { | 641 window.webkitResolveLocalFileSystemURL(fsURL, function(entry) { |
| 642 doc.icon = entry.toURL(); // should be === to fsURL, but whatevs.</stron
g> | 642 doc.icon = entry.toURL(); // should be === to fsURL, but whatevs.</stron
g> |
| 643 | 643 |
| 644 $scope.docs.push(doc); // add doc to model. | 644 $scope.docs.push(doc); // add doc to model. |
| 645 | 645 |
| 646 // Only want to sort and call $apply() when we have all entries. | 646 // Only want to sort and call $apply() when we have all entries. |
| 647 if (totalEntries - 1 == i) { | 647 if (totalEntries - 1 == i) { |
| 648 $scope.docs.sort(Util.sortByDate); | 648 $scope.docs.sort(Util.sortByDate); |
| 649 $scope.$apply(function($scope) {}); // Inform angular that we made cha
nges. | 649 $scope.$apply(function($scope) {}); // Inform angular that we made cha
nges. |
| 650 } | 650 } |
| 651 | 651 |
| 652 <strong>}, function(e) { | 652 <strong>}, function(e) { |
| 653 // Error: file doesn't exist yet. XHR it in and write it to the FS. | 653 // Error: file doesn't exist yet. XHR it in and write it to the FS. |
| 654 | 654 |
| 655 $http.get(doc.icon, {responseType: 'blob'}).success(function(blob) { | 655 $http.get(doc.icon, {responseType: 'blob'}).success(function(blob) { |
| 656 console.log('Fetched icon via XHR'); | 656 console.log('Fetched icon via XHR'); |
| 657 | 657 |
| 658 blob.name = doc.iconFilename; // Add icon filename to blob. | 658 blob.name = doc.iconFilename; // Add icon filename to blob. |
| 659 | 659 |
| 660 writeFile(blob); // Write is async, but that's ok. | 660 writeFile(blob); // Write is async, but that's ok. |
| 661 | 661 |
| 662 doc.icon = window.URL.createObjectURL(blob); | 662 doc.icon = window.URL.createObjectURL(blob); |
| 663 | 663 |
| 664 $scope.docs.push(doc); | 664 $scope.docs.push(doc); |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 735 and have them uploaded to Google Drive. | 735 and have them uploaded to Google Drive. |
| 736 </p> | 736 </p> |
| 737 | 737 |
| 738 <p> | 738 <p> |
| 739 Simply adding this to the gdocs service does the job: | 739 Simply adding this to the gdocs service does the job: |
| 740 </p> | 740 </p> |
| 741 | 741 |
| 742 <pre data-filename="app.js"> | 742 <pre data-filename="app.js"> |
| 743 gDriveApp.factory('gdocs', function() { | 743 gDriveApp.factory('gdocs', function() { |
| 744 var gdocs = new GDocs(); | 744 var gdocs = new GDocs(); |
| 745 | 745 |
| 746 var dnd = new DnDFileController('body', function(files) { | 746 var dnd = new DnDFileController('body', function(files) { |
| 747 var $scope = angular.element(this).scope(); | 747 var $scope = angular.element(this).scope(); |
| 748 Util.toArray(files).forEach(function(file, i) { | 748 Util.toArray(files).forEach(function(file, i) { |
| 749 gdocs.upload(file, function() { | 749 gdocs.upload(file, function() { |
| 750 $scope.fetchDocs(); | 750 $scope.fetchDocs(); |
| 751 }); | 751 }); |
| 752 }); | 752 }); |
| 753 }); | 753 }); |
| 754 | 754 |
| 755 return gdocs; | 755 return gdocs; |
| 756 }); | 756 }); |
| 757 </pre> | 757 </pre> |
| 758 | 758 |
| 759 <p class="backtotop"><a href="#top">Back to top</a></p> | 759 <p class="backtotop"><a href="#top">Back to top</a></p> |
| OLD | NEW |