Угловая область передачи в ng-include
У меня есть контроллер, который я написал, который я использую в нескольких местах в моем приложении с 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;
});
});
});
8 ответов
В итоге я переписал его в директиву и связал необходимое значение в области видимости
scope: {
item: '='
}
Поздно к вечеринке, но есть небольшой угловой взлом, чтобы добиться этого без реализации глупой директивы.
Добавление встроенной директивы, которая расширит область действия вашего контроллера (например, 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 несколько раз с разными элементами.
Вот плункер для достижения того, что вы хотите
Проблема заключалась в том, что элемент постоянно меняется в области видимости контроллера, которая содержит только одну ссылку на переменную элемента, которая стирается при каждой инструкции загрузки.
Введение директивы, которая расширяет текущую область, позволяет вам иметь изолированную область для всех ng-include. Как следствие, ссылка на предмет сохраняется и является уникальной во всей расширенной области.
Вы можете использовать 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
будет определен в этой дочерней области (первоначально как пустой объект). Но когда ваши асинхронные данные получены, ссылка на пустой объект должна содержать вновь полученные данные.
ЛЮБОВЬ @ ответ Танина. решает так много проблем одновременно и очень элегантно. Для тех из вас, как я, кто не знает 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-variable и добавляет эти члены в свою локальную область видимости.
Я надеюсь, что это то, что вы хотели бы; это чисто и универсально.
Выше не будет работать для атрибутов второго уровня, таких как <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];
});
}
}
);
}
};
});
Я думаю, что ng-init лучше для этого.
<div ng-include='myFile.html' ng-init="myObject = myCtrl.myObject; myOtherObject=myCtrl.myOtherObject"/>
Возможно очевидное обновление для ответов Майка и Танина - если вы используете встроенные шаблоны как:
<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 },