Setting $scope.myModel element with ng-change enters in infinite loop
Asked Answered
W

1

0

I'm pretty new to Angular and trying to achieve something "basic". I've been googling for 2 days without success and would appreciate some help.

I have an html page on which I'm trying to:

  1. Initialize data with a HTTP POST request
  2. Call a function via the ng-change event to update the data with another HTTP POST when elements used as filters are changed (i.e. categories, sorting asc/desc...)

My problem is that when I'm updating the model programmatically with the HTTP response (only in this case), it triggers the ng-change event attached to the element, which itself calls the update function and then enters in an infinite loop: ng-change -> updating function -> ng-change -> updating function

Note: I'm using Angular Material template but it doesn't change the code

HTML

<html ng-app="MyApp">
    <body layout="column" ng-controller="SearchServiceController">
        <h1 class="md-headline">Filter results</h1>
        <form name="searchServiceForm" novalidate>
            <md-input-container>
                <md-select placeholder="Choose category" ng-model="searchService.selectedCategory" ng-change="changedSearchServiceCriteria()">
                    <md-option ng-value="category.value" ng-repeat="category in listOfCategories">{{ category.title }}</md-option>
                </md-select>
             </md-input-container>
             <md-input-container>
                 <md-select placeholder="Sort by" ng-model="searchService.sortBy" ng-change="changedSearchServiceCriteria()">
                     <md-option ng-value="criteria.value" ng-repeat="criteria in sortByCriterias">{{ criteria.title }}</md-option>
                  </md-select>
              </md-input-container>
          </form>
          <h1 class="md-headline">{{ selectedCategory.title }}</h1>
          <p class="md-body-1">{{ selectedCategory.description }}</p>
    </body>
</html>

JS

var app = angular.module('MyApp', ['ngMaterial']);

app.controller('SearchServiceController', function($scope, $http, $location) {
  // Initialize data using the category id parameter in the URL
  $http({
    method: 'POST',
    url: '/projets/get-offerings-list',
    data: {selectedCategory: $location.path().split("/")[4]},
    headers: {'Content-Type': 'application/x-www-form-urlencoded'}
    })
      .success(function(response) {
        alert('INIT');
        $scope.listOfCategories = response.listOfCategories;
        $scope.sortByCriterias = response.sortByCriterias;
        $scope.searchService = response.searchService;
      })
      .error(function(response) {
        console.log('Failure occured');
      });

  // Update data
  $scope.changedSearchServiceCriteria = function() {
    $http({
    method: 'POST',
    url: '/projets/get-offerings-list',
    data: {selectedCategory: $location.path().split("/")[4]},
    headers: {'Content-Type': 'application/x-www-form-urlencoded'}
    })
      .success(function(response) {
        alert('UPDATE');
        $scope.listOfCategories = response.listOfCategories;
        $scope.sortByCriterias = response.sortByCriterias;
        $scope.searchService = response.searchService;
      })
      .error(function(response) {
        console.log('Failure occured');
      });
  };
});

RESULT

INIT
Object {listOfCategories: Array[3], sortByCriterias: Array[2], searchService: Object}
UPDATE
Object {listOfCategories: Array[3], sortByCriterias: Array[2], searchService: Object}
UPDATE
Object {listOfCategories: Array[3], sortByCriterias: Array[2], searchService: Object}
UPDATE
Object {listOfCategories: Array[3], sortByCriterias: Array[2], searchService: Object}
UPDATE
Object {listOfCategories: Array[3], sortByCriterias: Array[2], searchService: Object}
UPDATE
Object {listOfCategories: Array[3], sortByCriterias: Array[2], searchService: Object}
....infinite loop....

This doesn't occur when I'm updating the model programmatically without using the response of the HTTP request. See here: http://plnkr.co/edit/baNjr85eAOkKVu4dnf1m?p=preview Would you have any ideas on how I could update the form element without provoking the ng-change event?

Thanks

Please note that I do not want to use a workaround using $watch like there: ngChange is called when model changed programmatically

Walkabout answered 16/7, 2015 at 20:53 Comment(0)
T
0

You should try to add a working example into http://jsfiddle.net/ or similar.

I would think at a guess and not tested that you need to keep the last search and only run the update AJAX request if it changes as it gets a new address (or whatever thats called in JS) every time - even though its the same value and re evaluates again

$scope.searchService = "";
$scope.lastSearch = ""

// Update data using form elements
$scope.changedSearch = function() {

    if($scope.searchService != $scope.lastSearch){

      $http({
        method: 'POST',
        url: '/projects/get-list',
        data: $scope.searchService,
        headers: {'Content-Type': 'application/x-www-form-urlencoded'}
      })
        .success(function(response) {
            // Request succeeded? Display message
            console.log('UPDATE');
            console.log(response);
            // Update data
            $scope.listOfCategories = response.listOfCategories;
            $scope.sortByCriterias = response.sortByCriterias;
            $scope.searchForm = response.searchForm;
        })
          .error(function(response) {
            // Request failed? Display message
            console.log('Failure occured');
        })
          .complete(function(response) {
            // do after success or error
            $scope.lastSearch = $scope.searchService;
            console.log('complete');
        });
    }
};
Trimolecular answered 16/7, 2015 at 21:14 Comment(1)
Thanks for your suggestion. It seems to be a bug. I'll try that. I added a Plunker example to illustrate how it should work. I didn't add the Plunker to illustrate the bug as it relies on an http request and I was unable to do that in Plunker without a link to a remote server.Walkabout

© 2022 - 2024 — McMap. All rights reserved.