とほほのAngularJS入門

AngularJSAngular は別物です。バージョン 1.x を AngularJS、2.0 以降を Angular と呼びますが、根本から作り直しているので互換性はありません。AngularJS は 1.8.x で開発を停止しており、2021年12月31日にサポートも停止されました。現在は拡張LTS (XLTS) のフェーズにはいっています。(2022/1/2追記)
目次

AngularJSとは?

AngularJS は、下記の様な特徴を持つ JavaScript フレームワークのひとつです。

これまでのWebサービスでは、サーバ側で画面(HTML/DOM)を生成していたのに対し、最近のWebサービスでは、サーバ側はDB操作のみを処理し、クライアント-サーバ間をAjaxでJSON交換し、画面(HTML/DOM)はクライアント側で生成する方式が増えてきました。AngularJSは、クライアント側 JavaScript のコントローラでデータモデルを管理し、画面(ビュー)とリアルタイムにデータを交換するのに適したフレームワークです。

サンプル

AngularJSの簡単なサンプルを下記に示します。AngularJSは、ng-app を指定した要素の中で使用します。ng-model="yourName" に入力した文字列が、{{yourName}} の箇所に表示されます。

HTML
<!doctype html>
<html ng-app>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js"></script>
</head>
<body>
 <div>
  <p><input type="text" ng-model="yourName"></p>
  <p>Hello {{yourName}}!</p>
 </div>
</body>
</html>
Sample

Hello {{yourName}}!

下記の様に、{{...}} の中で、JavaScript の式を記述することができます。

HTML
<!doctype html>
<html ng-app>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js"></script>
</head>
<body>
 <div><input type="text" ng-model="x"></div>
 <div><input type="text" ng-model="y"></div>
 <div>{{x}} vs {{y}} : {{( x > y ) ? x : y}} is big.</div>
</body>
</html>
Sample
{{x}} vs {{y}} : {{ ( x > y ) ? x : y }} is big

フィルタ

値を表示する際に、いくつかのフィルタを使用することができます。

HTML
<!doctype html>
<html ng-app>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js"></script>
</head>
<body>
{{12345678|number}}                         // カンマ区切り => 12,345,678
{{12.34|number:4}}                          // 少数以下4桁 => 12.3400
{{12.34|currency}}                          // 通貨 => $12.34
{{1234|currency:'&yen;'}}                   // 通貨(円記号) => ¥1,234.00
{{'2099-12-31T12:59:59'|date}}              // 日付フォーマット => Dec 31, 2099
{{'2099-12-31T12:59:59'|date:'yyyy/MM/dd'}} // 日付フォーマット => 2099/12/31
{{[1,2,3,4]|limitTo:3}}                     // 最初の3件のみ表示 => [1,2,3]
{{"Yamada"|lowercase}}                      // 小文字変換 => yamada
{{"Yamada"|uppercase}}                      // 大文字変換 => YAMADA
{{{name:"Yamada", age:36}|json}}            // JSON形式で表示 => { "name": "Yamada", "age": 26 }
{{[1,3,5,2,4]|orderBy}}                     // ソートして表示 => [1,2,3,4,5]
{{[1,3,5,2,4]|orderBy:'':true}}             // 逆順ソートして表示 => [5,4,3,2,1]
</body>
</html>

フィルターに関する詳細は下記を参照してください。

モジュールとコントローラ

下記では、モジュールとコントローラを利用した例を示します。ng-app でアプリケーションを指定し、ng-controller でコントローラを指定します。コントローラの中で $scope に定義した変数を {{...}} で使用することができます。

HTML
<!doctype html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js"></script>
<script>
var app = angular.module('myApp', []);
app.controller('myController', function($scope) {
  $scope.message = 'Hello world!';
});
</script>
</head>
<body>
 <div ng-app="myApp">
  <div ng-controller="myController">
   {{message}}
  </div>
 </div>
</body>
</html>
Sample
{{message}}

AngularJS 1.2 以降では、$scope ではなく this を用いて、次のように記述することが可能で、最近ではこの記法が主に使用されています。呼び出す側は as を用いてオブジェクトを定義し、オブジェクトのプロパティとして値を参照します。この形式により、複数のコントローラを利用する場合に $scope の範囲が不明確になるのを防ぐことができます。

HTML
<!doctype html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js"></script>
<script>
var app = angular.module('myApp', []);
app.controller('myController', function() {
  this.message = 'Hello world!';
});
</script>
</head>
<body>
 <div ng-app="myApp">
  <div ng-controller="myController as myCtrl">
   {{myCtrl.message}}
  </div>
 </div>
</body>
</html>
Sample
{{myCtrl.message}}

リスト

ng-repeat を用いて、リスト操作を行うことができます。

HTML
<!doctype html>
<html ng-app="myApp">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js"></script>
<script>
var app = angular.module('myApp', []);
app.controller('myController', function() {
    this.members = [
        { name: 'Tanaka', age: 36 },
        { name: 'Suzuki', age: 16 },
        { name: 'Yamada', age: 26 }
    ];
});
</script>
</head>
<body>
 <div ng-controller="myController as myCtrl">
  <ul>
  <li ng-repeat="member in myCtrl.members">{{member.name}} {{member.age}}</li>
  </ul>
  <div>{{myCtrl.members.length}} members</div>
 </div>
</body>
</html>
Sample
{{myCtrl.members.length}} members

orderByフィルタを用いて、age でソートして表示することが可能です。

HTML
  <li ng-repeat="member in myCtrl.members|orderBy:'age'">{{member.name}} {{member.age}}</li>

orderByフィルタの第二引数に true を指定すると逆順にソートします。

HTML
  <li ng-repeat="member in myCtrl.members|orderBy:'age':true">{{member.name}} {{member.age}}</li>

リストフィルタ

filter を用いることで、要素をフィルタリングして表示することができます。下記の例では、テキストフィールドに入力した文字にマッチする要素のみを表示します。

HTML
<!doctype html>
<html ng-app="myApp">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js"></script>
<script>
var app = angular.module('myApp', []);
app.controller('myController', function() {
    this.members = [
        { name: 'Tanaka', age: 36 },
        { name: 'Suzuki', age: 16 },
        { name: 'Yamada', age: 26 }
    ];
});
</script>
</head>
<body>
 <div ng-controller="myController as myCtrl">
  <input type="text" ng-model="searchText">
  <ul>
  <li ng-repeat="member in myCtrl.members|filter:searchText">{{member.name}} {{member.age}}</li>
  </ul>
 </div>
</body>
</html>
Sample

アクション

ng-click などで、コントローラに定義したアクションを呼び出すことができます。

HTML
<!doctype html>
<html ng-app="myApp">
<head>
<title>TEST</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js"></script>
<script>
var app = angular.module("myApp", []);
app.controller('myController', function() {
  this.count = 0;
  this.inc = function() { this.count++; }
  this.dec = function() { this.count--; }
});
</script>
</head>
<body>
<div ng-controller="myController as myCtrl">
  {{myCtrl.count}}
  <button ng-click="myCtrl.inc()">+1</button>
  <button ng-click="myCtrl.dec()">-1</button>
</div>
</body>
</html>
Sample
{{myCtrl.count}}

フォームバリデーション

フォームに対して様々なバリデーションルールを記述することができます。下記の例では、テキストが必須(required)であること、最少文字数(minlength)が3文字、最大文字数(maxlength)が8文字であることを示しています。それぞれのバリデーションがエラーになると、対応するエラーメッセージを表示します。novalidate は HTML 本来のバリデーションを実行せず、AngularJS のバリデーションにまかせることを意味します。

HTML
<!doctype html>
<html ng-app>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js"></script>
</head>
<body>
 <form name="myForm" novalidate>
  <div><input type="text" name="text1" ng-model="sampleText" ng-required="true" ng-minlength=3 ng-maxlength=8></div>
  <div ng-show="myForm.text1.$error.required">Required!</div>
  <div ng-show="myForm.text1.$error.minlength">Too short!</div>
  <div ng-show="myForm.text1.$error.maxlength">Too long!</div>
 </form>
</body>
</html>
Sample
Required!
Too short!
Too long!

各フォームで使用可能なバリデーションルールは、下記等を参照してください。

もう少し複雑なサンプル

下記は、名前と年齢を入力して Add ボタンを押すとリストに追加し、チェックボックスをチェックして Del ボタンを押すとリストから削除するサンプルです。最後に {{myCtrl|json}} でコントローラの内容をデバッグ用に表示しています。

HTML
<!doctype html>
<html ng-app="myApp">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js"></script>
<script>
var app = angular.module("myApp", []);
app.controller("myController", function() {
  this.members = [
    { name:"Tanaka", age:16 },
    { name:"Yamada", age:26 },
    { name:"Suzuki", age:36 },
  ];
  this.onAdd = function() {
    this.members.push({ name:this.name, age:this.age });
    this.name = this.age = "";
  };
  this.onDel = function() {
    for (var i = 0; i < this.members.length; i++) {
      if (this.members[i].checked) {
        this.members.splice(i--, 1);
      }
    }
  };
});
</script>
</head>
<body ng-controller="myController as myCtrl">
 <div>
  <input type="text" ng-model="myCtrl.name">(<input type="text" ng-model="myCtrl.age">)
  <button ng-click="myCtrl.onAdd()">Add</button>
 </div>
 <div ng-repeat="member in myCtrl.members">
  <input type="checkbox" ng-model="member.checked">{{member.name}}({{member.age}})
 </div>
 <div><button ng-click="myCtrl.onDel()">Del</button></div>
 <hr><pre>{{myCtrl|json}}</pre><hr>
</body>
</html>
Sample
()
{{member.name}}({{member.age}})

{{myCtrl|json}}

{{...}} をそのまま表示するには

ng-app を指定した要素の中では、{{ ... }} が AngularJS により解釈されてしまいます。解釈せずに表示するには、下記の様に、バックスラッシュ(\)でエスケープした文字で表示する、}} の間にHTMLのコメントを挿入する、ngNonBindable ディレクティブを用いるなどの方法があります。(ngBindable がサポートされていれば、デフォルトで NonBindable にして、解釈したい箇所だけ Bindable にできるのですが・・・)

HTML
{{'\{\{name\}\}'}}
{{name}<!-- -->}
<span class="ng-non-bindable">{{name}}</span>