Два взгляда в одном из разделов общего доступа в режиме AngularUI Router

Я новичок в AngularUI Router и хотел бы использовать его для следующего сценария:

Макет , общий для всех страниц, включает верхнюю панель навигации, содержащую меню с кнопками справа и раздел содержимого, заполняющий пробел ниже. На странице есть несколько страниц, которые я сопоставляю с состояниями интерфейса 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>

Сейчас я использую два параллельных представления и два контроллера для каждого состояния. Но этот способ не может взаимодействовать с двумя областями/контроллерами.

Итак, как бы вы это сделали?

Ответы

Ответ 1

$scope - это не модель, ее ссылка на модель, склейка между данными и представлением. Если $scopes в двух или более контроллерах необходимо разделить одну модель/состояние/данные, используйте экземпляр объекта singleton, зарегистрировав службу angular. Эта одна услуга /factory может быть введена в любое количество контроллеров, как вам нравится, и тогда все может сработать с одним источником истины.

Вот пример 1 factory, связывающий $scopes в navbar и body с ui-router 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>'
    }
  }
})

Factory и контроллеры:

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;
    }
]);

Как вы можете видеть, объект factory {uiObject: {data: null}} вводится в контроллер с помощью uiFieldState, а затем его просто $scope.selected = uiFieldState.uiObject; для подключения factory к области ng-model="selected.data" .

Ответ 2

Вы должны использовать:

$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);
        });
    }
]);

Вы можете передать $rootScope вместо использования метода $scopes $onRootScope, который мы определили в конфиге. Однако это не рекомендуется использовать $emit и $onRootScope.

Вместо использования $emit вы всегда можете использовать $broadcast. Это, тем не менее, даст вам очень большие проблемы с производительностью, так как ваше приложение растет. Так как это пузырьки данных через все контроллеры.

Если ваши контроллеры являются родителями и дочерьми друг к другу, им не нужно использовать $rootScope.

Вот и пример разницы между $emit и $broadcast: jsFiddle

И их действительно отличия в производительности:

enter image description here