cho mỗi tài liệu trong "tài liệu" về mô hình dữ liệu của chúng tôi. Mỗi mục
có chứa biểu tượng tệp, đường liên kết để mở tệp trên web và ngày cập nhật gần đây nhất.
Lưu ý: Để tạo HTML hợp lệ cho mẫu, chúng ta sẽ sử dụng thuộc tính data-*
cho mã của Angular
Trình lặp lại ngRepeat , nhưng bạn không cần phải làm như vậy. Bạn có thể dễ dàng viết trình lặp là
<li ng-repeat="doc in docs">
.
Tiếp theo, chúng ta cần cho Angular biết bộ điều khiển nào sẽ giám sát quá trình kết xuất của mẫu này. Để làm được điều đó, chúng tôi
dùng lệnh ngController để yêu cầu DocsController
thống trị mẫu
:
<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>
Xin lưu ý rằng những gì bạn không thấy ở đây là chúng tôi kết nối trình nghe hoặc thuộc tính sự kiện để thu thập dữ liệu
ràng buộc. Angular đang đảm nhận phần việc khó khăn đó cho chúng tôi!
Bước cuối cùng là làm cho Angular bật các mẫu của chúng ta. Cách thông thường để làm việc đó là đưa vào
ngApp cho đến hết ngày :
<html data-ng-app="gDriveApp">
Bạn cũng có thể thu hẹp phạm vi của ứng dụng xuống một phần nhỏ hơn của trang nếu muốn. Chúng tôi chỉ có
một bộ điều khiển trong ứng dụng này, nhưng nếu chúng ta bổ sung thêm sau này, hãy đặt ngApp ở trên cùng
giúp toàn bộ trang sẵn sàng cho Angular.
Sản phẩm hoàn thiện của main.html
sẽ có dạng như sau:
<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>
Lưu ý về Chính sách bảo mật nội dung
Không giống như nhiều khung JS MVC khác, Angular v1.1.0+ không yêu cầu tinh chỉnh để hoạt động trong môi trường nghiêm ngặt
CSP . Tính năng này hoạt động bình thường!
Tuy nhiên, nếu bạn đang sử dụng phiên bản cũ hơn của Angular giữa v1.0.1 và v1.1.0, bạn sẽ cần biết
Góc để chạy trong "chế độ bảo mật nội dung". Bạn có thể thực hiện việc này bằng cách thêm lệnh ngCsp
cùng với ngApp :
<html data-ng-app data-ng-csp>
Xử lý việc uỷ quyền
Mô hình dữ liệu không phải do ứng dụng tạo ra. Thay vào đó, dữ liệu này được điền từ một API bên ngoài (
API Google Drive). Do đó, bạn cần thực hiện một vài thao tác để điền dữ liệu của ứng dụng.
Trước khi có thể đưa ra yêu cầu API, chúng ta cần tìm nạp mã thông báo OAuth cho Tài khoản Google của người dùng.
Do đó, chúng ta đã tạo một phương thức để gói lệnh gọi đến chrome.identity.getAuthToken()
và lưu trữ
accessToken
để chúng ta có thể sử dụng lại cho các lệnh gọi đến API Drive trong tương lai.
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 );
}
};
Lưu ý: Khi truyền lệnh gọi lại không bắt buộc, chúng ta có thể linh hoạt biết được khi nào mã thông báo OAuth được
sẵn sàng.
Lưu ý: Để đơn giản hoá một chút, chúng tôi đã tạo thư viện gdocs.js để xử lý các tác vụ API.
Sau khi có mã thông báo, đã đến lúc tạo yêu cầu đối với API Drive và điền dữ liệu vào mô hình.
Bộ điều khiển bộ xương
"Mô hình" đối với Trình tải lên là một mảng đơn giản (còn gọi là tài liệu) gồm các đối tượng sẽ được kết xuất dưới dạng
những
trong mẫu:
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 ();
});
}
Xin lưu ý rằng gdocs.auth()
được gọi trong hàm khởi tạo DocsController. Khi Angular
nội bộ tạo ra bộ điều khiển thì chúng ta được đảm bảo sẽ có một mã thông báo OAuth mới cho người dùng.
Đang tìm nạp dữ liệu
Đã bố trí mẫu. Bộ điều khiển được nâng cao. Có mã thông báo OAuth. Bây giờ bạn phải làm gì?
Đã đến lúc xác định phương thức bộ điều khiển chính, fetchDocs()
. Đây là con ngựa của bộ điều khiển,
chịu trách nhiệm yêu cầu tệp của người dùng và gửi mảng tài liệu cùng với dữ liệu từ phản hồi của 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()
sử dụng dịch vụ $http
của Angular để truy xuất nguồn cấp dữ liệu chính qua XHR. Quyền truy cập OAuth
mã thông báo được đưa vào tiêu đề Authorization
cùng với các tiêu đề và thông số tuỳ chỉnh khác.
successCallback
xử lý phản hồi của API và tạo một đối tượng tài liệu mới cho mỗi mục nhập trong
nguồn cấp dữ liệu.
Nếu bạn chạy fetchDocs()
ngay bây giờ, mọi thứ sẽ hoạt động và danh sách các tệp xuất hiện:
Tuyệt!
Đợi đã,...chúng tôi thiếu các biểu tượng tệp gọn gàng. What gives? Kiểm tra nhanh bảng điều khiển cho thấy nhiều
lỗi liên quan đến CSP:
Lý do là chúng ta đang cố đặt các biểu tượng img.src
thành URL bên ngoài. Nội dung này vi phạm Chính sách bảo mật nội dung (CSP). Ví dụ: https://ssl.gstatic.com/docs/doclist/images/icon_10_document_list.png
. Để khắc phục vấn đề này, chúng tôi
cần đưa những thành phần từ xa này vào ứng dụng.
Nhập thành phần hình ảnh từ xa
Để Chính sách quyền riêng tư (CSP) không còn phàn nàn với chúng ta, chúng ta sử dụng XHR2 để "nhập" các biểu tượng tệp dưới dạng Blobs, sau đó đặt
img.src
với một blob: URL
do ứng dụng tạo.
Dưới đây là successCallback
đã cập nhật với mã XHR được thêm:
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 );
}
});
});
};
Bây giờ, CSP đã hài lòng trở lại với chúng ta, nên chúng ta sẽ nhận được các biểu tượng tệp đẹp mắt:
Chuyển sang chế độ ngoại tuyến: lưu tài nguyên bên ngoài vào bộ nhớ đệm
Cần thực hiện tối ưu hoá rõ ràng: không thực hiện hàng trăm yêu cầu XHR cho mỗi biểu tượng tệp trên
mỗi lệnh gọi đến fetchDocs()
. Xác minh điều này trong bảng điều khiển Công cụ dành cho nhà phát triển bằng cách nhấn nút "Làm mới"
nút vài lần. Mỗi lần, n hình ảnh sẽ được tìm nạp:
Hãy sửa đổi successCallback
để thêm một lớp lưu vào bộ nhớ đệm. Những nội dung bổ sung được in đậm:
$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 );
};
Lưu ý rằng trong lệnh gọi lại webkitResolveLocalFileSystemURL()
, chúng ta sẽ gọi $scope.$apply()
khi
đã xem mục nhập cuối cùng. Thông thường, bạn không cần gọi $apply()
. Angular phát hiện các thay đổi đối với dữ liệu
tự động. Tuy nhiên, trong trường hợp này, chúng ta có thêm một lớp lệnh gọi lại không đồng bộ
Angular không biết đến. Chúng ta phải thông báo rõ ràng cho Angular thời điểm cập nhật mô hình.
Trong lần chạy đầu tiên, các biểu tượng sẽ không có trong Hệ thống tệp HTML5 và các lệnh gọi đến
window.webkitResolveLocalFileSystemURL()
sẽ khiến lệnh gọi lại lỗi được gọi. Để làm được việc đó
chúng ta có thể sử dụng lại kỹ thuật trước đó và tìm nạp hình ảnh. Sự khác biệt duy nhất ở lần này là
mỗi blob được ghi vào hệ thống tệp (xem writeFile() ). Bảng điều khiển sẽ xác minh thông tin này
hành vi:
Trong lần chạy tiếp theo (hoặc nhấn nút "Refresh" (Làm mới), URL được chuyển đến
webkitResolveLocalFileSystemURL()
tồn tại vì tệp đã được lưu vào bộ nhớ đệm trước đó. Ứng dụng sẽ đặt
doc.icon
vào filesystem: URL
của tệp và tránh tạo XHR tốn kém cho biểu tượng.
Tải lên bằng cách kéo và thả
Ứng dụng tải lên không quảng cáo sai khi không thể tải tệp lên!
app.js xử lý tính năng này bằng cách triển khai một thư viện nhỏ xung quanh tính năng Kéo và thả HTML5 được gọi là
DnDFileController
. Ứng dụng này cho phép kéo vào các tệp từ máy tính và tải các tệp đó lên
vào Google Drive.
Chỉ cần thêm đoạn mã này vào dịch vụ gdocs là xong:
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 ;
});