1

「パネル」と呼ばれるこのカスタム ディレクティブがあります。

<panel title="One Title">
    One Body
    <panel title="Two Title">two</panel>
</panel>

私の問題は、ネストされたパネル ディレクティブをレンダリングできないことです。javascript については、plunkr http://plnkr.co/edit/D0LfQqBViuraSNfmym4g?p=previewを参照してください。

私が期待する

<div>
    <h1>One Title</h1>
    <div>
        One Body
        <div>
            <h1>Two Title</h1>
             <div>Two Body</div>
            </div>
        </div>
    </div>
</div>

しかし、代わりに私は得る

<div>
    <h1>One Title</h1>
    <div>One Body</div>
</div>

ご覧のとおり、私の目的は、dom を操作するのではなく、コントローラーから提供されたデータから出力をレンダリングすることです。データを収集してコントローラーに提供する手段としてディレクティブを使用することを検討しています。これにより、コントローラーによって提供されたデータからテンプレートをレンダリングできるようになります。その結果、div で ng-transclude を使用せず、代わりに $compile または transclude(scope, fun...) の組み合わせを使用して、記載された目標を達成するソリューションを探しています。私の目標は、$compile と transclude(scope, fun...) を効果的に使用する方法をよりよく理解することでもあります。

4

2 に答える 2

1

に頼るつもりなので、これは簡単ではありませんng-bind-html

最初にこれを見てみましょう:

transclude(scope, function(clone, scope){
  panelCtrl.setBody($sce.trustAsHtml(clone.html()));  
});

上記cloneの関数には、次のような子panelディレクティブのコメント プレースホルダーが含まれます。

One Body
<!-- panel: undefined -->

これは、子パネル ディレクティブに がtransclude: 'element'あり、そのリンク関数がまだ実行されていないためです。

これを修正するのは簡単です。コードを少し変更するだけです。

var clone = transclude(scope, function () {});
panelCtrl.setBody($sce.trustAsHtml(clone.html()));

このようにして、clone次のような実際のテンプレートが含まれます。

One Body
<div class="ng-scope"><h1 class="ng-binding">{{panel.title}}</h1><div class="inner ng-binding" ng-bind-html="panel.body"></div></div>

それほど驚くことではありませんが、実際のテンプレートができましたが、バインディングはまだ行われていないためclone.html()、現時点ではまだ使用できません。

そして、本当の問題が始まります:バインディングがいつ終了するかをどうやって知ることができますか?

私の知る限り、正確な時期はわかりません。しかし、これを回避するには、$timeout!

を使用することにより$timeout、通常のコンパイル サイクルが中断されるため、子ディレクティブのバインディングが完了したことを親パネル ディレクティブに知らせる何らかの方法を見つける必要があります ( $timeout)。

1 つの方法は、通信にコントローラーを使用することで、最終的なコードは次のようになります。

app.controller('PanelCtrl', function($scope, $sce) {    
  $scope.panel = {
    title: 'ttt',
    body: $sce.trustAsHtml('bbb'),
  }

  this.setTitle = function(title) {
    $scope.panel.title = title;
  };

  this.setBody = function(body) {
    $scope.panel.body = body;
  };

  var parentCtrl,
      onChildRenderedCallback,
      childCount = 0;

  this.onChildRendered = function(callback) {
    onChildRenderedCallback = function () {
      callback();

      if (parentCtrl) {
        $timeout(parentCtrl.notify, 0);
      }
    };

    if (!childCount) {
      $timeout(onChildRenderedCallback, 0);
    }
  };

  this.notify = function() {
    childCount--;

    if (childCount === 0 && onChildRenderedCallback) {
      onChildRenderedCallback();
    }
  };

  this.addChild = function() {
    childCount++;
  };

  this.setParent = function (value) {
    parentCtrl = value;
    parentCtrl.addChild(this);
  };
});

app.directive('panel', function($compile, $sce, $timeout) {
  return {
    restrict: 'E',
    scope: {},
    replace: true,
    transclude: 'element',
    controller: 'PanelCtrl',
    require: ['panel', '?^panel'],
    link: function(scope, element, attrs, ctrls, transclude) {
      var panelCtrl = ctrls[0];
      var parentCtrl = ctrls[1];

      if (parentCtrl) {
        panelCtrl.setParent(parentCtrl);
      }

      var template =
        '<div>' +
        '  <h1>{{panel.title}}</h1>' +
        '  <div class="inner" ng-bind-html="panel.body"></div>' +
        '</div>';

      var templateContents = angular.element(template);
      var compileTemplateContents = $compile(templateContents);
      element.replaceWith(templateContents);

      panelCtrl.setTitle(attrs.title);

      var clone = transclude(scope, function () {});

      panelCtrl.onChildRendered(function() {
        panelCtrl.setBody($sce.trustAsHtml(clone.html()));
        compileTemplateContents(scope);
      });
    }
  }
});

プランカーの例: http://plnkr.co/edit/BBbWsUkkebgXiAdcnoYE?p=preview

plunker に多くのものを残しましたconsole.log()。実際に何が起こっているかを確認できます。

PS。ng-bind-html使用せずに DOM 操作を許可するか、@WilliamScott の回答のようなものを使用すると、作業がはるかに簡単になります。

于 2014-08-11T18:39:55.023 に答える
0

ディレクティブを単純化すると、次のようになります。

app.directive('panel', function(){
  return {
    restrict: 'E',
    template:'<div><h1>{{panel.title}}</h1><div ng-transclude></div></div>',
    scope: {},
    transclude: true,
    controller: 'PanelCtrl',
    link: function(scope, element, attrs, panelCtrl)
    {
      panelCtrl.setTitle(attrs.title);
    }
  }  
})

これがplunkrです。

于 2014-08-11T15:31:30.183 に答える