Ui router - вложенные представления с общим контроллером
У меня есть абстрактный родительский вид, предназначенный для совместного использования контроллера со своими вложенными представлениями.
.state('edit', {
abstract: true,
url: '/home/edit/:id',
templateUrl: 'app/templates/editView.html',
controller: 'editController'
})
.state('edit.details', {
url: '/details',
templateUrl: 'app/templates/editDetailsView.html'
})
.state('edit.info', {
url: '/info',
templateUrl: 'app/templates/editInfoView.html'
})
Маршрутизация работает как ожидалось.
Проблема заключается в том, что когда я обновляю переменную $scope
из одного из вложенных представлений, это изменение не отражается в представлении. Когда я делаю то же самое с родительского представления, он отлично работает. Это не ситуация, для которой требуется $apply
.
Я предполагаю, что для каждого представления создается новый экземпляр editController
, но я не уверен, почему и как его исправить.
Ответы
Ответ 1
Проблема здесь будет связана с этими Q и A: Как мне разделить данные области $между состояниями в angularjs ui-router?.
Способ решения проблемы скрыт в:
В AngularJS дочерняя область, обычно прототипически наследуемая от родительской области.
...
Наличие символа '.' в ваших моделях будет гарантировать, что прототипное наследование находится в игре.
// So, use
<input type="text" ng-model="someObj.prop1">
// rather than
<input type="text" ng-model="prop1">.
А также этот
Имейте в виду, что свойства области только наследуют цепочку состояний, если представления ваших состояний вложены. Наследование свойств области не имеет ничего общего с вложением ваших состояний и всего, что связано с вложением ваших представлений (шаблонов).
Вполне возможно, что у вас есть вложенные состояния, шаблоны которых заполняют u-views в разных не-вложенных местах на вашем сайте. В этом случае вы не можете ожидать доступа к переменным области представлений родительского состояния в представлениях дочерних состояний.
После этого мы должны сделать это в редакторе Controller
controller('editController', function ($scope) {
$scope.Model = $scope.Model || {SomeProperty : "xxx"};
})
И мы можем даже повторно использовать это controller: 'editController'
(мы не можем этого делать, потому что $scope.Model будет там - благодаря наследованию)
.state('edit', {
abstract: true,
url: '/home/edit/:id',
templateUrl: 'app/templates/editView.html',
controller: 'editController'
})
.state('edit.details', {
url: '/details',
templateUrl: 'app/templates/editDetailsView.html',
controller: 'editController'
})
.state('edit.info', {
url: '/info',
templateUrl: 'app/templates/editInfoView.html',
controller: 'editController'
})
Теперь тот же самый контроллер будет создаваться многократно (родительский все дети), но $scope.Model
будет инициирован только один раз (внутри родителя) и доступен везде
Проверьте этот аналогичный рабочий пример здесь
Ответ 2
На основе комментария PilotBob
Можно ли это сделать при использовании шаблона controllerAs
, дающего дочернему состоянию его собственный контроллер?
Я решил добавить другое решение, используя controllerAs
, сохраняя выше/оригинальную концепцию
Существует рабочий плункер
В состояниях теперь будут разные контроллеры, а родительское состояние будет называть его "parentCtrl" (не будет перезаписано в дочерней области с помощью дочернего контроллера)
.state("main", {
controller:'mainController',
controllerAs: "parentCtrl",
...
.state("main.1", {
parent: 'main',
controller:'child1Controller',
controllerAs: "ctrl",
...
.state("main.2", {
parent: 'main',
controller:'child2Controller',
controllerAs: "ctrl",
...
И это контроллеры:
.controller('mainController', function ($scope) {
this.Model = {Name : "yyy"};
})
.controller('child1Controller', function ($scope) {
$scope.Model = $scope.parentCtrl.Model;
})
.controller('child2Controller', function ($scope) {
$scope.Model = $scope.parentCtrl.Model;
})
Проверьте его в действии здесь
Ответ 3
Другая альтернатива, использующая resolve
.state('edit', {
abstract: true,
url: '/home/edit/:id',
templateUrl: 'app/templates/editView.html',
controller: 'editController',
resolve: {
baseData: function() {
return {};
}
}
})
.state('edit.details', {
url: '/details',
templateUrl: 'app/templates/editDetailsView.html',
controller: 'editController'
})
.state('edit.info', {
url: '/info',
templateUrl: 'app/templates/editInfoView.html',
controller: 'editController'
})
.controller('editController', function (baseData) {
baseData.foo = baseData.foo || 'bar';
});