Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does my watcher gets called twice on the same change?

I have an AngularJS 1.4* application running locally (yet). This app is served by an Laravel 5.1 backend RESTFul API.

I have to make this app that represents a package trip. A package is composed by days, ranging from 0 to N days. Each day have a list of services, ranging from 0 to N services. And a hotel.

My web server, from which my laravel application consumes from, delivers me a pre-setted package, containing a list of days: each one with a list of services and a hotel data (unused so far). On the response I have a list of properties for the package (that don't matter for now) and an array of days, called days_info. That response is being put in the $scope.package, on my PackageController. The PackageController also declares an directive called packageBlock, that consists in a list of days, and some other data for the package.

<div ng-repeat="day in package.days_info" class='row'>
    <div class='col-md-12'>
        <package-days-block></package-days-block>
    </div>
</div>

Inside <package-days-block> directive, I have another to iterate through the list of services inside every day.

<div class='container-fluid' ng-repeat='service in day.services' ng-controller="ServiceController">
    <service-block></service-block>
</div>

That's where the problem begins: to my undestandment, I now have a $scope.service inside my ServiceController. So, I started to change it on my need inside the ServiceController through a $scope.service.

The $scope.service has an attribute called service_id. I put a listener/watcher on it, so at any time the $scope.service.service_id is changed, I ask for another service_table (holds the informations about the services, it's based on the service_id previously choosen or changed by the user), and put it in the $scope.service.table.

// ServiceController
$scope.reloadServicesTable = function(service_id, service_day, date, paxes){
    MandatoryService.getServiceTable(service_id, service_day, date, paxes)
    .then(
        function(service_data) {
            $scope.service.table = service_data;
        }, 
        ...
    );

The reloadServicesTable is called on the watcher for the service_id changes.

// ServiceController
$scope.$watch(
    'service.service_id', // Places the watcher to watch the changes on the service's ID.
    function(new_service, old_service) {
        if( new_service === old_service )
            return; 

        $scope.reloadServicesTable($scope.service.service_id, $scope.service.service_day, $scope.day.date, $scope.package.paxes);

    }
);

The problem starts here: the request for the service's table is called twice when the service_id only changes once.

WHY, GOD, WHY?!

There's another part of my code where I, from the PackageController, run through the entire days_info array and reads the value of an attribute price inside the service.table: service.table.price. In there, I realise that there's two scope's: the one I handling and the other that I have no FREAKING IDEA where it came from!

If I put an console.log($scope); inside the method that runs through the days_info, I get two scopes for every request. This method is on the PackageController.

Any ideas why this is happening?

P.S.: It's my very first AngularJS application, so, take easy if I messed up on something basic...

EDIT:

As pointed out by an fellow on the comments, my question wasn't very reproducible. Sadly, I can't put here only the part I'm having doubts cause I don't have the slightest idea where the problem lies! (I know that this isn't much of help)

I took some screen shots from the Chrome Console:

First, the requestions fired on the change of the service_id

Request log for the service change (ordered by path name)

As you can see, every request is called twice everytime. This is not an one-time-thing. The /api/service/{id}... is the call for the service's table information. The /api/service/by_route/origin/... returns an list of services from one city to another (or the same). One does not interfere on the other.

The other image is the output from a console.log from the PackageController $scope, on the time that the service_id is being changed.

Scopes for the PackageController

As you can see, there's two different scopes. And the b scope is son of the r scope. The r scope is also calling the watcher on the service_id?

The call for the sums price is been called twice from differente places, as you can see in the image below:

Call Stack for the sumPrices method on PackageController

like image 716
Dennis Braga Avatar asked Feb 25 '16 23:02

Dennis Braga


2 Answers

It may solve you issue. Even i had faced exactly the same as you are mentioning.

  1. The reason for me was that, the controller was getting initialized again and there was a separate api call written in that, which was intended to load the page initially.

  2. There can also be a scenario where you have assigned controller twice in the mark up.

like image 85
Luke P. Issac Avatar answered Oct 18 '22 12:10

Luke P. Issac


<div ng-repeat="day in package.days_info" class='row'>
    <div class='col-md-12'>
        <package-days-block day="day"></package-days-block>
    </div>
</div>

<div class='container-fluid' ng-repeat='service in day.services'>
    <service-block service="service"></service-block>
</div>

Pass day and service down into directives. Use two way binding to pass your day and service changes back up to package.days_info.

Remove your ServiceController It does not make much sense to ng-repeat a controller. <service-block> and <package-days-block> are E directive that handle logic.

Write only one watcher in your PackageController that watch package.days_info When your day or service change, it can simply find out and do something about it.

Just Chill and fix it.

like image 23
robert Avatar answered Oct 18 '22 13:10

robert