使用Compile的AngularJS指令无法访问子元素

时间:2013-11-04 07:27:53

标签: angularjs angularjs-directive angularjs-scope

我的目的是创建一个可以将其子元素重新排列(不重新排序)到Bootstrap CSS Grid中的指令,但是我在访问子元素时遇到了很多困难。

我尝试过很多不同的东西,并研究过Compile vs Link vs Controller指令选项。我想我可能不得不在我的指令中将'compile'更改为'link'以使其工作,但我不确定该怎么做。

我有一个AngularJS directive on GitHub,它接受​​一个数组或参数对象来渲染一个简单或复杂的网格。

在下面的示例中,您可以看到layoutOptions.data = [3, 4],这意味着网格将在顶行中有3个单元格,在第二行中有4个单元格。这很有效。

第二步是我想将一些div作为指令的子元素呈现,并且指令将在创建时将它们放置在网格的单元格中。这由layoutOptions.content = ['apple', 'orange', 'pear', 'banana', 'lime', 'lemon', 'grape']显示,但这需要解耦,以便它可以是任何东西。

HTML输入

<div ng-app="blerg">
  <div ng-controller="DemoCtrl">
    <div class="container" hr-layout="layoutOptions">
      <div ng-transclude ng-repeat="fruit in layoutOptions.content">{{fruit}}</div>
    </div>
  </div>
</div>

期望(非实际)输出

实际输出如下,但不包括带有水果名称的内部DIV

<div class="container hr-layout" hr-layout="layoutOptions">
  <div class="row">
    <div class="col-md-4"><!-- from ng-repeat --><div>apple</div></div>
    <div class="col-md-4"><!-- from ng-repeat --><div>orange</div></div>
    <div class="col-md-4"><!-- from ng-repeat --><div>pear</div></div>
  </div>
  <div class="row">
    <div class="col-md-3"><!-- from ng-repeat --><div>banana</div></div>
    <div class="col-md-3"><!-- from ng-repeat --><div>lime</div></div>
    <div class="col-md-3"><!-- from ng-repeat --><div>lemon</div></div>
    <div class="col-md-3"><!-- from ng-repeat --><div>grape</div></div>
  </div>
</div>

在这里使用它的jsFiddle:http://jsfiddle.net/harryhobbes/jJDZv/show/

代码

angular.module('blerg', [])
  .controller('DemoCtrl', function($scope, $timeout) {
    $scope.layoutOptions = {
      data: [3, 4],
      content: ['apple', 'orange', 'pear', 'banana', 'lime', 'lemon', 'grape']
    };
  })
  .directive("hrLayout", [
    "$compile", "$q", "$parse", "$http", function ($compile, $q, $parse, $http) {
      return {
        restrict: "A",
        transclude: true,
        compile: function(scope, element, attrs) {
          //var content = element.children();
          return function(scope, element, attrs) {
            var contentCount = 0;
            var renderTemplate = function(value, content) {
              if (typeof content === 'undefined' || content.length <= contentCount)
                var cellContent = 'Test content(col-'+value+')';
              else if (Object.prototype.toString.call(content) === '[object Array]')
                var cellContent = content[contentCount];
              else
                var cellContent = content;

              contentCount++;
              return '<div class="col-md-'+value+'">'+cellContent+'</div>';
            };

            var renderLayout = function(values, content) {
              var renderedHTML = '';
              var rowCnt = 0;
              var subWidth = 0;

              angular.forEach(values, function(value) {
                renderedHTML += '<div class="row">';
                if(Object.prototype.toString.call(value) === '[object Array]') {
                  angular.forEach(value, function(subvalue) {
                    if(typeof subvalue === 'object') {
                      renderedHTML += renderTemplate(
                        subvalue.w.substring(4), renderLayout(subvalue.d)
                      );
                    } else {
                      renderedHTML += renderTemplate(subvalue.substring(4));
                    }
                  });
                } else {
                  if(value > 12) {
                    value = 12;
                  } else if (value <= 0) {
                    value = 1;
                  }
                  subWidth = Math.floor(12 / value);
                  for (var i=0; i< value-1; i++) {
                    renderedHTML += renderTemplate(subWidth);
                  }
                  renderedHTML += renderTemplate((12-subWidth*(value-1)));
                }
                renderedHTML += '</div>';
                rowCnt++;
              });
              return renderedHTML;
            };

            scope.$watch(attrs.hrLayout, function(value) {
              element.html(renderLayout(value.data));
            });
            element.addClass("hr-layout");
          };
        }
    };
  }]);

2 个答案:

答案 0 :(得分:0)

你的方法看起来太复杂了。也许您应该每次使用 ngRepeat 指令http://docs.angularjs.org/api/ng.directive:ngRepeat orderBy 过滤http://docs.angularjs.org/api/ng.filter:orderBy时更新元素的html strong> hrLayout 已更新?

答案 1 :(得分:0)

这可能有所帮助 - http://jsfiddle.net/PwNZ5/1/

App.directive('hrLayout', function($compile) {
    return {
        restrict: 'A',

        // allows transclusion
        transclude: true,

        // transcludes the content of an element on which hr-layout was placed
        template: '<div ng-transclude></div>',

        compile: function(tElement, tAttrs, transcludeFn) {
            return function (scope, el, tAttrs) {
                var data = scope.$eval(tAttrs.hrLayout),
                    dom = '';

                transcludeFn(scope, function cloneConnectFn(cElement) {
                    // hide the transcluded content
                    tElement.children('div[ng-transclude]').hide();

                    // http://ejohn.org/blog/how-javascript-timers-work/‎
                    window.setTimeout(function() {
                        for(var row = 0; row < data.data.length; row++) {
                            dom+= '<div class="row">';
                            for(var col = 0; col < data.data[row]; col++) {
                                dom+= '<div class="col-md-' + data.data[row] + '">' + tElement.children('div[ng-transclude]').children(':eq(' + ( row + col ) + ')').html() + '</div>';
                            }
                            dom+= '</div>';
                        }

                        tElement.after(dom);
                    }, 0);
                });
            };
        }
    };
});