Why does my watcher gets called twice on the same

2019-06-17 01:35发布

问题:

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

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.

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:

回答1:

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.



回答2:

<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.