it-swarm.com.ru

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;
            });
        });
});
37
kbjr

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

scope: {
    item: '='
}
17
kbjr

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

33
Sunil D.

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

Вот плункер, чтобы достичь того, что вы хотите

Проблема заключалась в том, что элемент постоянно изменяется в области видимости контроллера, которая содержит только одну ссылку на переменную элемента, которая стирается при каждой инструкции загрузки.

Введение директивы, которая расширяет текущую область, позволяет вам иметь изолированную область для всех ng-include. Как следствие, ссылка на предмет сохраняется и является уникальной во всей расширенной области.

32
Piou

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

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

Я надеюсь, что это то, что вы хотели бы; это чисто и универсально.

11
Tanin

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

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

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

    }
  };
});
3
Viktor Aseev

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

<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 },
1
Zmey Petrov