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 |