B.Balamanigandan B.Balamanigandan - 4 months ago 5
Ajax Question

$http issue - Values can't be returned before a promise is resolved in md-autocomplete Angular Material

I'm using Angular Material

md-autocomplete
in my project. In that I'm getting the suggest listing from the Service Host via ajax call using
$http
service.


Issue: $http issue - Values can't be returned before a promise is
resolved in
md-autocomplete
Angular Material

My Requirement: I need an updated Suggestion List using remote data
sources in
md-autocomplete
Angular Material - Ajax
$http
service.


I used the approach as mentioned in Angular Material link https://material.angularjs.org/latest/demo/autocomplete

Source Code:

Scenario 1:

HTML Source Code:

<md-autocomplete flex required
md-input-name="autocompleteField"
md-no-cache="true"
md-input-minlength="3"
md-input-maxlength="18"
md-selected-item="SelectedItem"
md-search-text="searchText"
md-items="item in querySearch(searchText)"
md-item-text="item.country" Placeholder="Enter ID" style="height:38px !important;">
<md-item-template>
<span class="item-title">
<span md-highlight-text="searchText" md-highlight-flags="^i"> {{item.country}} </span>
</md-item-template>
</md-autocomplete>


AngularJS Script:

//bind the autocomplete list when text change
function querySearch(query) {
var results = [];
$scope.searchText = $scope.searchText.trim();
if (query.length >=3) {
results = LoadAutocomplete(query);
}
return results;
}

//load the list from the service call
function LoadAutocomplete(id) {
var countryList = [];
$http({
method: "post",
url: "https://www.bbminfo.com/sample.php",
params: {
token: id
}
})
.success(function (response) {
countryList = response.records;
});

return countryList;
}


Scenario 2:

HTML with AngularJS Source Code:



<!DOCTYPE html>
<html>
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/angular-material.min.css">
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-animate.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-aria.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-messages.min.js"></script>

<!-- Angular Material Library -->
<script src="http://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/angular-material.min.js"></script>
<body>

<div ng-app="myApp" ng-controller="myCtrl">

<p>Person to Select:</p>

<md-autocomplete
ng-disabled="isDisabled"
md-no-cache="noCache"
md-selected-item="selectedItem"
md-search-text-change="searchTextChange()"
md-search-text="searchText"
md-selected-item-change="selectedItemChange(item)"
md-items="item in Person"
md-item-text="item.Name"
md-min-length="0"
placeholder="Which is your favorite Person?">
<md-item-template>
<span md-highlight-text="ctrl.searchText" md-highlight-flags="^i">{{item.country}}</span>
</md-item-template>
<md-not-found>
No Person matching "{{searchText}}" were found.
</md-not-found>
</md-autocomplete>
<br/>
</div>

<script>
var app = angular.module('myApp', ['ngMaterial']);

app.controller('myCtrl', function ($scope, $http, $q) {

$scope.searchText = "";
$scope.Person = [];
$scope.selectedItem = [];
$scope.isDisabled = false;
$scope.noCache = false;

$scope.selectedItemChange = function (item) {
alert("Item Changed");
}
$scope.searchTextChange = function () {

$http({
method: "POST",
url: "https://www.bbminfo.com/sample.php",
params: {
token: $scope.searchText
}
})
.success(function (response) {
$scope.Person = response.records;
});
}

});
</script>
</body>
</html>





In Scenario 1, I used the function to fetch the filtered list
md-items="item in querySearch(searchText)"
. But in Scenario 2, I used a
$scope
variable
md-items="item in Person"


Kindly refer the Snapshots

Snapshot 1:

Autocomplete Listing Issue - UI

Here I'm searching for indian but it shows the result for india. I debugged the issue in Firefox Browser Firebug, see the above Snapshot 1 it shows, the request was sent for the search term indian via POST Method and I got the response of one matching items as a JSON Object successfully, which is shown in the bottom of SnapShot 1


The issue I find out in this case, the Values can't be returned before
a promise is resolved


Steps I tried:

Case 1: I used the AngularJS
filter
in UI
md-items="item in Person | filter: searchText"
, it gives the filtered list of previously fetched remote data not a currently fetched remote data. While on Backspacing the character in the textbox it shows the improper suggestion list.

Case 2: I tried to update the changes in UI by calling
$scope.$apply()
within a
$http
service, but it fails. Because
$http
service calling the
$scope.$apply()
by default, show it throws me an error Error: [$rootScope:inprog].... Finally in this attempt I failed.

Case 3: I created a function, within the function I'm manually called the
$scope.$apply()
, within the function I manually pushed and poped one dummy item to the
$scope
variable which is bind in the
md-autocomplete
. But I failed in this attempt. Because here also I got a same output as same as in the snapshot.

function Ctrlm($scope) {
$scope.messageToUser = "You are done!";
setTimeout(function () {
$scope.$apply(function () {

$scope.dummyCntry = [
{
sno: 0,
country: ""
},
];

$scope.Person.push($scope.dummyCntry);

var index = $scope.Person.indexOf($scope.dummyCntry);
$scope.Person.splice(index, 1);

});
}, 10);
}


Case 4: I did the same approach as like "Case 3" within the $scope.$watchCollection. Here also I got a setback.

$scope.$watchCollection('Person', function (newData, oldDaata) {
$scope.dummyCntry = [
{
sno: 0,
country: ""
},
];

newData.push($scope.dummyCntry);

var index = newData.indexOf($scope.dummyCntry);
newData.splice(index, 1);
});


Case 5: Instead of
$http
service, I used the jquery ajax call. In that I used the
$scope.apply()
to update the UI manually. I failed in this attempt once again, here also I got the same output.

$scope.searchTextChange = function () {
if (($scope.searchText != undefined) && ($scope.searchText != null)) {

$.ajax({
type: 'GET',
url: "https://www.bbminfo.com/sample.php?token=" + $scope.searchText,
success: function (response) {
$scope.$apply(function () {
$scope.Person = response.records;
});
},
error: function (data) {
$scope.$apply(function () {
$scope.Person = [];
});
},
async: true
});


} else {
$scope.Person = [];
}
}



In all the attempts I can't able to fix the issue.


@georgeawg http://stackoverflow.com/users/5535245/georgeawg suggested me to post a new question, he stated, "write a new question that describes what you are actually trying to accomplish, include the desired behavior, a summary of the work you've done so far to solve the problem, and a description of the difficulty you are having solving it."

Reference Questions which I was posted in the earlier

Post 1: http://www.stackoverflow.com/questions/35624977/md-items-is-not-updating-the-suggesion-list-properly-in-md-autocomplete-angular

Post 2: http://www.stackoverflow.com/questions/35646077/manually-call-scope-apply-raise-error-on-ajax-call-error-rootscopeinprog


My Requirement: I need an updated Suggestion List using remote data
sources in Angular Material
md-autocomplete
- Ajax
$http
service.


Kindly assist me in this regards.


For Testing Purpose Use the following Source Code


Use the following URL for Remote Data Source: https://bbminfo.com/sample.php?token=ind

The Remote Data Source URL contains the List of Countries Name.


Directly Test the Code by click the below Run Code Snippet button.


Complete HTML with AngularJS Source Code:



<!DOCTYPE html>
<html>
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/angular-material.min.css">
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-animate.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-aria.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-messages.min.js"></script>

<!-- Angular Material Library -->
<script src="http://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/angular-material.min.js"></script>
<body>

<div ng-app="myApp" ng-controller="myCtrl">

<p>Country to Select:</p>
<md-content>
<md-autocomplete
ng-disabled="isDisabled"
md-no-cache="noCache"
md-selected-item="selectedItem"
md-search-text-change="searchTextChange()"
md-search-text="searchText"
md-selected-item-change="selectedItemChange(item)"
md-items="item in Person"
md-item-text="item.country"
md-min-length="0"
placeholder="Which is your favorite Country?">
<md-item-template>
<span md-highlight-text="searchText" md-highlight-flags="^i">{{item.country}}</span>
</md-item-template>
<md-not-found>
No Person matching "{{searchText}}" were found.
</md-not-found>
</md-autocomplete>
</md-content>
<br/>
</div>

<script>
var app = angular.module('myApp', ['ngMaterial']);

app.controller('myCtrl', function ($scope, $http, $q) {

$scope.searchText = "";
$scope.Person = [];
$scope.selectedItem = [];
$scope.isDisabled = false;
$scope.noCache = false;

$scope.selectedItemChange = function (item) {
alert("Item Changed");
}
$scope.searchTextChange = function () {

$http({
method: "post",
url: "https://www.bbminfo.com/sample.php",
params: {
token: $scope.searchText
}
})
.success(function (response) {
$scope.Person = response.records;
});
}

});
</script>
</body>
</html>




Answer

@KevinB http://stackoverflow.com/users/400654/kevin-b - Gives the Idea how to implement. I really thank him... Once again thanks alot Kevin...

I got the Exact solution, what I need.

The Source Code is

<!DOCTYPE html>
<html>
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/angular-material.min.css">
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-animate.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-aria.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-messages.min.js"></script>

<!-- Angular Material Library -->
<script src="http://ajax.googleapis.com/ajax/libs/angular_material/1.0.4/angular-material.min.js"></script>
<body>

<div ng-app="myApp" ng-controller="myCtrl"> 

<p>Country to Select:</p>
<md-content>
<md-autocomplete
          ng-disabled="isDisabled"
          md-no-cache="noCache"
          md-selected-item="selectedItem"
          md-search-text="searchText"
          md-items="item in searchTextChange(searchText)"
          md-item-text="item.country"
          md-min-length="0"
          placeholder="Which is your favorite Country?">
        <md-item-template>
          <span md-highlight-text="searchText" md-highlight-flags="^i">{{item.country}}</span>
        </md-item-template>
        <md-not-found>
          No Person matching "{{searchText}}" were found.
        </md-not-found>
      </md-autocomplete>
      </md-content>
      <br/>
</div>

<script>
    var app = angular.module('myApp', ['ngMaterial']);

    app.controller('myCtrl', function ($scope, $http, $q, GetCountryService) {

        $scope.searchText = "";
        $scope.Person = [];
        $scope.selectedItem = [];
        $scope.isDisabled = false;
        $scope.noCache = false;

        $scope.selectedItemChange = function (item) {
            //alert("Item Changed");
        }
        $scope.searchTextChange = function (str) {
			return GetCountryService.getCountry(str);
        }

    });
	
	app.factory('GetCountryService', function ($http, $q) {
        return {
            getCountry: function(str) {
                // the $http API is based on the deferred/promise APIs exposed by the $q service
                // so it returns a promise for us by default
				var url = "https://www.bbminfo.com/sample.php?token="+str;
                return $http.get(url)
                    .then(function(response) {
                        if (typeof response.data.records === 'object') {
                            return response.data.records;
                        } else {
                            // invalid response
                            return $q.reject(response.data.records);
                        }

                    }, function(response) {
                        // something went wrong
                        return $q.reject(response.data.records);
                    });
            }
        };
    });
</script>
</body>
</html>

I Briefly explained about md-autocomplete in the following blog - http://www.increvcorp.com/usage-of-md-autocomplete-in-angular-material/