Velkumar Velkumar - 2 months ago 23
AngularJS Question

angularJS controller method with ng-options

I have a scenario where i need to create drop-down dynamically inside ng-repeat and the data source for drop-down is also dynamic, meaning based on the query i need to bind my dropdown.

So, i decdided to call controller method, where i have a http get, by passing the query based on the query my service will return a generic data with key value pair. when i call the controller method i am ending up wih infinte loop

Error: $rootScope:infdig
Infinite $digest Loop


My Html

<table style="width: 100%">
<tr>
<td colspan="2"><strong>Enter Parameters</strong></td>
</tr>

<tr ng-repeat="x in reportDataParameter.UserParameterList">

<td>{{x.UserParamDefinition.DisplayLabel}}</td>
<td>
<select ng-model="x.Model" ng-options="item.ValueField as item.TextField for item in executeQuery(x.UserParamDefinition.Query)" class="form-control">
<option value="">Please Select</option>
</select>
</td>
</tr>
</table>


Controller

$scope.reportDataParameter = {};

$scope.reportDataParameter = {
"UserParameterList": [
{
"Key": "StateID",
"Value": "?ddl,State,State",
"UserParamDefinition": {
"DataType": "ddl",
"DisplayLabel": "State",
"Model": null,
"Query": "SELECT Id AS ValueField, BankName AS TextField FROM BankDetail"
}
},
{
"Key": "FacilityParentID",
"Value": "?ddl,FacilityParent,FacilityParent",
"UserParamDefinition": {
"DataType": "ddl",
"DisplayLabel": "FacilityParent",
"Model": null,
"Query": "SELECT Id AS ValueField, Name AS TextField FROM Organization"
}
}
],
"ReportTitle": "Facility List By Parent",
"Description": null,
"ReportPage": null
};

$scope.executeQuery = function (query) {
var baseUrl = 'https://localhost:62';
return $http.get(baseUrl + '/api/reports/executequery/' + query).then(function (result) {
return result.data;
});
}

Answer

First of all, remember that a function that you bind in a template is evaluated every digest cycle. executeQuery(x.UserParamDefinition.Query) is called every digest cycle for each ng-repeat entry. In your case that is two function calls per cycle.

Furthermore, each call queries the server using $http so you query the server every cycle.

Then, you use a promise in your ng-options once the promise is resolved a new digest is started and your function is called again. That's why you end up with the infinite loop. Apart from the issue that a promise is not a valid input for ng-options. The

The return result.data; of your callback just vanishes into thin air as it is not bound by the controller and not returned by executeQuery (that returns the promise).

The easiest way to resolve this is to bind your query data to a variable in your UserParameterList entries and query the server if the variable is not set. You could use a getter-method for that. Something like this would also be possible:

$scope.executeQuery = function(item) {
    var baseUrl = 'https://localhost:62';
    $http.get(baseUrl + '/api/reports/executequery/' + item.UserParamDefinition.Query)
         .then(function (result) {
             item.queryResult = result.data;
         });
}

In your template, you just use x.queryResult. If UserParameterList does not change, you could just run through the array once and execute executeQuery for each entry. If it does change, you could use a watcher to track changes an execute executeQuery. If UserParameterList indeed changes, you need to have concurrency in mind as in "items can change while a query is running" and deal with it.

Comments