简体   繁体   中英

Parse content within AngularJS directive before rendering template

I have an element on the page that has been rendered by the server. Let's call it a playlist . It's going to be a directive.
<div playlist></div>

A playlist contains a number of tracks before Angular compile time.
<div class="Track" data-track-name="Pompeii" data-*="etc">...</div>

Once the page is loaded I'm including AngularJS and parsing playlist and a directive.

When I initialise the playlist directive, I'd like to loop through it's contents before compiling it's template and using the data collected to draw track directives in an ng-repeat directive belonging to the playlist directive.

Questions:

  1. Is there native functionality for parsing content before the template replaces any content within the directive tag? Once I'm in the directive controller the content has already been replaced.

  2. Is there a better solution to my problem? I unfortunately have to use content already rendered on the page as a data-source as a solution to a whole bunch of requirements for this project

  3. Could I use a track directive within my content from the server to avoid DOM manipulation?

Extra explanation

Before compile:

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

After compile:

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

Assuming this is your data structure, you could easily do this all without a directive:

Data Structure/Controller

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'] }
];
}

Template:

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

[edits]

Since you need to parse the DOM, you can use this directive to do it. It will parse the DOM and place it into a controller scope array. You can then pass this scope into a template or template URL. Use any jQuery you need to wipe the original 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 = [];
        }
    }
});

[edit #2] Including Beyer's suggestions. Adding this in case someone finds this useful in the future. jsFiddle

Template:

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

Directive:

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 = [];
        }
    }
});

I've found a solution that I'm super happy with using ngTransclude .

I write my content from the server for the tracks in their directive form:

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

And in my playlist template, I use a <div ng-transclude></div> directive that takes the content of the playlist from the original DOM, parses it as individual directives and is injected into the specified part of the playlist template.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM