AngularJS - Как сделать частичное с переменными?
Например, у меня есть частичное в car-list.html
и я хочу сделать это в нескольких местах с разными коллекциями автомобилей. Может быть, что-то вроде этого:
<h1>All New Cars</h1>
<div ng-include="car-list.html" ng-data-cars="allCars | onlyNew"></div>
<h1>All Toyotas</h1>
<div ng-include="car-list.html" ng-data-cars="allCars | make:toyota"></div>
Основное отличие от обычного состоит в том, что частичному не нужно ничего знать о том, какой список автомобилей он показывает. Ему дано множество автомобилей, и он их отображает. Возможно, как:
<!-- car-list.html -->
<div ng-repeat="car in cars" ng-controller="CarListControl">
{{car.year}} {{car.make}} {{car.model}}
</div>
3 ответа
Эта директива обеспечивает двустороннюю привязку данных между родительской областью и переименованными "локальными" переменными в дочерней области. Это может быть объединено с другими директивами, такими как ng-include
для потрясающего повторного использования шаблона. Требуется AngularJS 1.2.x
jsFiddle: AngularJS - включает частичное с локальными переменными
Разметка
<div with-locals locals-cars="allCars | onlyNew"></div>
В чем дело:
- Это в основном расширение
ngInclude
директива, позволяющая передавать переименованные переменные из родительской области.ngInclude
не требуется вообще, но эта директива была разработана, чтобы хорошо с ней работать. - Вы можете прикрепить любое количество
locals-*
атрибуты, которые все будут анализироваться и отслеживаться для вас как угловые выражения.- Эти выражения становятся доступными для включенного частичного, прикрепленного как свойства
$scope.locals
объект. - В приведенном выше примере
locals-cars="..."
определяет выражение, которое становится доступным как$scope.locals.cars
, - Подобно тому, как
data-cars="..."
атрибут будет доступен через JQuery с помощью.data().cars
- Эти выражения становятся доступными для включенного частичного, прикрепленного как свойства
Директива
РЕДАКТИРОВАТЬ Я рефакторинг, чтобы использовать (и быть независимым от) родной ngInclude
директива, и перенести некоторые вычисления в функцию компиляции для повышения эффективности.
angular.module('withLocals', [])
.directive('withLocals', function($parse) {
return {
scope: true,
compile: function(element, attributes, transclusion) {
// for each attribute that matches locals-* (camelcased to locals[A-Z0-9]),
// capture the "key" intended for the local variable so that we can later
// map it into $scope.locals (in the linking function below)
var mapLocalsToParentExp = {};
for (attr in attributes) {
if (attributes.hasOwnProperty(attr) && /^locals[A-Z0-9]/.test(attr)) {
var localKey = attr.slice(6);
localKey = localKey[0].toLowerCase() + localKey.slice(1);
mapLocalsToParentExp[localKey] = attributes[attr];
}
}
var updateParentValueFunction = function($scope, localKey) {
// Find the $parent scope that initialized this directive.
// Important in cases where controllers have caused this $scope to be deeply nested inside the original parent
var $parent = $scope.$parent;
while (!$parent.hasOwnProperty(mapLocalsToParentExp[localKey])) {
$parent = $parent.$parent;
}
return function(newValue) {
$parse(mapLocalsToParentExp[localKey]).assign($parent, newValue);
}
};
return {
pre: function($scope, $element, $attributes) {
// setup `$scope.locals` hash so that we can map expressions
// from the parent scope into it.
$scope.locals = {};
for (localKey in mapLocalsToParentExp) {
// For each local key, $watch the provided expression and update
// the $scope.locals hash (i.e. attribute `locals-cars` has key
// `cars` and the $watch()ed value maps to `$scope.locals.cars`)
$scope.$watch(
mapLocalsToParentExp[localKey],
function(localKey) {
return function(newValue, oldValue) {
$scope.locals[localKey] = newValue;
};
}(localKey),
true
);
// Also watch the local value and propagate any changes
// back up to the parent scope.
var parsedGetter = $parse(mapLocalsToParentExp[localKey]);
if (parsedGetter.assign) {
$scope.$watch('locals.'+localKey, updateParentValueFunction($scope, localKey));
}
}
}
};
}
};
});
Вы можете достичь этого легко с directive
,
Что-то вроде того:
angular.module('myModule')
.directive('cars', function () {
return {
restrict: 'E',
scope: { 'cars': '=data' },
template: "<div ng-repeat='car in cars'>\n" +
" {{car.year}} {{car.make}} {{car.model}}\n" +
"</div>"
};
});
Тогда вы можете использовать это так:
<h1>All New Cars</h1>
<cars data="allCars | onlyNew"></cars>
<h1>All Toyotas</h1>
<cars data="allCars | make:toyota"></cars>
Вы можете найти больше информации о директивах здесь.
Я хотел бы предложить свое решение, которое имеет другой дизайн.
Идеальное использование для вас:
<div ng-include-template="car-list.html" ng-include-variables="{ cars: (allCars | onlyNew) }"></div>
Объект ng-include-variable добавляется в локальную область. Следовательно, он не засоряет вашу глобальную (или родительскую) область.
Вот ваша директива:
.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
}
)
(Это в Coffeescript)
IMO, ng-include немного странно. Наличие доступа к глобальной области действия уменьшает ее возможность повторного использования.