[AngularJS] 画面遷移時における値の受け渡し方法いろいろ

2014.07.10

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

angularjs125title

車輪開発大好きおたいがです。こんにちは。(挨拶)

今回は、画面遷移時にコントローラを跨いで値を受け渡す方法をまとめてみました。「良い」とされる例もあれば、「悪い」と思われる例もありますが、お付き合いください。

自前グローバルオブジェクトを使う

ある意味で最強なグローバルオブジェクト ( トップレベルのオブジェクト ) ですが、今回のような例で使用した場合、チーム内のコードレビューでお説教されることうけあい

サンプル

一応、サンプル貼っておきます。( 真似することはお勧めしません )

ソース

シンタックスハイライトもおかしなことになってしまう破壊力…

var Globals = {}; // ('A`)
(function () {
    angular.module('appName', ['ngRoute'], function($routeProvider) {
        $routeProvider
        .when('/fromView/', { controller: 'FromViewController', templateUrl: 'view/FromView.html' })
        .when('/toView/'  , { controller: 'ToViewController',   templateUrl: 'view/ToView.html'   });
    });
    angular.module('appName').controller('FromViewController', function($scope, $location) {
        $scope.buttonClick = function() {
            Globals.hoge = 'hoge : ' + new Date().getTime();
            $location.path('/toView/');
        };
    });
    angular.module('appName').controller('ToViewController', function($scope) {
        $scope.fromViewValue = Globals.hoge;
    });
}());

$rootScope ( 親 $scope ) のプロパティを使う

選択しがちな手段かと思いますが、プロパティ名の管理が必要になることと、プロパティ値の変更がビューやロジックに 関わる or 関わらない かで、良し悪しが分れると思います。個人的な意見ですが、画面遷移時に利用するのは少し違う気がします。

以下の例は、グローバルオブジェクトを使う方法と、ほとんど変わりありません。

ソース

(function () {
    angular.module('appName', ['ngRoute'], function($routeProvider) {
        $routeProvider
        .when('/fromView/', { controller: 'FromViewController', templateUrl: 'view/FromView.html' })
        .when('/toView/'  , { controller: 'ToViewController',   templateUrl: 'view/ToView.html'   });
    });
    angular.module('appName').controller('FromViewController', function($scope, $location, $rootScope) {
        $scope.buttonClick = function() {
            $rootScope.hoge = 'hoge : ' + new Date().getTime();
            $location.path('/toView/');
        };
    });
    angular.module('appName').controller('ToViewController', function($scope, $rootScope) {
        $scope.fromViewValue = $rootScope.hoge;
    });
}());

$routeParams を使う

$routeProvider でルーティング定義した URL パラメータを経由して値を受け渡す方法です。渡す値を URL に含めて良い場合に使われます。

ソース

(function () {
    angular.module('appName', ['ngRoute'], function($routeProvider) {
        $routeProvider
        .when('/fromView/',    { controller: 'FromViewController', templateUrl: 'view/FromView.html' })
        .when('/toView/'  ,    { controller: 'ToViewController',   templateUrl: 'view/ToView.html'   })
        .when('/toView/:hoge', { controller: 'ToViewController',   templateUrl: 'view/ToView.html'   });
    });
    angular.module('appName').controller('FromViewController', function($scope, $location) {
        $scope.buttonClick = function() {
            $location.path('/toView/' + 999 );
        };
    });
    angular.module('appName').controller('ToViewController', function($scope, $routeParams) {
        if (angular.isDefined($routeParams.hoge)) {
            $scope.fromViewValue = $routeParams.hoge;
        }
    });
}());

自前 Service コンポーネントを使う

先述のように URL にパラメータを含めることが許されない場合、または、ルーティング機能以外の方法 ( ngInclude の中身を切り替える…など ) で遷移して値を受け渡すとき、この手段は有用と個人的に考えています。

ソース

(function () {
    angular.module('appName', ['ngRoute'], function($routeProvider) {
        $routeProvider
        .when('/fromView/', { controller: 'FromViewController', templateUrl: 'view/FromView.html' })
        .when('/toView/'  , { controller: 'ToViewController',   templateUrl: 'view/ToView.html'   });
    });
    angular.module('appName').service('sharedObject', function() {
        var service = {
            hoge : null
        };
        return service;
    });
    angular.module('appName').controller('FromViewController', function($scope, $location, sharedObject) {
        $scope.buttonClick = function() {
            sharedObject.hoge = 'sharedObject : ' + Math.random();
            $location.path('/toView/');
        };
    });
    angular.module('appName').controller('ToViewController', function($scope, sharedObject) {
        $scope.fromViewValue = sharedObject.hoge;
    });
}());

$rootScope ( 親 $scope ) の
$on(), $broadcast(), $emit() メソッドを使う

$scope のプロパティに直接値を持たせるのではなく、$scope のイベントライフサイクル機能を使って値を受け渡す方法です。この方法に関しても、自前 Service コンポーネントと同じく有用と考えています。

サンプル

ソース

(function () {
    angular.module('appName', ['ngRoute'], function($routeProvider) {
        $routeProvider
        .when('/fromView/', { controller: 'FromViewController', templateUrl: 'view/FromView.html' })
        .when('/toView/'  , { controller: 'ToViewController',   templateUrl: 'view/ToView.html'   });
    });
    angular.module('appName').controller('MainController', function($scope, $interval) {
        var hogeChangeWatcher = $scope.$on('hogeChange', function(event, args) {
            var stop = $interval(
                function() {
                    $interval.cancel(stop);
                    $scope.$broadcast('fugaChange', args);
                }, 500
            );
        });
        var destroyWatcher =  $scope.$on('$destroy', function() {
            hogeChangeWatcher();
            destroyWatcher();
        });
    });
    angular.module('appName').controller('FromViewController', function($scope, $location) {
        $scope.buttonClick = function() {
            $scope.$emit('hogeChange', [Math.random()]);
            $location.path( '/toView/');
        };
    });
    angular.module('appName').controller('ToViewController', function($scope) {
        var fugaChangeWatcher = $scope.$on('fugaChange', function(event, args) {
            $scope.fromViewValue = args[0];
        });
        var destroyWatcher =  $scope.$on('$destroy', function() {
            fugaChangeWatcher();
            destroyWatcher();
        });
    });
}());

まとめ

5 種類の方法を紹介しましたが、いかがでしたでしょうか。当記事が、用途に応じて手段を上手く使い分けるための参考になれば幸いです。