Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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>
like image 267
Alex Avatar asked Mar 27 '14 12:03

Alex


1 Answers

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 = [];
        }
    }
});
like image 104
Pete Avatar answered Oct 17 '22 08:10

Pete