데이터 모델 "문서"의 모든 문서에 대해 쿼리합니다. 각 항목
에는 파일 아이콘, 웹에서 파일을 열 수 있는 링크, last updatedDate가 포함됩니다.
참고: 템플릿을 유효한 HTML로 만들기 위해 Angular의data-*
ngRepeat 반복자를 사용하지만 반드시 실행할 필요는 없습니다. 반복기를 다음과 같이 쉽게 작성할 수 있습니다.
<li ng-repeat="doc in 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">
원한다면 앱의 범위를 페이지의 더 작은 부분으로 줄일 수도 있습니다. Google은
이 앱에 컨트롤러가 하나 더 있지만 나중에 더 추가하려면 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 );
}
};
드림
참고: 선택적 콜백을 전달하면 OAuth 토큰이 승인되는 시점을 유연하게 알 수 있습니다.
제공합니다.
참고: 좀 더 단순화하기 위해 API 작업을 처리하는 gdocs.js 라이브러리를 만들었습니다.
토큰을 가져왔으면 이제 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 응답의 데이터로 docs 배열을 작성하는 일을 담당합니다.
$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
입니다. 이 문제를 해결하기 위해
앱에 로컬로 이러한 원격 애셋을 가져와야 합니다.
원격 이미지 애셋 가져오기
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() 참조). 콘솔에서는
있습니다.
다음에 실행 (또는 '새로고침' 버튼을 누르면)되면 URL이
파일이 이전에 캐시되었기 때문에 webkitResolveLocalFileSystemURL()
이(가) 존재합니다. 앱은
doc.icon
를 파일의 filesystem: URL
에 추가하고 아이콘에 비용이 많이 드는 XHR을 만들지 않습니다.
드래그 앤 드롭 업로드
업로더 앱은 파일을 업로드할 수 없다면 거짓 광고입니다.
app.js 는 HTML5 드래그 앤 드롭이라는 작은 라이브러리를 구현하여 이 기능을 처리합니다.
DnDFileController
입니다. 데스크톱에서 파일을 드래그하여 업로드할 수 있습니다.
Google Drive로 이동합니다.
이를 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 ;
});