In my AngularJS project, I have a select
HTML element, that should be bound with the states of Brazil. So, I'm using a ng-repeat to populate the states, that are loaded from my project database, using a RESTful API.
Then, in my controller I created a variable on scope named estados
, and set an empty array for it [1]. After I call a $http.get [2], I receive the data from the base, and set it for the $scope.estados [3], that is used in ng-repeat. [4]
Controller code:
myApp.controller('myCtrl', function($scope, $http, API_URL) {
$scope.estados = []; //[1]
$http({ // [2]
method: 'GET',
url:API_URL + '/estados',
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).then(function(response) {
if(response.status == 200){
$scope.estados = response.data; // [3]
console.log($scope.estados);
}
});
});
HTML code:
<select name="uf" class="form-control" ng-model="Empresa.endereco.estado_uf">
<option ng-repeat="estado in estados" ng-value="estado.uf"> {{estado.nome}}</option> <!-- [4] -->
</select>
But, the ng-repeat doesn't update with the new value. When I call de console.log($scope.estados);
in the success function of $http.get.then()
I can see that the $scope is updated with correct values, but it is not passed to the View.
I had already tried calling $scope.$apply();
after setting $scope.estados but I it throw the error: [$rootScope:inprog] $digest already in progress
.
If I instance the variable with another value it's bound, like this:
$scope.estados = [
{
uf: 'SP',
nome: 'São Paulo'
}
];
But it's never updated with the database values. What am I doing wrong? I seems to be like something related to references, but I don't know exactly what.
I have 2 recommendations.
First - don't assign your data to a $scope
variable (do a quick Google search on "The Dot Rule" for some context). Instead assign it to a controller variable with this.estados = data
in your controller code, and then access it in the DOM with the ControllerAs variable ... e.g.:
ng-controller="myCtrl as myCtrl"
and then in the DOM myCtrl.estados
instead of $scope.estados
Note: In the $http
callback function, this
won't refer to the Controller anymore. You'll want to store a reference to this
in the body of your Controller code so you can reference it later. I'm prone to using var controller = this;
at the top of all my Controller code so it's easily accessible in any return functions.
Second ... use ng-options for your select instead of a repeater. It's much more robust and ensures proper data binding. So in your instance:
<select ... ng-options="estado.uf as estado.name for estado in myCtrl.estados"></select>
The ...
above is meant to reference anything else you need in your select
element, like ng-model
, class
, etc.
I suspect that just converting to Dot notation instead of using $scope
will remedy the situation... directives like ng-if
and ng-switch
(anything that pulls elements out of the DOM) can make $scope
variables inaccessible somewhat unpredictably. And using ng-options
is always a better practice IMO.