【问题标题】:Parse content within AngularJS directive before rendering template在渲染模板之前解析 AngularJS 指令中的内容
【发布时间】:2014-03-27 12:04:56
【问题描述】:

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

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

加载页面后,我将包含 AngularJS 并解析 playlist 和指令。

当我初始化 playlist 指令时,我想在编译它的模板并使用收集的数据在 @987654323 中绘制 track 指令之前循环遍历它的内容@ 属于 playlist 指令的指令。

问题:

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

  2. 我的问题有更好的解决方案吗?不幸的是,我不得不使用页面上已经呈现的内容作为数据源来解决这个项目的一大堆需求

  3. 我能否在来自服务器的内容中使用 track 指令来避免 DOM 操作?

补充说明

编译前:

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

【问题讨论】:

  • 我想你可能看错了。所有数据操作都应在控制器中完成,指令应对更改做出反应。你有没有机会把一个 jsFiddle 放在一起? Angular 的强大之处在于您不需要解析 DOM。否则,会有带有 pre-link 和 post-link 的 Link 来操作 DOM 和范围。
  • 实际上,您可以发布您的播放列表数据结构/模型吗?这可能有助于我理解你想要什么。
  • 播放列表有一些控件,例如 playAllqueueAll,而曲目则有 play队列。除此之外,它们没有太多其他的数据结构。

标签: javascript angularjs angularjs-directive


【解决方案1】:

假设这是您的数据结构,您可以轻松地完成这一切而无需指令:

数据结构/控制器

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。使用您需要擦除原始 DOM 的任何 jQuery。 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 = [];
        }
    }
});

[edit #2] 包括 Beyer 的建议。添加这个以防将来有人发现这很有用。 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 = [];
        }
    }
});

【讨论】:

  • 是的,这是理想的解决方案 - 但我绝对必须在页面加载时使用 DOM 中的内容。
  • Angular 的诀窍是首先考虑数据。通常,对于像 jQuery 这样的框架,你会考虑结构和布局。当我开始使用 Angular 时,我落入了同样的陷阱。 Angular 可能不适合您的工作。如果您需要解析 DOM,使用 jQuery 可能会更好。
  • 如果 jQuery 是解析来自 DOM 的数据的最佳方式,那就太好了——不管我仍然需要在应用程序的其余部分使用 Angular。真的,我只是想知道我是否可以在引导之前访问指令的内容。如果不是,我将不得不重新考虑如何使用 DOM 生成我的数据模型(我对此并不满意——不过这是这个项目的要求)。
  • @mister_rampage 好问题,我还没有看到 compile 在野外使用了这么多。关于为什么要使用它的最佳资源已在docs.angularjs.org/guide/compiler 结束,具体请参阅An Example of "Compile" Versus "Link" 部分。就我个人而言,我从来没有真正用过自己编译。
  • @Beyers +1,我认为文档中的关键声明是“这使得范围模型成为唯一的事实来源。”。为构建模型而对 DOM 的整个操作似乎与 Angular 的观点背道而驰。
【解决方案2】:

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

我从服务器中以指令形式为 tracks 编写内容:

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

在我的 playlist 模板中,我使用了 &lt;div ng-transclude&gt;&lt;/div&gt; 指令,该指令从原始 DOM 中获取 playlist 的内容,将其解析为单独的指令并注入进入 播放列表 模板的指定部分。

【讨论】:

  • 那么playlist 有什么意义,如果你也可以在服务器上渲染它?
  • 我同意拜尔斯的观点。 DOM 解析的问题在于,如果 DOM 发生了变化,那么 Angular 不知道它,因为模型没有被更新。从某种意义上说,你的观点已经成为了模式。最好让服务器只渲染它并完全不使用 Angular。
  • 非 js 版本中的播放列表只是曲目名称的列表,但在 js 版本中,它是一个带有大量额外标记的交互式设备 - 每个曲目都相同。
猜你喜欢
  • 1970-01-01
  • 2013-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-30
  • 2014-11-15
  • 2017-02-11
相关资源
最近更新 更多