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

Ng-repeat с ng-transclude внутри директивы

Я хочу создать список с пользовательским поведением при его изменении содержимого. Я пытаюсь создать директиву для этого, но я немного теряюсь в том, как объединить ng-transclude с директивой ng-repeat. Может ли кто-нибудь поставить меня на путь?

Html:

<div ng-app="myApp">
  <div ng-controller="ctrl">
    <mylist items="myItem in items">
       <span class="etc">{{myItem}}</span>
    </mylist>
  </div>
</div>

JavaScript:

angular.module('myApp', [])    

.controller('ctrl', function ($scope) {
  $scope.items = ['one', 'two', 'three'];
})    

.directive('mylist', function () {
  return {
    restrict:'E',
    transclude: 'element',
    replace: true,
    scope: true,
    template: [
      '<ul>',
        '<li ng-repeat="WhatGoesHere in items" ng-transclude></li>',
      '</ul>'
    ].join(''),
    link: function (scope, element, attr) {
      var parts = attr.items.split(' in ');
      var itemPart = parts[0];
      var itemsPart = parts[1];
      scope.$watch(itemsPart, function (value) {
        scope.items = value; 
      });      
    }
  }
});

У меня есть часть этого несколько работающего здесь

EDIT:

Критерии:

  • Шаблон элемента должен быть определен в представлении, а не в директиве, и он должен иметь доступ к свойству item в дочерней области. В идеале я хочу определить это, как это делается в директиве ng-repeat
  • Директива должна иметь доступ к списку, чтобы я мог установить правильные часы и изменить их. Если возможно, я хотел бы иметь легкий доступ к сгенерированным элементам DOM (я также могу сделать это с помощью element[0].querySelectorAll('ul>li') или что-то еще, он должен работать только в Chrome).
  • Если возможно, я хотел бы повторно использовать логику в директиве ng-repeat, потому что она уже делает многое из того, что я хочу. Я предпочитаю не копировать код. Я просто хочу увеличить его поведение, а не изменять его.
4b9b3361

Ответ 1

Решил проблему самостоятельно:

Я могу сделать это на этапе компиляции (jsfiddle), добавив атрибут ng-repeat при компиляции шаблона и подавая это содержание моего атрибута.

Html:

<div ng-app="myApp">
  <div ng-controller="ctrl">
    <mylist element="myItem in items">{{myItem}}</mylist>
  </div>
</div>

JavaScript:

var myApp = angular.module('myApp', [])

.controller('ctrl', function ($scope) {
  $scope.items = ['one', 'two', 'three'];
})

.directive('mylist', function ($parse) {
  return {
    restrict:'E',
    transclude: 'element',
    replace: true,
    scope: true,
    template: [
      '<ul>',
      '<li ng-transclude></li>',
      '</ul>'
    ].join(''),
    compile: function (tElement, tAttrs, transclude) {
      var rpt = document.createAttribute('ng-repeat');
      rpt.nodeValue = tAttrs.element;
      tElement[0].children[0].attributes.setNamedItem(rpt);
      return function (scope, element, attr) {
        var rhs = attr.element.split(' in ')[1];
        scope.items = $parse(rhs)(scope);
        console.log(scope.items);
      }        
    }
  }
});

Ответ 2

Альтернативный способ добиться этого следующим образом.

Index.html:

<html ng-app='myApp'>

<head>
    <title>AngularJS Transclude within Repeat Within Directive</title>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.18/angular.min.js"></script>
    <script src='index.js'></script>
</head>

<body ng-controller='myController'>
    <people>Hello {{person.name}}</people>
    <button name="button" ng-click="changeRob()">Change Rob</button>
</body>
</html>

index.js:

var myApp = angular.module( 'myApp', [] );

myApp.controller( 'myController', function( $scope ) {
    $scope.people = [
        { name: 'Rob'  },
        { name: 'Alex' },
        { name: 'John' }
    ];

    $scope.changeRob = function() {
        $scope.people[0].name = 'Lowe';
    }
});

myApp.directive( 'people', function() {
    return {
        restrict: 'E',

        transclude: true,
        template: '<div ng-repeat="person in people" transcope></div>',
    }
});

myApp.directive( 'transcope', function() {
    return {
        link: function( $scope, $element, $attrs, controller, $transclude ) {
            if ( !$transclude ) {
                throw minErr( 'ngTransclude' )( 'orphan',
                    'Illegal use of ngTransclude directive in the template! ' +
                    'No parent directive that requires a transclusion found. ' +
                    'Element: {0}',
                    startingTag( $element ));
            }
            var innerScope = $scope.$new();

            $transclude( innerScope, function( clone ) {
                $element.empty();
                $element.append( clone );
                $element.on( '$destroy', function() {
                    innerScope.$destroy();
                });
            });
        }
    };
}); 

Смотрите в действии в этом подобном плунжере. Основываясь на этом продолжительном обсуждении проблемы Github.

Ответ 3

Другие ответы, к сожалению, не работают с новейшей версией углового (я проверил 1.4), поэтому я думаю, что есть преимущество, чтобы поделиться этим jsbin, который я нашел:

var app = angular.module('app', [])
  .controller('TestCtrl', function($scope) {
    $scope.myRecords = ['foo', 'bar', 'baz'];
  });

app.directive('myDirective', function($compile) {
  var template = '<div id="inner-transclude" ng-repeat="record in records"></div>';

  return {
    scope: {
      records: '='
    },
    restrict: 'A',
    compile: function(ele) {
      var transclude = ele.html();
      ele.html('');

      return function(scope, elem) {
        var tpl = angular.element(template);
        tpl.append(transclude);

        $compile(tpl)(scope);

        elem.append(tpl);
      };
    }
  };
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.js"></script>


<div ng-app="app" ng-controller="TestCtrl">
  <div my-directive records="myRecords">
    ?: {{record}}
  </div>

</div>

Ответ 4

Передача не требуется, поскольку items содержит то, что нам нужно для создания шаблона. Иными словами, внутри элемента нет ничего, т.е. <mylist>nothing new here we need to transclude</mylist>. Кажется, что Angular будет делать $watch для нас тоже.

.directive('mylist', function () {
  return {
    restrict:'E',
    replace: true,
    scope: true,
    template: [
      '<ul>',
      '<li ng-repeat="myItem in items">{{myItem}}</li>',
      '</ul>'
    ].join('')
  }
});

HTML:

<mylist></mylist>

Fiddle.

Обратите внимание, что создание новой области необязательно, поэтому вы можете прокомментировать эту строку:

//scope: true,

Обновить. Возможно, вы можете создать область выделения:

scope: { items: '='},

HTML:

<mylist items=items></mylist>

Fiddle.

Update2: на основе дополнительной информации, предоставленной Jan:

Шаблон элемента должен быть определен в представлении... Я хотел бы повторно использовать логику в директиве ng-repeat

Хорошо, поэтому давайте поместим все это в представление и используем ng-repeat:

<ul mylist>
  <li ng-repeat="myItem in items">
    <span class="etc">{{myItem}}</span>
   </li>
</ul>

у него [директива] должен быть доступ к свойству item в дочерней области... Директива должна иметь доступ к списку, чтобы я мог установить правильные часы и изменить их.

Следуя вашей оригинальной скрипте, мы будем использовать нормальную область для дочерних элементов (т.е. дочерняя область будет прототипически наследоваться от родительской области): scope: true,. Это гарантирует, что директива имеет доступ к свойствам, определенным в области контроллера, например, items.

доступ к сгенерированным элементам DOM

Функция директивной ссылки имеет аргумент element. Поэтому в HTML выше элемент будет установлен в элемент <ul>. Таким образом, у нас есть доступ ко всем элементам DOM. Например, element.find('li') или element.children(). В приведенной ниже ссылке, я имею $watch массив items. Обратный вызов $watch имеет доступ к element, поэтому у вас есть доступ к сгенерированным элементам DOM. Журналы обратного вызова element.children() на консоли.

Fiddle.

Таким образом, чтобы добавить пользовательское поведение в список, просто нарисуйте директиву на ul или ol и уходите.