Ответ 1
Я переписал его в директиву и привязал необходимое значение в области с помощью
scope: {
item: '='
}
У меня есть контроллер, который я написал, который я использую в нескольких местах в своем приложении с ng-include
и ng-repeat
, например:
<div
ng-repeat="item in items"
ng-include="'item.html'"
ng-controller="ItemController"
></div>
В контроллере/шаблоне я ожидаю, что значение item
будет существовать, и все это построено вокруг этой идеи. Теперь, однако, мне нужно использовать контроллер несколько иначе, без ng-repeat
, но все же нужно иметь возможность передать в item
. Я видел ng-init
и думал, что он может делать то, что мне нужно, например:
<div
ng-init="item = leftItem"
ng-include="'item.html'"
ng-controller="ItemController"
></div>
<div
ng-init="item = rightItem"
ng-include="'item.html'"
ng-controller="ItemController"
></div>
Но это, похоже, не работает. У кого-нибудь есть идеи, как я могу передать переменную для области в единственном экземпляре, подобном этому?
Изменить:
Контроллер над этим загружается в значениях leftItem
и rightItem
, примерно так:
.controller('MainController', function($scope, ItemModel) {
ItemModel.loadItems()
.then(function(items) {
$scope.$apply(function() {
$scope.leftItem = items.left;
$scope.rightItem = items.right;
});
});
});
Я переписал его в директиву и привязал необходимое значение в области с помощью
scope: {
item: '='
}
Вы можете использовать атрибут onload
, который предоставляет ngInclude
для этого:
<div ng-include="'item.html'"
ng-controller="ItemController"
onload="item = rightItem">
</div>
ИЗМЕНИТЬ
Попробуйте сделать что-то подобное в родительской области:
$scope.dataHolder = {};
Затем, когда получены асинхронные данные, сохраните данные на dataHolder
:
$scope.dataHolder.leftItem = items.leftItem;
$scope.dataHolder.rightItem = items.rightItem;
Теперь, когда ng-include
загружает шаблон, он создаст дочернюю область, которая наследует свойства родителя. Таким образом, $scope.dataHolder
будет определяться в этой дочерней области (первоначально как пустой объект). Но когда ваши асинхронные данные принимаются, ссылка на пустой объект должна содержать вновь полученные данные.
Поздно к вечеринке, но есть немного angular 'hack' для достижения этого без реализации немой директивы.
Добавление встроенной директивы, расширяющей область вашего контроллера (например, ng-if), где вы используете ng-include, фактически позволит вам изолировать имя переменной для всех включенных областей.
Итак:
<div ng-include="'item.html'"
ng-if="true"
onload="item = rightItem">
</div>
<div ng-include="'item.html'"
ng-if="true"
onload="item = leftItem">
</div>
Затем вы можете привязать свой шаблон item.html к переменной элемента несколько раз с помощью разных элементов.
Вот плункер для достижения желаемого
Проблема заключалась в том, что элемент продолжает меняться в области контроллера, который содержит только одну ссылку на переменную item, которая удаляется при каждой команде onload.
Представляя директиву, которая расширяет текущую область действия, вы можете иметь изолированную область для всех ng-include. Как следствие, ссылка на элемент сохраняется и уникальна во всех расширенных областях.
ЛЮБОВЬ @Ответ Танина. решает так много проблем сразу и очень изящно. Для тех из вас, как я, которые не знают Coffeescript, здесь javascript...
ПРИМЕЧАНИЕ. По причинам, которые я слишком новичок, чтобы понять, этот код требует, чтобы вы процитировали имя своего шаблона один раз, вместо требования ng-include, чтобы дважды указывать имена ваших шаблонов, т.е. <div ng-include-template="template-name.html" ... >
вместо <div ng-include-template="'template-name.html'" ... >
.directive('ngIncludeTemplate', function() {
return {
templateUrl: function(elem, attrs) { return attrs.ngIncludeTemplate; },
restrict: 'A',
scope: {
'ngIncludeVariables': '&'
},
link: function(scope, elem, attrs) {
var vars = scope.ngIncludeVariables();
Object.keys(vars).forEach(function(key) {
scope[key] = vars[key];
});
}
}
})
Использование onload не является чистым решением, поскольку оно помещает глобальную область. Если у вас что-то более сложное, оно начнет сбой.
ng-include не является повторным, поскольку он имеет доступ к глобальной области. Это немного странно.
Вышеизложенное неверно. ng-if с onload не мешает глобальной области
Мы также не хотим писать конкретную директиву для каждой ситуации.
Создание общей директивы вместо ng-include является более чистым решением.
Идеальное использование выглядит следующим образом:
<div ng-include-template="'item.html'" ng-include-variables="{ item: 'whatever' }"></div>
<div ng-include-template="'item.html'" ng-include-variables="{ item: variableWorksToo }"></div>
Директива:
.directive(
'ngIncludeTemplate'
() ->
{
templateUrl: (elem, attrs) -> attrs.ngIncludeTemplate
restrict: 'A'
scope: {
'ngIncludeVariables': '&'
}
link: (scope, elem, attrs) ->
vars = scope.ngIncludeVariables()
for key, value of vars
scope[key] = value
}
)
Вы можете видеть, что директива не использует глобальную область действия. Вместо этого он считывает объект из ng-include-variables и добавляет эти члены в свою собственную локальную область.
Надеюсь, это то, что вам хотелось бы; он чистый и общий.
ng-init лучше для этого, я думаю.
<div ng-include='myFile.html' ng-init="myObject = myCtrl.myObject; myOtherObject=myCtrl.myOtherObject"/>
Выше не будет работать для атрибутов второго уровня, например <div ng-include-template=... ng-include-variables="{ id: var.id }">
. Обратите внимание на var.id
.
Обновленная директива (уродливая, но работает):
.directive('ngIncludeTemplate', function() {
return {
templateUrl: function(elem, attrs) { return attrs.ngIncludeTemplate; },
restrict: 'A',
scope: {
'ngIncludeVariables': '&'
},
link: function(scope, elem, attrs) {
var cache = scope.ngIncludeVariables();
Object.keys(cache).forEach(function(key) {
scope[key] = cache[key];
});
scope.$watch(
function() {
var val = scope.ngIncludeVariables();
if (angular.equals(val, cache)) {
return cache;
}
cache = val;
return val;
},
function(newValue, oldValue) {
if (!angular.equals(newValue, oldValue)) {
Object.keys(newValue).forEach(function(key) {
scope[key] = newValue[key];
});
}
}
);
}
};
});
Возможно очевидное обновление для ответов Майка и Танина - если вы используете встроенные шаблоны как:
<script type="text/ng-template" id="partial">{{variable}}</script>
<div ng-include-template="'partial'" ng-include-variables="{variable: variable}"></div>
Затем в директиве ngIncludeTemplate замените
templateUrl: function(elem, attrs) { return attrs.ngIncludeTemplate; },
С
template: function(elem, attrs) { return document.getElementById(attrs.ngIncludeTemplate.split("'")[1]).innerHTML },