はじめに
Angular JS で複数のコントローラ間でモデル(状態や値)を共有する方法として、次の 3 種類を解説します。
- モデルを共有するサービスを使用する (Shared Service)。
- 親コントローラのスコープを子コントローラで共有する (Parent Scope Sharing)。
- イベントを利用する (Pub/Sub)。
Shared Service
複数のコントローラ間で共有するモデルをサービスとして作成し、そのサービスを複数のコントローラで参照します。
実装例を示します。
<!DOCTYPE HTML>
<html ng-app="AngularJsStudy">
<head>
<title>Shared State Service - AngularJS Study</title>
<meta charset="utf-8" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" />
<link rel="stylesheet" href="app.css" />
</head>
<body>
<h4>Shared State Service</h4>
<div class="container ng-cloak" ng-controller="ShareControllerA">
<h5>This is ShareControllerA</h5>
<input type="text" class="form-control" ng-model="data.text">
</div>
<div class="container ng-cloak" ng-controller="ShareControllerB">
<h5>This is ShareControllerB</h5>
<input type="text" class="form-control" ng-model="data.text">
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<script src="app.js"></script>
</body>
</html>
(function() {
var app = angular.module("AngularJsStudy", []);
app.factory("SharedStateService", function() {
return {
text: 'SharedStateService'
};
});
app.controller("ShareControllerA", function($scope, SharedStateService) {
$scope.data = SharedStateService;
});
app.controller("ShareControllerB", function($scope, SharedStateService) {
$scope.data = SharedStateService;
});
}());
ShareControllerA
と ShareControllerB
では、スコープ $scope
の data
に SharedStateService
をバインドしています。
こうすることで、AngularJS の一番の特長であるビューとモデルの双方向バインドを効果的に利用できます。
Parent Scope Sharing
AngularJS ではコントローラをネストすることができます。
コントローラをネストした場合、子要素のコントローラは、親要素のコントローラのスコープを参照することができます。
実装例を示します。
<!DOCTYPE HTML>
<html ng-app="AngularJsStudy">
<head>
<title>Parent Scope Sharing - AngularJS Study</title>
<meta charset="utf-8" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" />
<link rel="stylesheet" href="app.css" />
</head>
<body>
<h4>Parent Scope Sharing</h4>
<div class="container ng-cloak" ng-controller="MasterController">
<h5>Master Controller</h5>
<input type="text" class="form-control" ng-model="text">
<hr/>
<div class="container" ng-controller="ChildControllerA">
<h5>Child Controller A</h5>
<input type="text" class="form-control" ng-model="$parent.text">
</div>
<hr/>
<div class="container" ng-controller="ChildControllerB">
<h5>Child Controller B</h5>
<input type="text" class="form-control" ng-model="$parent.text">
</div>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<script src="app.js"></script>
</body>
</html>
(function() {
var app = angular.module("AngularJsStudy", []);
app.controller("MasterController", function($scope) {
$scope.text = "Shared Text";
});
app.controller("ChildControllerA", function($scope) {
});
app.controller("ChildControllerB", function($scope) {
});
}());
Shared Service
と同様の方法で、こちらの方が直感的かもしれませんが、
AngularJS のお勧めではビューに無関係なロジックはサービスにとあるので、
AngularJS らしく実装したいなら Shared Service
の方でしょう。
Pub/Sub
プロパティが変更されたら、$scope
の $emit()
, $broadcast()
メソッドを利用してイベントを送信し、
$scope
の $on
メソッドでイベントを受信する方法です。
実装例を示します。
<!DOCTYPE HTML>
<html ng-app="AngularJsStudy">
<head>
<title>Pub/Sub - Share State</title>
<meta charset="utf-8" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" />
<link rel="stylesheet" href="app.css" />
</head>
<body>
<h4>Pub/Sub</h4>
<div class="container" ng-controller="Controller">
<h5>Controller 1</h5>
<input type="text" class="form-control" ng-model="text">
<button type="button" class="btn btn-default" ng-click="setText()">設定</button>
</div>
<hr/>
<div class="container" ng-controller="Controller">
<h5>Controller 2</h5>
<input type="text" class="form-control" ng-model="text">
<button type="button" class="btn btn-default" ng-click="setText()">設定</button>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<script src="app.js"></script>
</body>
</html>
(function() {
var app = angular.module("AngularJsStudy", []);
app.factory("SharedService", ["$rootScope", function($rootScope) {
var text = "Shared Text";
return {
text: {
get: function() { return text; },
set: function(t) {
console.log("[enter] text.set");
text = t;
$rootScope.$broadcast('changedText');
console.log("[leave] text.set");
}
}
};
}]);
app.controller("Controller", function($scope, SharedService) {
$scope.text = SharedService.text.get();
$scope.setText = function() {
console.log("[enter] setText");
SharedService.text.set($scope.text);
console.log("[leave] setText");
};
$scope.$on('changedText', function() {
console.log("[enter] changedText");
$scope.text = SharedService.text.get();
console.log("[leave] changedText");
});
});
}());
実装例ではすべてのコントローラにイベントが届くように $rootScope
の $broadcast()
メソッドを利用してイベントを送信しています。
参考
参考サイトによると directive を用いてモデルを共有する方法もあるようです。