データモデル「docs」内のすべてのドキュメントに対して
自動的に適用されます各項目
には、ファイルのアイコン、ウェブ上でファイルを開くリンク、最終更新日時が含まれています。
次に、このテンプレートのレンダリングを管理するコントローラを Angular に指示する必要があります。そのために、
ngController ディレクティブを使用して、テンプレートを管理するよう DocsController
に指示します
:
<body data-ng-controller="DocsController">
<section id="main">
<ul>
<li data-ng-repeat="doc in docs">
<img data-ng-src=""> <a href=""></a>
<span class="date"></span>
</li>
</ul>
</section>
</body>
ここでは、イベント リスナーやイベント プロパティのデータをフックする例は示されていません。
あります。Angular は大変な作業です。
最後に、テンプレートに Angular のライトを追加します。一般的な方法は、
ngApp ディレクティブのすべて :
<html data-ng-app="gDriveApp">
必要に応じて、アプリの対象をページ内の小さな領域に絞り込むこともできます。私たちが持っているのは
このアプリのコントローラは 1 つですが、後でさらに追加する場合は ngApp を一番上に置きます。
要素を使用することで、ページ全体を Angular に対応できるようになります。
main.html
の最終的な結果は次のようになります。
<html data-ng-app="gDriveApp">
<head>
…
<base target="_blank">
</head>
<body data-ng-controller="DocsController">
<section id="main">
<nav>
<h2>Google Drive Uploader</h2>
<button class="btn" data-ng-click="fetchDocs()">Refresh</button>
<button class="btn" id="close-button" title="Close"></button>
</nav>
<ul>
<li data-ng-repeat="doc in docs">
<img data-ng-src=""> <a href=""></a>
<span class="date"></span>
</li>
</ul>
</section>
コンテンツ セキュリティ ポリシーについて
他の多くの JS MVC フレームワークとは異なり、Angular v1.1.0 以降では、厳格な
CSP。すぐに使えます。
ただし、v1.0.1 と v1.1.0 の間で古いバージョンの Angular を使用している場合は、
「コンテンツ セキュリティ モード」で実行する Angular。そのためには、ngCsp ディレクティブをインクルードします。
ngApp とともに追加します。
<html data-ng-app data-ng-csp>
認可の処理
データモデルはアプリ自体では生成されません。代わりに、外部 API(
Google Drive API)。そのため、アプリのデータを入力するには多少の作業が必要です。
API リクエストを行う前に、ユーザーの Google アカウントの OAuth トークンを取得する必要があります。
そのため、chrome.identity.getAuthToken()
の呼び出しをラップし、
accessToken
。これは Drive API の今後の呼び出しで再利用できます。
GDocs.prototype.auth = function(opt_callback) {
try {
chrome.identity.getAuthToken({interactive: false}, function(token) {
if (token) {
this.accessToken = token;
opt_callback && opt_callback();
}
}.bind(this));
} catch(e) {
console.log(e);
}
};
トークンを取得したら、Drive API に対してリクエストを行い、モデルにデータを入力します。
スケルトン コントローラ
「モデル」(ドキュメントと呼ばれます)で、
をテンプレートに追加します。
var gDriveApp = angular.module('gDriveApp', []);
gDriveApp.factory('gdocs', function() {
var gdocs = new GDocs();
return gdocs;
});
function DocsController($scope, $http, gdocs) {
$scope.docs = [];
$scope.fetchDocs = function() {
...
};
// Invoke on ctor call. Fetch docs after we have the oauth token.
gdocs.auth(function() {
$scope.fetchDocs();
});
}
gdocs.auth()
は、DocsController コンストラクタの一部として呼び出されます。Angular の
内部処理によりコントローラが作成されると、ユーザーを待つ新しい OAuth トークンが保証されます。
データを取得しています
テンプレートのレイアウト。コントローラ スキャフォールディング済み。OAuth トークンを手に入れましょう。次のステップ
ここで、メイン コントローラ メソッド fetchDocs()
を定義します。コントローラの主軸であり、
ユーザーのファイルをリクエストし、ドキュメント配列に API レスポンスのデータを格納します。
$scope.fetchDocs = function() {
$scope.docs = []; // First, clear out any old results
// Response handler that doesn't cache file icons.
var successCallback = function(resp, status, headers, config) {
var docs = [];
var totalEntries = resp.feed.entry.length;
resp.feed.entry.forEach(function(entry, i) {
var doc = {
title: entry.title.$t,
updatedDate: Util.formatDate(entry.updated.$t),
updatedDateFull: entry.updated.$t,
icon: gdocs.getLink(entry.link,
'http://schemas.google.com/docs/2007#icon').href,
alternateLink: gdocs.getLink(entry.link, 'alternate').href,
size: entry.docs$size ? '( ' + entry.docs$size.$t + ' bytes)' : null
};
$scope.docs.push(doc);
// Only sort when last entry is seen.
if (totalEntries - 1 == i) {
$scope.docs.sort(Util.sortByDate);
}
});
};
var config = {
params: {'alt': 'json'},
headers: {
'Authorization': 'Bearer ' + gdocs.accessToken,
'GData-Version': '3.0'
}
};
$http.get(gdocs.DOCLIST_FEED, config).success(successCallback);
};
fetchDocs()
は Angular の $http
サービスを使用して、XHR を介してメインフィードを取得します。OAuth アクセス
トークンは、他のカスタム ヘッダーやパラメータとともに Authorization
ヘッダーに含まれます。
successCallback
は API レスポンスを処理し、エントリごとに新しいドキュメント オブジェクトを作成します。
できます。
この時点で fetchDocs()
を実行すると、すべてが機能し、ファイルのリストが表示されます。
やりました!
おっと...きれいなファイル アイコンがありません。その理由は、コンソールをざっと確認すると
CSP 関連のエラー:
アイコン img.src
を外部 URL に設定しようとしているためです。これは CSP に違反しています。例: https://ssl.gstatic.com/docs/doclist/images/icon_10_document_list.png
この問題を解決するには
リモートアセットをローカルアプリに pull する必要があります。
リモート画像アセットのインポート
CSP が怒鳴るのをやめるために、XHR2 を使用して「インポート」します。ファイル アイコンを blob に設定してから、
img.src
をアプリによって作成された blob: URL
にマッピング。
XHR コードを追加して更新した successCallback
は次のとおりです。
var successCallback = function(resp, status, headers, config) {
var docs = [];
var totalEntries = resp.feed.entry.length;
resp.feed.entry.forEach(function(entry, i) {
var doc = {
...
};
$http.get(doc.icon, {responseType: 'blob'}).success(function(blob) {
console.log('Fetched icon via XHR');
blob.name = doc.iconFilename; // Add icon filename to blob.
writeFile(blob); // Write is async, but that's ok.
doc.icon = window.URL.createObjectURL(blob);
$scope.docs.push(doc);
// Only sort when last entry is seen.
if (totalEntries - 1 == i) {
$scope.docs.sort(Util.sortByDate);
}
});
});
};
CSP に再び満足して、次のような素晴らしいファイル アイコンが表示されるようになりました。
オフラインにする: 外部リソースのキャッシュ保存
最適化が必要なのは明らかに、ファイル アイコンごとに数百回の XHR リクエストを行わないことです。
fetchDocs()
を呼び出すたびに作成されます。デベロッパー ツール コンソールで [更新] を押して、これを確認できます
ボタンを何回か押します。毎回、n 個の画像が取得されます。
successCallback
を変更してキャッシュ レイヤを追加しましょう。追加された部分は太字でハイライト表示されています。
$scope.fetchDocs = function() {
...
// Response handler that caches file icons in the filesystem API.
var successCallbackWithFsCaching = function(resp, status, headers, config) {
var docs = [];
var totalEntries = resp.feed.entry.length;
resp.feed.entry.forEach(function(entry, i) {
var doc = {
...
};
// 'https://ssl.gstatic.com/doc_icon_128.png' -> 'doc_icon_128.png'
doc.iconFilename = doc.icon.substring(doc.icon.lastIndexOf('/') + 1);
// If file exists, it we'll get back a FileEntry for the filesystem URL.
// Otherwise, the error callback will fire and we need to XHR it in and
// write it to the FS.
var fsURL = fs.root.toURL() + FOLDERNAME + '/' + doc.iconFilename;
window.webkitResolveLocalFileSystemURL(fsURL, function(entry) {
doc.icon = entry.toURL(); // should be === to fsURL, but whatevs.
$scope.docs.push(doc); // add doc to model.
// Only want to sort and call $apply() when we have all entries.
if (totalEntries - 1 == i) {
$scope.docs.sort(Util.sortByDate);
$scope.$apply(function($scope) {}); // Inform angular that we made changes.
}
}, function(e) {
// Error: file doesn't exist yet. XHR it in and write it to the FS.
$http.get(doc.icon, {responseType: 'blob'}).success(function(blob) {
console.log('Fetched icon via XHR');
blob.name = doc.iconFilename; // Add icon filename to blob.
writeFile(blob); // Write is async, but that's ok.
doc.icon = window.URL.createObjectURL(blob);
$scope.docs.push(doc);
// Only sort when last entry is seen.
if (totalEntries - 1 == i) {
$scope.docs.sort(Util.sortByDate);
}
});
});
});
};
var config = {
...
};
$http.get(gdocs.DOCLIST_FEED, config).success(successCallbackWithFsCaching);
};
webkitResolveLocalFileSystemURL()
コールバックでは、実行時に $scope.$apply()
を呼び出している点に注目してください。
最後のエントリが表示されます。通常、$apply()
を呼び出す必要はありません。Angular がデータの変更を検出します
構築できます。ただし、この例では非同期コールバックの追加レイヤがあり、
Angular では認識されません。モデルを更新したら、Angular に明示的に通知する必要があります。
初回実行時には、アイコンは HTML5 ファイル システムに存在せず、
window.webkitResolveLocalFileSystemURL()
を指定するとエラー コールバックが呼び出されます。そのため
前の手法を再利用して画像を取得できます今度が違うのは
各 blob がファイル システムに書き込まれることを条件とします(writeFile() を参照)。コンソールでは
動作:
次回実行したとき(または [更新] ボタンを押すと)、
webkitResolveLocalFileSystemURL()
が存在するのは、ファイルが以前にキャッシュに保存されているためです。アプリによる設定
doc.icon
をファイルの filesystem: URL
に追加して、アイコンのコストのかかる XHR が発生しないようにします。
ドラッグ&ドロップ アップロード
アップローダ アプリがファイルをアップロードできないのは、虚偽の広告です。
app.js では、HTML5 ドラッグ&ドロップに関する小さなライブラリ
DnDFileController
。デスクトップからファイルをドラッグしてアップロードできる
Google ドライブに移動します。
これを gdocs サービスに追加するだけで、次のように機能します。
gDriveApp.factory('gdocs', function() {
var gdocs = new GDocs();
var dnd = new DnDFileController('body', function(files) {
var $scope = angular.element(this).scope();
Util.toArray(files).forEach(function(file, i) {
gdocs.upload(file, function() {
$scope.fetchDocs();
});
});
});
return gdocs;
});