在渲染模板之前解析AngularJS指令中的内容

时间:2014-03-27 12:04:57

标签: javascript angularjs angularjs-directive

我在页面上有一个由服务器呈现的元素。我们称之为播放列表。它将成为一个指令 <div playlist></div>

播放列表在Angular编译时间之前包含许多曲目
<div class="Track" data-track-name="Pompeii" data-*="etc">...</div>

加载页面后,我将包括AngularJS并解析播放列表和指令。

当我初始化播放列表指令时,我想在编译它的模板之前循环浏览它的内容并使用收集的数据来绘制跟踪属于播放列表指令的ng-repeat指令中的指令。

问题:

  1. 在模板替换指令标记内的任何内容之前,是否存在解析内容的本机功能?一旦我在指令控制器中,内容已被替换。

  2. 我的问题有更好的解决方案吗?遗憾的是,我不得不使用已在页面上呈现的内容作为数据源,作为该项目的大量需求的解决方案

  3. 我可以在服务器内容中使用track指令来避免DOM操作吗?

  4. 额外解释

    编译前:

    <div playlist>
       <div class="Track" data-track-name="Pompeii">Pompeii</div>
       <div class="Track" data-track-name="Weight of living">Weight of living</div>
    </div>
    

    编译后:

    <div playlist>
       <!-- from playlist template -->
       <div class="Playlist">
           <div class="Playlist-controls">....</div>
           <div class="Playlist-tracks">
               <div track ng-repeat="track in tracks">
                   <!-- from track template -->
                   <div class="Track">...</div>
                </div>
           </div>
    
       </div>
    </div>
    

2 个答案:

答案 0 :(得分:4)

假设这是您的数据结构,您可以在没有指令的情况下轻松完成所有操作:

数据结构/控制器

function ctrl($scope) {
$scope.Playlists = [
  { name: 'Playlist 1', tracks: [ 'Track 1', 'Track 2', ..., 'Track n'] },
  { name: 'Playlist 2', tracks: [ 'Track 1', 'Track 2', ..., 'Track n'] },
  ...
  { name: 'Playlist n', tracks: [ 'Track 1', 'Track 2', ..., 'Track n'] }
];
}

模板:

<div ng-controller="ctrl">
  <div ng-repeat="playlist in Playlists">
    <div> {{ playlist.name }} </div>
    <div ng-repeat="track in playlist.tracks">
       {{ track }}
    </div>
  </div>
</div>

[编辑]

由于您需要解析DOM,因此您可以使用此指令来执行此操作。它将解析DOM并将其放入控制器范围数组中。然后,您可以将此范围传递到模板或模板URL中。使用你需要的任何jQuery擦除原始DOM。 jsFiddle

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

myApp.directive('playlist', function() {
    return {
        restrict: 'A',
        scope: false,
        link: {
            pre: function(scope, element) {
                $('.Track').each(function(index, ele) {
                   scope.tracks.push($(ele).attr('data-track-name')); 
                });
                console.log(scope.tracks);
            },
            post: function(scope, element) {
              // do any compiling, etc...    
            }
        },
        controller: function($scope, $element) {
            $scope.tracks = [];
        }
    }
});

[编辑#2]包括拜尔的建议。添加此项以防将来有人发现这有用。 jsFiddle

模板:

<div ng-app="myApp">
    <script type="text/ng-template" id="tpl.html">
        <div>My playlist controls</div>
        <div ng-repeat="track in tracks">
           {{ track }}
        </div>
   </script>
    <div playlist>
       <div class="Track" data-track-name="Pompeii">Pompeii</div>
       <div class="Track" data-track-name="Weight of living">Weight of living</div>
    </div>
</div>

指令:

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

myApp.directive('playlist', function($compile, $templateCache) {
    var extractData = function(scope) {
        $('.Track').each(function(index, ele) {
            scope.tracks.push($(ele).attr('data-track-name'));
        });
    }

    return {
        replace: true,
        restrict: 'A',
        compile: function(ele, attr, ctrl) {
            return {
              pre: function preLink(scope, ele, attr, ctrl) {
                  extractData(scope);
              },
              post: function postLink(scope, ele, attr, ctrl) {
                  ele.html($templateCache.get("tpl.html"));
                  $compile(ele.contents())(scope);
              }
            }
        },
        controller: function($scope, $element) {
            $scope.tracks = [];
        }
    }
});

答案 1 :(得分:0)

我找到了一个解决方案,我对使用ngTransclude感到非常满意。

我在服务器上以指令形式编写曲目的内容:

<div track track-id="245" track-name="Pompeii" track-duration="3.24">Pompeii</div>

在我的播放列表模板中,我使用<div ng-transclude></div>指令从原始DOM中获取播放列表的内容,将其解析为单个指令和被注入播放列表模板的指定部分。