Timofey Yatsenko2013-03-19 12:26:47
Timofey Yatsenko, 2013-03-19 12:26:47

AngularJS: How to catch event after scope change and rendering?

I started to study this framework, I ran into the most banal task that I can’t implement.

It is necessary to read data from the DOM after the value in the scope has been changed and rendered to the view.
If we read the value from the DOM immediately after changing the scope, then of course we get nothing, since the DOM has not yet been rendered.

The task as a whole is as follows:
There is a scope in which there is a property (length of the time interval) on which a horizontal grid is built. The task is such that if the length of this grid becomes larger than the visible area of ​​​​the screen, you need to make it scroll horizontally.

Grid goes not in the table but on DIVs. columns line up one after the other from right to left and have float:left.
Accordingly, so that they do not move out under each other when there is not enough space in width, you need to set the parent container to a static width that will fit all the columns of the grid.

To do this, I need to catch the event when AngularJS has finished drawing all the columns, calculate their size with indents, and set the sum of the widths of these columns to the wrapper.

As I just did not try to do this ... I stumbled upon such a topic on SO stackoverflow.com/questions/12102366/angularjs-fire-an-event-immediately-after-scope-digest

And I started implementing through directives and $evalAsync. But I can't get the right result.
Here is an example code:

angular.module('mailingDirectives', [])
    .directive('scroller', function($timeout) {
        return {
            restrict: 'A',
            link: function(scope, element, attrs) {
                scope.$watch(attrs.scroller, function(){
                    var width=0;
                            width += $(this).outerWidth(true);
                        var wrapper = element.parent();
                        if (wrapper.width() < width) {
                            wrapper.css('overflow-x', 'scroll');
                        } else {
                            wrapper.css('overflow-x', 'hidden');

    <div class="quotesGridWrapper span9">
            <div scroller="dateSpan" class="quotesGrid">
                <div class="month" ng-repeat="month in quotesGrid">

                    <span class="monthName">{{month.name}} {{month.year}}</span>

                    <div class="day" ng-repeat="day in month.days">
                        <div class="date">
                            {{day.date.getDate()}} <br /> <span title="">{{day.dowShot}}</span>

                        <div class="mailBtn">
                            <a class="btn btn-small"><i class="icon-envelope"></i></a>

                        <div class="quotes">

                            <div class="full">

                            <div class="free">

                            <div class="busy">


As a result, now we have the fact that when reading the width of the columns in the grid, it reads them each time in a different way. Those. the addition of new ones and the reading process alternate with each other. Once he considers that there are 5 columns, another time that there are 7, etc.
Those. $evalAsync is not actually called after $digest.

Answer the question

In order to leave comments, you need to log in

4 answer(s)
aretmy, 2013-03-19

Maybe just use the ngStyle directive ?

Timofey Yatsenko, 2013-03-21

Studying the theoretical base on AngularJs, I will try to form exactly what I need.
I need to catch an event (if any) that occurs after the $digest function has completed and all listeners of this function have completed (the DOM has been fully prepared).
The post on SO, which I cited at the beginning of the topic, is just about the same, but the solution indicated there does not work in my case. My guess is that the problem is that AngularJs executes listeners asynchronously and doesn't know when they are done.
Unfortunately, I have no idea how to solve this problem.

lusever, 2013-04-01

Experiment with setTimeout:

scope.$watch(attrs.scroller, function(){
  setTimeout(function() {
  }, 1);

The function inside will be added to the end of the execution stack, which is what you want.
If the code was posted on jsFiddle it would be easier to check the guess.

maxaon, 2013-12-02

In scope.$watch(attrs.scroller, function(){..., you use "attrs.scroller" as a condition for watching (watchExpression) and apparently its value does not change during the digest cycle. Angular calls this handler once, for initialization.The
watchExpression can be either an expression or a callable function.Use the
name of the mutable variable as watchExpression or a function that returns some value.i.e. if the size depends on 'quotesGrid' use it as the attribute value. (Don't forget that watchExpression compares against $$hashKey for objects)
I'll also add that using DOM accesses in watchExpression is highly discouraged, because watchExpressions are repeatedly executed in the digest cycle.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question