0

My first experiment with angular.js.

I've a few columns, each of them includes some template:

<div class="col-md-5 js-column" ng-repeat="orm in orms" repeat-done="equalHeight">
    <h2>{{ orm.name }}</h2>
    <ng-include src="'/inc/_compiled/'+orm.id+'.html'"></ng-include>
</div>

Each included template contains the same elements as other templates, but they've different height. Example element:

<pre data-task="model" class="task-model">
from django.db import models
class Teacher(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

</pre>

The thing I want to achieve is to set equal height for specific element in all the columns. That means all pre.task-model will have the same height.

I've created some directive and the idea was to trigger it after ngRepeat loop is finished (scope.$last). But when I try to access the included nodes via jQuery/DOM selectors, I get nothing. I know each template is available in element variable, but I need to get also the other columns.

var ormApp = angular
    .module('ormApp', [])
    .directive('repeatDone', function () {
        return function (scope, element, attrs) {
            if (scope.$last) {
                $('.js-column pre.task-model'); //<- got nothing
            }
        };
    })
yedpodtrzitko
  • 9,035
  • 2
  • 40
  • 42
  • My guess is that the directive is triggered before the DOM is updated. What happens if you put your jQuery selector in a setTimeout function? – Nathan Breit Oct 20 '13 at 16:20
  • Can't you use CSS for that? – Jonas Oct 20 '13 at 16:25
  • @JonasHartmann I would like to, but I can't imagine how to do it via css. – yedpodtrzitko Oct 20 '13 at 16:30
  • @Nathan setting timeout helped (even if it's not nice solution), thanks. – yedpodtrzitko Oct 20 '13 at 16:31
  • pre.task-model { height: 200px; } // Try that out, but maybe I didn't understand what you want to do. – Jonas Oct 20 '13 at 16:32
  • @JonasHartmann there's a plenty of these `pre` elements (pre.task-foo, pre.task-bar...) and I don't know height of their content so setting it to some specific number wouldn't work. – yedpodtrzitko Oct 20 '13 at 16:38
  • Following the idea here: http://stackoverflow.com/a/13472605/1981709 You could try to add your directive to each element inside the template, if possible. – Jonas Oct 20 '13 at 16:49

1 Answers1

1

As mentioned in the comments, using $timeout solves the problem.
But why ?

The issue here is that several operations involved in the process, such as creating a new element (by ngRepeat), fetching the template (by ngInclude) using $http.get() (even if it comes from the $templateCache), resolving the promises returned etc, are asynchronously and are handled using $evalAsync(), which means that they are going to "happen" when everything else currently on Angular's async queue is processed.

Since there are several levels of nested $evalAsyncs (in this particular case 7), you can achieve what you want by iteratively calling $evalAsync() until the async operations required for fetching and lining the template are completed.

This is of course not a robust (or recommended) way to solve the problem, it is just meant to explain what is going on. Using $timeout puts the operation in the browser's event queue, so the command will be processed after Angular's current $digest loop is completed (i.e. after all tasks on Angular's async queue have been processed and all taks added to the queue by those async taks etc).
(Another side-effect here is that, since the rendering engine's "render" command is already on the browser event queue, using $timeout will execute the command after the next DOM rendering has taken place. This has no relevance for this particular case, but might be important in other situations.)


See, also, this short demo illustrating both aproaches (open the DevTools console).

gkalpak
  • 47,844
  • 8
  • 105
  • 118