untuk setiap dokumen dalam model data "docs". Setiap item
berisi ikon file, tautan untuk membuka {i>file<i} di web, dan tanggal pembaruan terakhir.
Catatan: Untuk membuat template menjadi HTML yang valid, kami menggunakan atribut data-*
untuk Angular
Iterator ngRepeat , tetapi Anda tidak perlu melakukannya. Anda dapat dengan mudah
menulis {i>repeater<i} sebagai
<li ng-repeat="doc in docs">
.
Selanjutnya, kita perlu memberi tahu Angular pengontrol mana yang akan mengawasi rendering template ini. Untuk itu, kita
gunakan perintah ngController untuk memberi tahu DocsController
agar memiliki kendali atas template
:
<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>
Perlu diingat, yang tidak Anda lihat di sini adalah kita menghubungkan pemroses peristiwa atau properti untuk data
jaringan. Angular melakukan tugas berat itu untuk kita!
Langkah terakhir adalah membuat Angular menerangi template kita. Cara yang umum untuk melakukannya
adalah dengan menyertakan
ngApp hingga di :
<html data-ng-app="gDriveApp">
Anda juga dapat mengatur aplikasi ke porsi halaman yang lebih kecil jika menginginkannya. Kita hanya punya
satu pengontrol di aplikasi ini. Namun, jika kita ingin menambahkan lebih banyak pengontrol nanti, menempatkan ngApp di bagian teratas
membuat seluruh halaman siap untuk Angular.
Produk akhir untuk main.html
akan terlihat seperti ini:
<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>
Sekilas tentang Kebijakan Keamanan Konten
Tidak seperti banyak kerangka kerja JS MVC lainnya, Angular v1.1.0+ tidak memerlukan penyesuaian untuk bekerja dalam
CSP . Perangkat ini langsung berfungsi, dan siap digunakan!
Namun, jika Anda menggunakan Angular versi lama antara v1.0.1 dan v1.1.0, Anda harus memberi tahu
Angular untuk dijalankan dalam "mode keamanan konten". Hal ini dilakukan dengan menyertakan perintah ngCsp
bersama ngApp :
<html data-ng-app data-ng-csp>
Menangani otorisasi
Model data tidak dibuat oleh aplikasi itu sendiri. Sebagai gantinya, data tersebut diisi dari API eksternal (
API Google Drive). Dengan demikian, ada sedikit pekerjaan yang diperlukan untuk mengisi data aplikasi.
Sebelum dapat membuat permintaan API, kami perlu mengambil token OAuth untuk Akun Google pengguna.
Untuk itu, kita telah membuat metode untuk menggabungkan panggilan ke chrome.identity.getAuthToken()
dan menyimpan
accessToken
, yang dapat digunakan kembali untuk panggilan mendatang ke 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 );
}
};
Catatan: Meneruskan callback opsional memberi kami fleksibilitas untuk mengetahui kapan token OAuth
siap.
Catatan: Untuk sedikit menyederhanakan, kami telah membuat library, gdocs.js untuk menangani tugas API.
Setelah kita memiliki token, saatnya membuat permintaan terhadap Drive API dan mengisi model.
Pengontrol kerangka
"Model" untuk Pengunggah adalah susunan sederhana (disebut dokumen) dari objek yang akan dirender sebagai
mereka
s dalam {i>template<i}:
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 ();
});
}
Perhatikan bahwa gdocs.auth()
dipanggil sebagai bagian dari konstruktor DocsController. Saat Angular
internal membuat pengontrol, kita diasuransikan memiliki token OAuth baru yang menunggu pengguna.
Mengambil data
Template ditata. Pengontrol dikerahkan. token OAuth yang dimiliki. Selanjutnya bagaimana?
Saatnya menentukan metode pengontrol utama, fetchDocs()
. Ini adalah pekerja keras dari pengontrol,
bertanggung jawab untuk meminta file pengguna dan mengajukan array dokumen dengan data dari respons 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()
menggunakan layanan $http
Angular untuk mengambil feed utama melalui XHR. Akses OAuth
token disertakan dalam header Authorization
bersama dengan header dan parameter kustom lainnya.
successCallback
memproses respons API dan membuat objek dokumen baru untuk setiap entri dalam
feed Anda.
Jika Anda menjalankan fetchDocs()
sekarang, semuanya akan berfungsi dan daftar file akan muncul:
Wow!
Tunggu,...kita kehilangan ikon {i>file<i} yang rapi itu. What gives? Pemeriksaan cepat konsol menunjukkan banyak
error terkait CSP:
Alasannya adalah karena kita mencoba menyetel ikon img.src
ke URL eksternal. Hal ini melanggar CSP. Contoh: https://ssl.gstatic.com/docs/doclist/images/icon_10_document_list.png
. Untuk memperbaikinya, kita
perlu menarik aset jarak jauh ini secara lokal ke aplikasi.
Mengimpor aset gambar jarak jauh
Agar CSP berhenti meneriaki kita, kita menggunakan XHR2 untuk "mengimpor" ikon file sebagai Blob, lalu atur
img.src
menjadi blob: URL
yang dibuat oleh aplikasi.
Berikut adalah successCallback
yang diperbarui dengan kode XHR yang ditambahkan:
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 );
}
});
});
};
Setelah CSP senang lagi dengan kita, kita mendapatkan ikon file yang bagus:
Sedang offline: menyimpan resource eksternal ke dalam cache
Pengoptimalan yang perlu dilakukan: tidak membuat 100 permintaan XHR untuk setiap ikon file di
setiap panggilan ke fetchDocs()
. Verifikasi hal ini di konsol Developer Tools dengan menekan tombol "Refresh"
beberapa kali. Setiap kali, n gambar diambil:
Mari kita ubah successCallback
untuk menambahkan lapisan cache. Penambahan ditandai dengan cetak tebal:
$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 );
};
Perhatikan bahwa dalam callback webkitResolveLocalFileSystemURL()
, kita memanggil $scope.$apply()
saat
entri terakhir terlihat. Biasanya memanggil $apply()
tidak diperlukan. Angular mendeteksi perubahan pada data
model secara automagis. Namun dalam kasus ini, kita memiliki lapisan tambahan callback asinkron yang
Angular tidak diketahui. Kita harus secara eksplisit memberi tahu Angular saat model telah diperbarui.
Saat pertama kali dijalankan, ikon tidak akan ada di Sistem File HTML5 dan panggilan ke
window.webkitResolveLocalFileSystemURL()
akan menyebabkan callback error dipanggil. Untuk itu
kita bisa menggunakan kembali teknik dari
sebelumnya dan mengambil gambar. Satu-satunya perbedaan kali ini adalah
bahwa setiap blob ditulis ke sistem file (lihat writeFile() ). Konsol memverifikasi hal ini
perilaku:
Setelah dijalankan berikutnya (atau tekan tombol "{i>Refresh<i}"), URL yang diteruskan ke
webkitResolveLocalFileSystemURL()
ada karena file telah di-cache sebelumnya. Kumpulan aplikasi
doc.icon
ke filesystem: URL
file dan menghindari pembuatan XHR yang mahal untuk ikon.
Upload tarik lalu lepas
Aplikasi uploader adalah iklan palsu jika tidak dapat mengupload file!
app.js menangani fitur ini dengan menerapkan library kecil di sekitar Tarik lalu Lepas HTML5 yang disebut
DnDFileController
. Fungsi ini memberikan kemampuan untuk menarik file dari desktop dan menguploadnya
ke Google Drive.
Cukup dengan menambahkan kode ini ke layanan gdocs akan melakukan tugasnya:
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 ;
});