AngularJSをサクッと覚える必要があったので勉強したメモ。
生のJSで扱おうと思うとアロー関数使うとうまく動かない箇所があったので、昔ながらの無名関数で書く。
中で呼び出すスクリプト等は学習時に利用した教材に合わせているため古い。
記事中のコードはここにあります
最小限のアプリケーションの作成
AngularJSの仕組みを使って画面に要素を表示するまで。
<!DOCTYPE html> <!-- ng-appでsampleAppモジュールを関連付け --> <html lang="ja" ng-app="sampleApp"> <head> <meta charset="UTF-8"> <title>sample</title> <!-- AngularJSの読み込み --> <script src="//code.angularjs.org/1.5.0/angular.min.js"></script> <script src="app.js"></script> <!-- bootstrapの読み込み --> <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css"/> </head> <body> <!-- ng-controllerでsampleControllerを関連付け --> <div ng-controller="sampleController"> <!-- sampleControllerのScopeのmsgを出力 --> <h2>{{ msg }}</h2> </div> </body> </html>
// sampleAppモジュールを作成 const sampleApp = angular.module('sampleApp', []); // sampleAppモジュールにsampleControllerを関連付ける // AngularJSの機能で$scopeサービスを利用してコントローラーに紐づく変数を定義する sampleApp.controller('sampleController', ['$scope', function ($scope) { $scope.msg = 'Hello, World!'; }]);
ng-modelによる双方向バインディング
操作によって変更された値を即座に画面に反映する双方向バインディングの仕組みがある。
テキストボックスの操作に応じて表示が変化するのを確かめられるサンプル。
<!DOCTYPE html> <html lang="ja" ng-app="sampleApp"> <head> <meta charset="UTF-8"> <title>sample</title> <script src="//code.angularjs.org/1.5.0/angular.min.js"></script> <script src="app.js"></script> <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css"/> </head> <body> <div ng-controller="sampleController"> <h2>{{ msg }}</h2> <!-- ng-modelによるbindingで入力に対応して表示が変わる --> <input type="text" ng-model="msg"> </div> </body> </html>
ngRouteを使ったルーティング
AngularJSではURLの#より後ろの文字列をpathとしてルーティングを行うことができる。
<!DOCTYPE html> <html lang="ja" ng-app="sampleApp"> <head> <meta charset="UTF-8"> <title>sample</title> <script src="//code.angularjs.org/1.5.0/angular.min.js"></script> <!-- ルーティング用のモジュールを読み込む --> <script src="//code.angularjs.org/1.5.0/angular-route.min.js"></script> <script src="app.js"></script> <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css"/> </head> <body> <!-- ルーティング結果を埋め込む場所をng-viewでマークする --> <div ng-view></div> <!-- 切り替えを確認するためにボタンを置く --> <a href="#/" class="btn">Sample</a> <a href="#/sample2" class="btn">Sample2</a> </body> </html>
const sampleApp = angular.module('sampleApp', ['ngRoute']); sampleApp .controller('sampleController', ['$scope', function ($scope) { $scope.msg = 'Hello, World!'; }]) .controller('sampleController2', ['$scope', function ($scope) { $scope.msg = 'Hello, NewWorld!'; }]); // #/ の場合と、 #/sample2の場合のルーティングを設定する sampleApp.config(['$routeProvider', function ($routeProvider) { $routeProvider .when('/', { template: '<h2>{{ msg }}</h2>', controller: 'sampleController' }) .when('/sample2', { template: '<h2>{{ msg }}</h2>', controller: 'sampleController2' }) }]);
ルーティング時にpathからパラメータを渡す
ルーティングのpathにパターンを定義して、処理の中で値として利用することができる。
パターンはいくつかあるけど、単純なケースのサンプル。
<!DOCTYPE html> <html lang="ja" ng-app="sampleApp"> <head> <meta charset="UTF-8"> <title>sample</title> <script src="//code.angularjs.org/1.5.0/angular.min.js"></script> <script src="//code.angularjs.org/1.5.0/angular-route.min.js"></script> <script src="app.js"></script> <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css"/> </head> <body> <div ng-view></div> <a href="#/" class="btn">Sample</a> <a href="#/sample2" class="btn">Sample2</a> </body> </html>
const sampleApp = angular.module('sampleApp', ['ngRoute']); sampleApp.config(['$routeProvider', function ($routeProvider) { // #/ の場合と、 #/{何らかの文字列} の場合のルーティングを設定する // この定義の場合、#/{何らかの文字列}における何らかの文字列がparamにバインドされてControllerに渡される $routeProvider .when('/', { template: '<h2>{{ msg }}</h2>', controller: 'sampleController' }) .when('/:param', { template: '<h2>{{ msg }}</h2>', controller: 'sampleController' }) }]); // $routeParamsを利用してpathに含まれる文字列(paramに対応するもの)をViewに反映する // 何も渡されなかったときはデフォルトの文字列を表示する sampleApp .controller('sampleController',['$scope', '$routeParams', function ($scope, $routeParams) { $scope.msg = $routeParams.param || 'Hello, World!'; }]);
templateを別のファイルに切り出す
ルーティングのサンプルでは直接記載したtemplateを別ファイルに切り出せるようにする。
<!-- 元々templateの中に書いてたhtmlをここに書く --> <h2>{{ msg }}</h2>
const sampleApp = angular.module('sampleApp', ['ngRoute']); // templateUrlに切り出したhtmlを指定する sampleApp.config(['$routeProvider', function ($routeProvider) { $routeProvider .when('/', { templateUrl: 'template.html', controller: 'sampleController' }) .when('/:param', { templateUrl: 'template.html', controller: 'sampleController' }) }]); sampleApp .controller('sampleController',['$scope', '$routeParams', function ($scope, $routeParams) { $scope.msg = $routeParams.param || 'Hello, World!'; }]);
controllerAsを使って$scopeを使わないでcontrollerを書く
ng-controllerの記述を ng-controller="sampleController as main"
のように変えたり、$routeProviderの指定をcontollerAs
に変えることによって$scopeを利用せず記述することができる。
Angularの考え方に近く、後期に書かれたAngularJSはこちらで書かれていることが多いっぽい。
<!-- $routeProviderで指定したctrlの別名から値を参照する --> <h2>{{ ctrl.msg }}</h2>
const sampleApp = angular.module('sampleApp', ['ngRoute']); sampleApp.config(['$routeProvider', function ($routeProvider) { $routeProvider // controllerとcontrollerAsを分けて書く場合 .when('/', { templateUrl: 'template.html', controller: 'sampleController', controllerAs: 'ctrl' }) // templateに埋め込む形で書く場合 .when('/:param', { templateUrl: 'template.html', controller: 'sampleController as ctrl' }) }]); sampleApp .controller('sampleController',['$routeParams', function ($routeParams) { // $scopeじゃなくてthisに値を設定する this.msg = $routeParams.param || 'Hello, World!'; }]);
AngularJSのService
ビジネスロジックをまとめるための仕組み。
AngularJSの仕組みで名称を指定すると自動でDIされる。($scope、$routeProvider、$routeParamsなど)。
$scope以外は基本的にシングルトンなので、コンポーネント間の値の共有に使われたりする。
Serviceは後述の方法で自作してAngularJSから理由することができる。
Serviceを自作する
const sampleApp = angular.module('sampleApp', ['ngRoute']); sampleApp.config(['$routeProvider', function ($routeProvider) { $routeProvider .when('/', { templateUrl: 'template.html', controller: 'sampleController', controllerAs: 'ctrl' }) .when('/:param', { templateUrl: 'template.html', controller: 'sampleController as ctrl' }) }]); // Serviceを作成してモジュールに登録する sampleApp.service('sampleService', function () { this.firstName = '太郎'; this.lastName = '山田'; this.fullName = () => this.firstName + ' ' + this.lastName; }); // 登録したServiceをInjectして処理を呼び出す sampleApp .controller('sampleController', ['$routeParams', 'sampleService', function ($routeParams, sampleService) { // $scopeじゃなくてthisに値を設定する this.msg = ($routeParams.param || 'Hello, World!') + ' ' + sampleService.fullName(); }]);
AngularJSのDirective
Directiveを自作する
AngularJSの世界におけるDirectiveはコンポーネントのようなもので、独自に定義したtemplateや処理をelementやattributeの形で埋め込めるもの。
今回はelementとして扱う単純な例と、親から子のDirectiveにどのように値を渡すかのサンプルを書く
<!DOCTYPE html> <html lang="ja" ng-app="sampleApp"> <head> <meta charset="UTF-8"> <title>sample</title> <script src="//code.angularjs.org/1.5.0/angular.min.js"></script> <script src="app.js"></script> <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css"/> </head> <body> <div ng-controller="sampleController as ctrl"> <!-- 双方向バインディングでディレクティブに値を渡す --> <sample-directive-a value="ctrl.msg"></sample-directive-a> <!-- 文字列でディレクティブに値を渡す --> <sample-directive-b value="ctrl.msg"></sample-directive-b> <!-- interpolationで渡すとディレクティブに渡る文字列自体が変化するので見た目上双方向バインディングのものと同じになる --> <sample-directive-b value="{{ ctrl.msg }}"></sample-directive-b> <!-- ディレクティブに関数を渡す --> <sample-directive-c function="ctrl.upperMsg()"></sample-directive-c> <!-- 値の変更の確認用 --> <input type="text" ng-model="ctrl.msg"> </div> </body> </html>
const sampleApp = angular.module('sampleApp', []); // Directiveに渡す用の値を作るController sampleApp .controller('sampleController', [function () { this.msg = 'Hello, World!'; this.upperMsg = function () { return this.msg.toUpperCase(); } }]); // Directive sampleApp .directive('sampleDirectiveA', () => { return { restrict: 'E', // elementとしてのみ呼び出せるよう制限 template: '<h2>{{ value }}</h2>', scope: { value: '=' // scopeを=にすると親から渡されたその値は双方向バインディング状態になる } } }) .directive('sampleDirectiveB', () => { return { restrict: 'E', template: '<h2>{{ value }}</h2>', scope: { value: '@' // scopeを@にすると文字列で渡されたことになる。 } } }) .directive('sampleDirectiveC', () => { return { restrict: 'E', template: '<h2>{{ function() }}</h2>', scope: { function: '&' // scopeを&にすると関数や配列を渡されたことになる。 } } });