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 немного странно. Наличие доступа к глобальной области действия уменьшает ее возможность повторного использования.

Другие вопросы по тегам