Search code examples
angularjsangularjs-directiveangularjs-scopeangularjs-controllerangularjs-watch

Directive scope.$watch called after controller function


So I have a controller and directive. I am having trouble understanding why my controller runs after my directive. I have scoured stack trying to find the answer but it seems like most of the answers are a case to case basis.

My controller in its simplest form is as so

app.controller('MyCtrl', ['$scope', function ($scope){
$scope.initAddress = {
    place_id: "",
    geometry: {
        location: {
            A: "",
            F: ""
        }
    },
}
$scope.gotoLocation = function (placeId) {
                   $scope.initAddress = {
                       place_id: "placeId",
                       geometry: {
                           location: {
                               A: placeId.geometry.location.lat(),
                               F: placeId.geometry.location.lng()
                           }
                       }
}};}]);

When I click on my ng-click button I am going to the gotoLocation scoped function and redefining $scope.initAddress. My HTML is as follows:

<div class="row">
<my-map address="initAddress"></my-map>
</div>
<div ng-repeat="place in placelistData.placeList">
    <button ng-click="gotoLocation(place.id)">{{place.id}}</button>
</div>

Now when I look at my directive, I am using

 .directive('myMap', function () {
    return {
        restrict: 'E',
        replace: true,
        scope: {
            address: '=?',
        },
        template: '<div class="dp-places-map-wrapper"><input type="text" class="dp-places-map-input"><div class="dp-places-map-canvas"></div></div>',

        link: function (scope, element, attrs) {
scope.$watch('address', function () {
   if (map && scope.address)
   map.setCenter(getLocation(scope.address));
 });
 }

Right now, when I click the button it runs to the controller and $scopes.initAddress and doesn't hit $scope.watch. When I click it again, it hits $scope.watch first then it goes to my controller and updates accordingly. How can I change it so that it will hit the controller prior and then the $scope.watch catching the change in the controller. Rather than running $scope.watch first then running the controller.

Thanks for the help


Solution

  • Regarding understanding of why controller runs after my directive.

    As per the angular $compile phase, directives are always called before the controllers using depth first traversl algorithm.

    The reason being, directive is a reusable component. So it must be instantiated before the controllers are called.

    e.g. your html may look like this

    <body ng-app="demo">
    <div ng-controller="controller1">
         <my-map address="initAddress"></my-map>
    </div>
    <div ng-controller="controller2">
        <my-map address="initAddress"></my-map>   
    </div>
    <div ng-controller="controller3">
    <my-map address="initAddress"></my-map>
     </div></body>
    

    Here initAddress would be the scope variables in the respective controllers "controller1","controller2", and "controller3". Hence always the directives will be called before the controllers using depth first traversl algorithm.

    Here is the lifecycle of the directive

    1. Directive is instantiated
    2. Outside controller is called in which directive is declared.
    3. The controller defined inside the directive is called.
    4. The prelink if defined is called.
    5. The postlink if defined is called note : The link function if defined is always the postlink function. If the compile function are declared then link function is never called.

    Please see the post for more details. http://yogeshtutorials.blogspot.in/2015/12/angularjs-directive-lifecycle.html