Подтвердить что ты не робот

Angular передача области для 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;
            });
        });
});
4b9b3361

Ответ 1

Я переписал его в директиву и привязал необходимое значение в области с помощью

scope: {
    item: '='
}

Ответ 2

Вы можете использовать атрибут 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 будет определяться в этой дочерней области (первоначально как пустой объект). Но когда ваши асинхронные данные принимаются, ссылка на пустой объект должна содержать вновь полученные данные.

Ответ 3

Поздно к вечеринке, но есть немного 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. Как следствие, ссылка на элемент сохраняется и уникальна во всех расширенных областях.

Ответ 4

ЛЮБОВЬ @Ответ Танина. решает так много проблем сразу и очень изящно. Для тех из вас, как я, которые не знают 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];  
      });  
    }  
  }  
})

Ответ 5

Использование 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 и добавляет эти члены в свою собственную локальную область.

Надеюсь, это то, что вам хотелось бы; он чистый и общий.

Ответ 6

ng-init лучше для этого, я думаю.

<div ng-include='myFile.html' ng-init="myObject = myCtrl.myObject; myOtherObject=myCtrl.myOtherObject"/>

Ответ 7

Выше не будет работать для атрибутов второго уровня, например <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];
            });
          }
        }
      );

    }
  };
});

Ответ 8

Возможно очевидное обновление для ответов Майка и Танина - если вы используете встроенные шаблоны как:

<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 },