46

コンテンツが変更されたときのカスタム動作を備えたリストを作成したいと考えています。このためのディレクティブを作成しようとしましたが、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; 
      });      
    }
  }
});

私はこれの一部をここでいくらか働いています

編集:

基準:

  • アイテムのテンプレートは、ディレクティブではなくビューで定義する必要があり、子スコープのアイテム プロパティにアクセスできる必要があります。理想的には、これを ng-repeat ディレクティブで行われるように定義したい
  • 適切な監視を設定して変更できるように、ディレクティブはリストにアクセスできる必要があります。可能であれば、生成された DOM アイテムに簡単にアクセスしたいと思います ( element[0].querySelectorAll('ul>li')Chrome でのみ動作する必要があります)。
  • 可能であれば、ng-repeat ディレクティブのロジックを再利用したいと考えています。これは、既に多くのことを実行しているためです。できれば、コードをコピーしたくありません。動作を変更するのではなく、動作を拡張したいだけです
4

5 に答える 5

20

自分で問題を解決しました:

テンプレートのコンパイル時に属性を追加し、属性のコンテンツをフィードすることにより、コンパイルステップ(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);
      }        
    }
  }
});
于 2013-01-20T16:26:37.593 に答える
9

残念ながら、他の回答はAngularの最新バージョンでは機能しません(私がチェックし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>

于 2016-06-10T20:10:00.270 に答える
5

itemsテンプレートをレンダリングするために必要なものが含まれているため、トランスクルージョンは必要ありません。言い換えれば、要素の中には何もありません-つまり、 <mylist>nothing new here we need to transclude</mylist>。Angularは私たちのためにも$watchingを行うようです。

.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>

フィドル

新しいスコープの作成はオプションであるため、次の行をコメントアウトできることに注意してください。

//scope: true,

更新:オプションで、分離スコープを作成できます。

scope: { items: '='},

HTML:

<mylist items=items></mylist>

フィドル

Update2:Janから提供された追加情報に基づく:

アイテムのテンプレートはビューで定義する必要があります...ng-repeatディレクティブのロジックを再利用したいと思います

では、すべてをビューに入れて、ng-repeatを使用しましょう。

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

it [ディレクティブ]は子スコープのitemプロパティにアクセスできる必要があります...適切なウォッチを設定して変更できるように、ディレクティブはリストにアクセスできる必要があります

元のフィドルに続いて、通常の子スコープを使用します(つまり、子スコープは通常、親スコープから継承します)scope: true,。これにより、ディレクティブがコントローラーのスコープで定義されたプロパティにアクセスできるようになりますitems

生成されたDOMアイテムへのアクセス

ディレクティブのリンク関数にはelement引数があります。したがって、上記のHTMLでは、要素は要素に設定され<ul>ます。したがって、すべてのDOM要素にアクセスできます。例:、element.find('li')またはelement.children()。以下で参照されているフィドルでは、items配列を$watchしています。$ watchコールバックはにアクセスelementできるため、生成されたDOMアイテムにアクセスできます。element.children()コールバックはコンソールにログを記録します。

フィドル

要約すると、カスタム動作をリストに追加するには、ディレクティブをulまたはolに配置するだけで、すぐに使用できます。

于 2013-01-17T21:45:49.693 に答える