Know when the DOM is rendered in a controller

I am using AngularJS and I have a problem. I have $watch on controller:

function MyController() {
    $watch('myModel', function() {
        $rootScope.$emit('do-oper');
    });
}

'myModel' is also changing my DOM by binding.

In a directive I have:

$rootScope.$on('do-oper', function() {
    // Do DOM manipulation
});

The problem is that on the time of emitting 'do-oper', the DOM is not rendered with the updates 'myModel'. How can I trigger the DOM manipulation AFTER the DOM is rendered?

Answers


You can try emitting the event inside app.run in AngularJS.


I don't think that manipulating DOM directly is a good idea when working with MVC structure like angular. You're going against the pattern. In MVC, the view should be as passive as possible, listen for model changes to update accordingly, should not update view directly by code. The code inside controller also should not care about when the view is rendered.

There is a workaround using $timeout, but I recommend you should not do it. Redesign your code to embrace MVC pattern is a better solution.

A workaround could be like this:

$rootScope.$on('do-oper', function() {
    $timeout(function(){
        // Do DOM manipulation
    });
});

Another big problem with this workaround is that the code could inadvertently update the DOM attributes and these updates may collide with angular which could cause unpredictable results.

You could change the directive to something like this:

$rootScope.$on('do-oper', function() {
        //Update properties of directive's scope based on the changed `do-oper`
});

Your directive template binds to scope's properties to update accordingly.


In the event that you need to respond to changes in the DOM through actions outside of angular's control, you might want to check the ng-ScrollSpy directive's code for a potential method: An AngularJS module for navigation highlighting.

Of particular note, in the link function we see:

angular.element( document ).ready( function() {
                            setup();
                            determiner();
                    });

This suggests that you can change your controller code, perhaps like this:

angular.element( document ).ready( function() {
                     $watch('myModel', function() {
                        $rootScope.$emit('do-oper');
                     });      
);

Further reading that might be of interest: http://henriquat.re/directives/advanced-directives-combining-angular-with-existing-components-and-jquery/angularAndJquery.html


Need Your Help

How do I download a file in a brackets-shell app?

html5 iframe browser adobe-brackets brackets-shell

I am using Brackets-shell to build a desktop packaged app for Windows and OSX. The application that runs on Brackets shell is able to open dynamically generated Excel sheets and PDF files from a se...