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 pile 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 piling it's template and using the data collected to draw track directives in an ng-repeat
directive belonging to the playlist directive.
Questions:
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.
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
Could I use a track directive within my content from the server to avoid DOM manipulation?
Extra explanation
Before pile:
<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 pile:
<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>
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 pile 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 piling it's template and using the data collected to draw track directives in an ng-repeat
directive belonging to the playlist directive.
Questions:
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.
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
Could I use a track directive within my content from the server to avoid DOM manipulation?
Extra explanation
Before pile:
<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 pile:
<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>
Share
Improve this question
edited Mar 27, 2014 at 12:23
Alex
asked Mar 27, 2014 at 12:04
AlexAlex
8,6829 gold badges47 silver badges58 bronze badges
3
- I think you might be looking at this the wrong way. All data manipulation should be done in the controller and the directive should react to the changes. Is there any chance you could throw together a jsFiddle? The power of Angular is that you don't need to parse the DOM. Otherwise, there is the Link with pre-link and post-link to manipulate the DOM and the scope. – Pete Commented Mar 27, 2014 at 12:19
- Actually, could you post your data structure/model for playlist? That might help me a bit in understanding what you want. – Pete Commented Mar 27, 2014 at 12:20
- A playlist has a few controls, such as playAll or queueAll, whereas a track has things like play or queue. Other than that there's not much else to them as data structures. – Alex Commented Mar 27, 2014 at 12:29
2 Answers
Reset to default 5Assuming 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 piling, 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($pile, $templateCache) {
var extractData = function(scope) {
$('.Track').each(function(index, ele) {
scope.tracks.push($(ele).attr('data-track-name'));
});
}
return {
replace: true,
restrict: 'A',
pile: 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"));
$pile(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.