385
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

Angular JS で複数のコントローラ間でモデル(状態や値)を共有する方法 3 種類

はじめに

Angular JS で複数のコントローラ間でモデル(状態や値)を共有する方法として、次の 3 種類を解説します。

  • モデルを共有するサービスを使用する (Shared Service)。
  • 親コントローラのスコープを子コントローラで共有する (Parent Scope Sharing)。
  • イベントを利用する (Pub/Sub)。

Shared Service

複数のコントローラ間で共有するモデルをサービスとして作成し、そのサービスを複数のコントローラで参照します。
実装例を示します。

app.html
<!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>
app.js
(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;
    });
}());

ShareControllerAShareControllerB では、スコープ $scopedataSharedStateService をバインドしています。
こうすることで、AngularJS の一番の特長であるビューとモデルの双方向バインドを効果的に利用できます。

Parent Scope Sharing

AngularJS ではコントローラをネストすることができます。
コントローラをネストした場合、子要素のコントローラは、親要素のコントローラのスコープを参照することができます。
実装例を示します。

app.html
<!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>
app.js
(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 メソッドでイベントを受信する方法です。
実装例を示します。

app.html
<!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>
app.js
(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 を用いてモデルを共有する方法もあるようです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
385
Help us understand the problem. What are the problem?