Reputation: 1676
I need to render a list of categories in a webapp I'm developing using AngularJS.
Each category can have an array of subcategories
.
In order to render them, I created a recursive category
directive that works like this:
if the category doesn't have subcategories, render as a complete category.
if the category has subcategories, render the supercategory as a container for subcategories, and insert and compile the subcategories.
This is the code of the directive:
.directive('category',
function(WeeklyCalendar, categoryRowsFactory, $compile){
return {
replace: true,
restrict: 'A',
templateUrl: 'new-category-tpl',
scope: {
category: '=categoryData'
},
link: function (scope, elem, attrs) {
//recursive categories rendering
if(scope.category.subcategories) {
$compile('<div ng-repeat="sub in category.subcategories track by sub.id" category category-data="sub"></div>')
(scope, function(cloned, scope){ elem.append(cloned) })
}
/* This works */
//elem.append('aaa');
/* This doesn't */
elem.find('.content').append('aaa');
}
};
})
Everything working well so far, you can check the result here: codepen
So, each category has category-row
s that are split in a header
and a content
, like this:
<div class="category">
<div class="category-row">
<div class="header"></div>
<div class="content"></div>
</div>
</div>
Now, I need to append some HTML to the div with class content
from the link
function. Anyway, using the classic element.find('.content').append('string')
won't work, as find
returns no result. (even if jQuery is included). Strangely enough, because if you inspect the HTML you can see that each category clearly has a category-row
children, which contains a .content
div. Simply appending to element
works, but it's not enough for me.
By the way, this is the template for a category
:
<script type="text/ng-template" id="new-category-tpl">
<div ng-class="{'category multicategory': category.subcategories, 'category single': category.type == 'Single', 'subcategory': category.type == 'Subcategory'}">
<div ng-if="category.subcategories" class="supercategory">
<strong>{{category.name}}</strong>
</div>
<div ng-if="category.type != 'WithSub'" class="category-row">
<div class="header">{{category.name}}</div>
<div class="content"></div>
</div>
</div>
</script>
Nothing fancy, as far as I can see.
I wasted an entire afternoon trying to understand what I'm missing, without success. Maybe you will be smarter/luckier. So, does anyone know why element.find(..)
doesn't work in that link
function?
The solution from ExpertSystem works, up to a point. I am able to append
in the $timeout
call, that part is fine. But later I need to access the same element I append in the directive's controller, but the template is still uncompiled and it doesn't work.
Maybe I am using the wrong approach. This is what I am trying to achieve:
I need to display categories in a calendar;
These categories can be of three types:
.header
with the category name and a .content
; The .content
is simply a empty div
in the template.link
function by jQuery (since I have to check how many of them have to be inserted, depending on the size of the calendar; I also need to check if I have to append only one row of cells or many rows, depending on the type of category).elem.css
or ng-style
.This becomes impossible if element
becomes inaccessible, since I have to place events with absolute positioning and I need to know top
and left
. I Here is a short version of what I'm trying to achieve in the end:
.directive('category',function(){
//...usual stuff
controller: function($scope, $element){
this.getPosition = function(date) {
var cellSelector//compute selector for placeholder cell depending on date
var cell = $element.find(cellSelector);
return { top: cell.top(), left: cell.left()}
}
}
})
.directive('event',function(){
//...usual stuff
require: '^category',
link: function(scope, elem, attrs, categoryCtrl) {
var position = categoryCtrl.getPosition(scope.event.date);
element.css(positiong);
}
})
I am out of ideas. Can someone think of a better way to achieve what I described?
Upvotes: 0
Views: 113
Reputation: 48211
The "problem" is that ngIf
(among other directives) removes the template from the DOM and decides what/if to append at a later time (after a few $digest loops).
So at the time you try to access .content
, the element's innerHTML looks like this:
<!-- ngIf: category.subcategories -->
<!-- ngIf: category.type!=='WithSub' -->
The "safest" to access .content
once it is rendered is to wrap your code into a $timeout
callback:
$timeout(function () {
elem.find('.content').text('aaa');
});
See, also, this short demo.
Upvotes: 2