AngularJS を本気でつかうための tips
最近、管理画面で AngularJS をつかってみている。 そんな中で、いくつか工夫した点があるのでそれをシェアさせていただきます。
XHR のエラーを表示する
XHR のエラーがおきた際のハンドリングをいちいち手でかくのは非効率。管理画面とか中の人しかつかわないので、エラーがおこった旨を随時報告するだけでよい。
そんなケースでは以下のようにする。
angular.module('myapp.exceptionHandler', [])
.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push(function($q, $log, $rootScope) {
return {
'responseError': function(response) {
$log.error(response);
var h = $(response.content).find('html');
$rootScope.$broadcast('showExceptionDialog', response);
$q.reject(response);
}
};
});
}])
.controller('ExceptionDialogCtrl', function ($scope, $http, $rootScope) {
$rootScope.$on('showExceptionDialog', function (event, err) {
$scope.err = err;
$('#errorDialog').modal();
});
});
$http で XHR のエラーが発生した場合、bootstrap のモーダルを表示するようにしている。便利。
やや乱暴だけど。
X-Requested-With ヘッダを送出する
X-Requested-With ヘッダをつけたいな、ってときは以下のようにしたらよい。
angular.module('myapp.exceptionHandler', [])
.config(['$httpProvider', function ($httpProvider) {
$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHTTPRequest';
}]);
ディレクトリ構成をそれなりに考える
app/
controller/
entry.js
comment.js
app.js
上記のようにするとよいかなあ、とおもっています。今回は、既存の AngularJS じゃないところから AngularJS にかえようとしているのでこんな綺麗なかんじになってなくて、CRUD 対象のテーブルごとに App を定義してるんですけどね!
Pagination について
<table>
<tr ng-repeat="row in rows">
<td>{{row.id}}</td>
<td>{{row.name}}</td>
</tr>
</table>
<a class="btn btn-default btn-block" ng-click="read_more()" ng-show="has_next" >Read More</a>
みたいにしておいて、
.controller('ctrl.foo.index', function (api, $scope) {
$scope.rows = [];
$scope.page = 1;
$scope.query = function () {
api.query({page: $scope.page}, function (dat) {
$scope.rows = $scope.rows.concat(dat.rows);
$scope.has_next = dat.has_next;
});
};
$scope.query();
$scope.read_more = function () {
$scope.page++;
$scope.query();
};
});
などとすればよい。
Pagination の方式にはいろいろあるが、基本的にはこういう風に実装するのが楽。
API アクセスについて
管理画面用に JSON API を用意している。管理画面用に用意した JSON API をコールするのに最初は ngResource をつかっていたのだが、いかんせんめんどくさい。ngResource をつかっていると ngResource の流儀にあわせなければならないし、ちょっとでも rail をはずれると、もうめんどくさくてしょうがない。おしつけがましい。
そして、ngResource のインスタンスを共通化しようと思うと DI で導入することになるわけだけど、それをいちいち引数でうけとるのがまぁダルい。
そういうわけで、$http をベースに自分でかきなおした。(ぶっちゃけ jQuery の ajax 機能でかいてもよかった)
https://github.com/tokuhirom/angular-api-client.js にそのソースを公開しています。
以下のように API client を定義しておく。
angular.module('myapp.api', ['AngularAPIClient'])
.factory('api', function (AngularAPIClient) {
return {
entry: apiClient.make([
['query', 'GET', '/entry/query'],
['create', 'POST', '/entry/create'],
]),
member: apiClient.make([
['query', 'GET', '/member/query'],
])
};
});
これは以下のように使用することができる。
angular.module('myapp', ['myapp.api'])
.controller('EntryCtrl', function (api) {
api.entry.query({}, function (dat) {
$scope.rows = $scope.rows.concat(dat.rows);
});
});
ライブラリのコード自体も30行ぐらいしかないので、よめばすぐわかるのでよい。
なお、API Client のライブラリはサーバーのルーティングコードから自動生成している。
resource 'entry', 'Entry', sub {
get 'query';
post 'create';
};
とか書いておくと、サーバーサイドでは MyApp::Admin::C::Entry#query に /entry/query がマッピングされる、みたいなかんじ。そして、ついでにこの DSL から api-client.js も生成している的な!!
まとめ
いくつかの tips をのべさせていただきました。
追記
AngularAPI 的には $ はリザーブドだと tmatsuo さんにきいたので $ つけるのやめた!