対象読者
- AngularJSを使っている開発者
- JavaScriptのフレームワークの動向などに興味を持っている方
必要な環境
この記事では、AngularJSを使用し、Chrome(42.0)、IE11、Firefox(37.0)、Safari(8.0.4)の環境にて確認を行っています。
バージョン1.4では何がかわるのか
バージョン1.4での大きな特徴として、以下の変更がある予定です。
- 新しいRouter機能(ngNewRouter)
- より柔軟なクッキー機能($cookies)
- $parseや$compileのパフォーマンス改善
- $httpサービスにおけるパラメータシリアライズ方式の柔軟性強化
- ICU MessageFormatに対応したAngularJS式の利用
基本的には過去のソースコードが動かなくなるような変更はあまりなく、内部ハックをしているような開発者を除けば、既存のソースコードの対応が必要になってしまうケースは非常に限定的だと思います。
これら以外にも、細かい変更が多数含まれていて、実際に利用しているとちょっとした変更で便利になる機能や、ちょっと困ってしまう部分に対しての改善が随所に見られます。ただし、ngNewRouterはまだまだ仕様変更などがありそうですので、それ以外の新機能や機能改善の中で今回筆者が注目したものをいくつか紹介していきます。
新しい機能
バージョン1.4で新たに追加される機能を紹介します。
$http - パラメータのシリアライズ方法をカスタマイズする
$httpサービスでは、新たにGETパラメータを簡単にカスタマイズできるようになりました。また、ビルトインで利用できるシリアライズを提供する、以下の2つのサービスが追加されました。
- $httpParamSerializer - $httpサービスの中で、デフォルトで使用されているシリアライザ
- $httpParamSerializerJQLike - jQueryと同様のシリアライズ方法を提供するシリアライザ
例えば、jQueryのajax(get/post)メソッドでは、配列データの場合には「[]」付きでパラメータを作成しますが、そのような変更もリスト1のように簡単に変更が可能です。
var module = angular.module('app', []); module.controller('AppController', ['$http','$httpParamSerializerJQLike',AppController]); // (1) jQueryと同じようなkey[]=value1&key[]=valueのような形式にする function AppController($http,$httpParamSerializerJQLike) { $http({ method: 'GET', url: 'request.php', paramSerializer: $httpParamSerializerJQLike, // (2) シリアライズ方法を指定する params: { key: ['value1', 'value2'], foo: 'hoge' } }).success(function(result) { // : 省略 }); // (3) 自分で自由な形式にすることも可能 $http({ method: 'GET', url: 'request.php', paramSerializer: function(params) { var arr = []; for(var name in params) { if(angular.isArray(params[name])) { arr.push(name + "!" + params[name].join(',')); } else { arr.push(name + "!" + params[name]); } } return arr.join("/"); }, params: { key1: ['value1', 'value2'], foo: 'hoge' } }).success(function(result) { // : 省略 }); // (4) POSTデータのシリアライズに使う $http({ method: 'POST', url: 'request.php', headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}, transformRequest: $httpParamSerializerJQLike, data: { key1: ['value1', 'value2'], foo: 'hoge' } }).success(function(result) { // : 省略 }); }
(1)は、jQueryと同様にシリアライズを行うためのコードです。シリアライズ方法を変更するためには(2)のようにparamSerializerに指定します。(3)のように自分で好きなようにシリアライズ方法を変更するともできます。
また、このシリアライズサービスはGETパラメータ以外にも利用できます。例えば、(4)のように、POST時にJSONではなくFORMを利用した、POSTと同じような動作をさせる場合にはtransformRequestに指定することもできます。
ngJq - 内部で使用するjQuery互換モジュールの指定をする
AngularJSはjQueryが必須ではなく、jQueryがない場合にはjQueryのサブセットであるjqLiteを使い、jQueryが存在している場合にはjQueryを使っていました。ngJqディレクティブによりこれらの指定が自由にできるようになりました。リスト2のように、jQueryの指定があってもHTML要素にng-jq属性の指定がある場合にはjQueryを使わず、jqLiteを使います。
<html ng-jq> <head> <script src="http://code.jquery.com/jquery-2.1.3.js"></script> <script src="../vendors/1_4/angular.js"></script> </head>
また、リスト3のようにng-jqに値を指定することで異なるjQueryのバージョンの中から特定のバージョンを利用するもできます。
<!DOCTYPE html> <html ng-jq="jq1"> <head> : 省略 <script src="https://code.jquery.com/jquery-1.11.2.js"></script> <!-- (1) 1.11.2のバージョンを指定 --> <script> var jq1 = jQuery.noConflict(); <-- (2) ng-jqで指定した値と同じ変数名を利用 --> </script> <script src="http://code.jquery.com/jquery-2.1.3.js"></script> <!-- (3)他のライブラリでは2.1.3を利用--> : 省略 </head>
(1)でまず、AngularJS内で使用するjQueryを読み込み、(2)でjQueryのnoConflictメソッドを用いて「$」ではなくng-jqにて指定した変数名である「jq1」オブジェクトを作成します。
(3)では、AngularJS以外で利用するjQueryを読み込みます。
limitToフィルタ - オプションの追加によりページング処理を簡単に実現する
バージョン1.3までは、limitToフィルタを使えば配列内にあるデータから最初の指定件数だけを出力することができました。バージョン1.4では、ここに取得するデータの始まりのインデックスが指定できるようになります。
フィルタはngRepeatと共に利用することもできるため、なぜこのオプションがなかったのかが不思議なくらいですが、これでやっとページング処理が、リスト4のようにlimitToとngRepeatだけを使って実現できるようになりました。
<body ng-app="app" ng-controller="LimitToController"> <!-- (1) 単純な使い方 --> <div >{{list | limitTo: 3: 3}}</div> <h5>ページング</h5> <!-- (2) ngRepeatと組み合わせることで簡単にページング処理ができるようになる --> <div> <a ng-click="page(0)">1ページ</a>| <a ng-click="page(1)">2ページ</a>| <a ng-click="page(2)">3ページ</a>| <a ng-click="page(3)">4ページ</a> </div> <div ng-repeat="item in list | limitTo: limit: begin" style="border-bottom: 1px solid #666"> <span>{{item}}</span> </div> </body>
(1)は最も基本的なlimitToのフィルタの使用例です。続いて(2)のようにngRepeatと組み合わせて使用するとページング処理を作成することができます。
また、リスト5は実際のデータを作成する部分や、ページの切替を行うためのコントローラの実装例です。
module.controller('LimitToController',['$scope',function($scope){ var list = []; for(var i = 0; i < 20; i++){ list.push(i); } $scope.list = list; // データ $scope.limit = 5; // 1ページ当たりの件数 $scope.begin = 0; // 現在のページの最初のindex $scope.page = function(page){ $scope.begin = page * $scope.limit; } }]);