如何在AngularJS指令中进行动态模板操作?

时间:2014-05-23 07:36:43

标签: angularjs angularjs-directive

我刚刚开始使用Angular,并且正试图绕过适当的指令使用。我正在编写一个自定义指令,它接受一个对象数组并将其解析为可变数量的垂直div。它基本上是一个网格系统,其中元素被排列成堆叠的垂直列而不是行。 div的数量随着屏幕的宽度而动态变化,需要在div类中进行动态更改,并在页面调整大小时重建div列中数组元素的顺序。

当我将模板的内容用作普通的静态HTML时,一切都加载得很好。当您使用输入字段等时,过滤器会动态更改数据集

当我使用我的指令时,初始页面加载看起来很好。但是,动态过滤被破坏 - 它不再绑定到输入字段。更重要的是,在页面调整大小时,HTML根本无法编译,在DOM中留下空白屏幕和未编译的指令标记。

我不太了解Angular,可以解决这个问题。如果我不得不猜测,由于范围问题,听起来好像没有在页面$ compile上正确绑定。

注意:我知道为模板做字符串连接是不好的做法,但我只想在开始搞乱嵌套指令之前让事情有效。

编辑:这里是我前端代码的Github回购链接:https://github.com/danheidel/education-video.net/tree/master/site

HTML

<body ng-controller="channelListController">
  Creator: <input ng-model="query.creators">
  Tags: <input ng-model="query.tags">
  query: {{query}}
  <div id="channel-view">
    <channel-drawers channels="channels"></channel-drawers>
  </div>
</body>

JS

.controller('channelListController', function ($scope, $http){
  $http.get('api/v1/channels').success(function(data){
    $scope.channels = data;
  });
})
.directive('channelDrawers', function($window, $compile){
  return{
    restrict: 'E',
    replace: true,
    scope: {
      channels: '='
    },
    controller: 'channelListController',
    //templateUrl: 'drawer.html',
    link: function(scope, element, attr){
      scope.breakpoints = [
        {width: 0, columns: 1},
        {width: 510, columns: 2},
        {width: 850, columns: 3},
        {width: 1190, columns: 4},
        {width: 1530, columns: 5}
      ];

      angular.element($window).bind('resize', setWindowSize);
      setWindowSize();  //call on init

      function setWindowSize(){
        scope.windowSize = $window.innerWidth;
        console.log(scope.windowSize);
        _.forEach(scope.breakpoints, function(point){
          if(point.width <= scope.windowSize){
            scope.columns = point.columns;
          }
        });
        var tempHtml = '';
        for(var rep=0;rep<scope.columns;rep++){
          tempHtml +=
          '<div class="cabinet' + scope.columns + '">' +
            '<div class="drawer" ng-class="{' + ((rep%2 === 0)?'even: $even, odd: $odd':'even: $odd, odd:$even') + '}" ng-repeat="channel in channels | looseCreatorComparator : query.creators | looseTagComparator : query.tags | modulusFilter : ' + scope.columns + ' : ' + rep + '">' +
              '<ng-include src="\'drawer.html\'"></ng-include>' +
            '</div>' +
          '</div>';
        }
        console.log(tempHtml);
        element.html(tempHtml);
        $compile(element.contents())(scope);
      }
    }
  };
})

3 个答案:

答案 0 :(得分:1)

当您想要操作模板时,应该实现$ compile函数。当您想要将模板绑定到范围和/或设置任何观察者时,应实现链接功能。如果您有动态HTML,并将其插入到DOM中,请向自己询问这些问题:

  1. 您要修改模板吗?如果是,则创建一个元素模板(angular.element(...))并将其附加到元素参数。

  2. 您是否修改了模板(步骤1)并且您的模板包含应该从其他模板绑定的绑定表达式,插值表达式和/或属性?如果是这样,您需要编译并链接从步骤1创建的元素。

  3. 以下是一个例子:

          .directive('myDirective',function($compile) {
                restrict: 'E',
                scope: '=',
                compile: function(element, attr) {
                      // manipulating template?
                      var e = angular.element('<div ng-model="person">{{person.name}}</div>');
                      element.append(e);
    
                      // the following is your linking function
                      return function(scope, element, attr) {
                           // template contains binding expressions? Yes
                           $compile(e)(scope);
                      };
                }
          });
    

    要修复代码,请尝试将模板操作移动到编译函数,并在链接函数中调用$ compile(e)(范围)。

答案 1 :(得分:1)

首先,感谢pixelbits和pfooti的输入。它让我走上正轨。但是,我希望答案能够成为一个清白的答案,因为我们的讨论涉及的技术问题最终与实际答案相关。

基本上,这个问题框架很差。在做了更多阅读之后,很明显我正在使用Angular元素,而这些元素并非真正用于它们。

在这种情况下,我在我的指令中做了一堆模型操作,它确实应该在控制器中发生。我最终这样做,并将窗口调整大小处理程序代码移动到另一个组件。

现在,我甚至不需要指令来正确格式化我的数据。一些带有ng-class和ng-style风格的嵌套ng-repeats在2行HTML中完成了这项工作。

<div ng-repeat="modColumn in splitChannels"
  ng-class="{'col-even' : !$even, 'col-odd' : !$odd}"
  ng-style="{ width: 99 / windowAttr.columns + '%' }"
  class="cabinet">
  <div ng-repeat="channel in modColumn"
    ng-class="{'row-even' : !$even, 'row-odd' : !$odd}"
    ng-cloak
    class="panel roundnborder">
    <ng-include src="'panel.html'"></ng-include>
  </div>
</div>

如果我能从一位Angular初学者那里得到一些建议,那就是:如果你的代码越来越复杂,或者你正在深入研究内部事物,请退后一步,重新思考你是如何使用角度组件。您可能正在执行应该在另一个组件类中执行的操作。正确的角度代码往往非常简洁,模块化和简单。

答案 2 :(得分:0)

您是否有理由使用模板进行此操作?

您是否可以将列数绑定到范围内的变量?我已经做了类似的事情,只是摆弄ng-if指令来隐藏现在不重要的东西,或者将一般布局内容附加到当前作用域(我通常将其全部填入属性中$ scope.view)

还有很多这样的东西已经在css3的媒体选择器中运行了,根本不需要弄乱DOM。如果没有更清楚地了解你想要完成的事情,我不确定这是否是超级必要的。不止一种皮肤猫等方法。

否则,@ pixelbits是正确的 - 如果你直接摆弄DOM树,那需要在编译中进行 - 进入DOM的值进入链接。