LoginSignup
383

More than 5 years have passed since last update.

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

Posted at

はじめに

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
383