web-dev-qa-db-ja.com

1つのAngularUIルーターの状態共有スコープ内の2つのビュー

私はAngularUIルーターにかなり慣れていないので、次のシナリオで使用したいと思います。

レイアウトすべてのページに共通上部のナビゲーションバーには、右側にボタンのあるメニューと、下のスペースを埋めるコンテンツセクションが含まれています。このページには、UIルーターの状態にマップするいくつかのページがあります(ページ1、ページ2、...)。各ページには、独自のメニュー項目と独自のコンテンツを含めることができます。 メニューはスコープをコンテンツと共有する必要があります相互作用するため(たとえば、保存ボタンはコンテンツ内のフォームを送信します。フォームが有効な場合にのみ有効にする必要があります)。

mockup

HTMLはおおまかに次のようになります。

<body>
    <nav class="...">
        <h1>my site</h1>
        <div>MENU SHOULD GO HERE</div>
    </nav>
    <div class="row">
        <div class="column ...">
            CONTENT SHOULD GO HERE
        </div>
    </div>        
</body>

現在、状態ごとに2つの並列ビューと2つのコントローラーを使用しています。しかし、このように、2つのスコープ/コントローラーは相互作用できません。

では、どのようにそれを達成しますか?

16
jack_kerouac

$ scopeはモデルではなく、モデルへの参照であり、データとビューの間に接着します。 2つ以上の$ scopesで、コントローラーが1つのモデル/状態/データを共有する必要がある場合は、angularサービスを登録することにより、シングルトンオブジェクトインスタンスを使用します。あなたが好きなようにコントローラー、そしてそれからすべてはその1つの真実の源からうまくいくことができます。

これは、navbarとbodyの$ scopesをui-routerにリンクする1つのファクトリのデモです http://plnkr.co/edit/P2UudS?p=preview (左側のタブのみ)

ui-router(viewCはnavbarです):

$stateProvider
.state('left', {
  url: "/",
  views: {
    "viewA": {
      controller: 'LeftTabACtrl',
      template: 'Left Tab, index.viewA <br>' +
                '<input type="checkbox" ng-model="selected2.data" />' +
                '<pre>selected2.data: {{selected2.data}}</pre>'
    },
    {...},
    "viewC": {
      controller: 'NavbarCtrl',
      template: '<span>Left Tab, index.viewC <div ui-view="viewC.list"></div>' +
                '<input type="checkbox" ng-model="selected.data" />' +
                '<pre>selected.data: {{selected.data}}</pre></span>'
    }
  }
})

工場とコントローラー:

app.factory('uiFieldState', function () {
    return {uiObject: {data: null}}
});

app.controller('NavbarCtrl', ['$scope', 'uiFieldState', '$stateParams', '$state',
    function($scope, uiFieldState, $stateParams, $state) {
        $scope.selected = uiFieldState.uiObject;
    }
]);

app.controller('LeftTabACtrl', ['$scope', 'uiFieldState', '$stateParams', '$state',
    function($scope, uiFieldState, $stateParams, $state) {
        $scope.selected2 = uiFieldState.uiObject;
    }
]);

ご覧のとおり、ファクトリオブジェクト{uiObject: {data: null}}uiFieldStateでコントローラーに注入され、次にその単純な$scope.selected = uiFieldState.uiObject;ファクトリをスコープに接続するためのng-model="selected.data" .`

22
cheekybastard

次を使用する必要があります。

$on and $emit

データを送信するエミットコントローラー。

angular.module('MyApp').controller('MyController', ['$scope', '$rootScope', function ($scope, $rootScope){

  $rootScope.$emit('SomeEvent', data);
}]);

$ rootScopeを安全な方法で実装して、使用後にものを破棄して修正する方法の例:

angular
.module('MyApp')
.config(['$provide', function($provide){
    $provide.decorator('$rootScope', ['$delegate', function($delegate){

        Object.defineProperty($delegate.constructor.prototype, '$onRootScope', {
            value: function(name, listener){
                var unsubscribe = $delegate.$on(name, listener);
                this.$on('$destroy', unsubscribe);
            },
            enumerable: false
        });


        return $delegate;
    }]);
}]);

そして、処理する必要のあるデータを持つコントローラー。

angular.module('MyApp')
.controller('MySecondController', ['$scope', function MyController($scope) {

        $scope.$onRootScope('SomeEvent', function(event, data){
            console.log(data);
        });
    }
]);

構成で定義した$ scopesメソッド$ onRootScopeを使用する代わりに、$ rootScopeを渡すことができます。ただし、これは$ emitおよび$ onRootScopeを使用するための推奨される方法ではありません。

$ emitを使用する代わりに、いつでも$ broadcastを使用できます。ただし、これにより、アプリの成長に伴って非常に大きなパフォーマンスの問題が発生します。すべてのコントローラーを介してデータをバブルするため。

コントローラが相互に親と子である場合、コントローラは$ rootScopeを使用する必要はありません。

$ emitと$ broadcastの違いの例を次に示します。 jsFiddle

そして、それらは本当にパフォーマンスの違いです。

enter image description here

5
petur