user6039980 user6039980 - 5 months ago 845
AngularJS Question

AngularJS: ng-repeat doesn't refresh after updating array

I'm using Ionic framework, and trying to display a list of "Card" elements using the following HTML code:

<div ng-repeat="case in cases">

<div class="list card">

<div class="item item-avatar">
<button ng-click="rmpost(card.key)" style="background: transparent; border: none;"><i class="icon ion-close-round"></i></button><!--if (dataSnapshot.val().volunteer_email == firebase.auth().currentUser.email)-->
<h2>{{card.volunteer_name}} ({{card.volunteer_email}})</h2>
<p>{{card.title}} ({{card.creationdate}})</p>
</div>

<div class="item item-body">
<img class="full-image" src={{card.snapshot}}>
<p>{{card.message}}</p>
</div>

</div>

</div>


This is my JS code used inside the controller for updating the cases array:

.controller('campaignslookCtrl', function($rootScope, $scope, $cordovaGeolocation, $state, $ionicPopup, Util)
{
$scope.reload = function()
{
$scope.reload();
}

$scope.rmpost = function(key)
{
$scope.showConfirm = function()
{
var confirmPopup = $ionicPopup.confirm({ title: 'Warning! this action cannot be undone', template: 'Are you sure you want to delete this post?' });

confirmPopup.then(function(res)
{
if(res)
{
firebase.database().ref('/cases').remove(key);
$state.reload();
return;
}
});
}
}

$scope.cases = [];

//...

firebase.database().ref('/cases')
.on('child_added', function(dataSnapshot)
{

$scope.cases.push({key: dataSnapshot.key,
volunteer_name: dataSnapshot.val().volunteer_name,
volunteer_email: dataSnapshot.val().volunteer_email,
title: dataSnapshot.val().title,
creationdate: '',//(new Date(dataSnapshot.val().creationdate)).toString(),
snapshot: dataSnapshot.val().snapshot,
message: dataSnapshot.val().message});

});
//..
})


The $scope.cases got well displayed when being initialized to a constant value, but when being dynamically updated (via $push) nothing gets displayed.

I need an emergency help.

Thanks in advance.

Answer

The problem is that you're updating your model outside of the angular context. The data has changed, but angular hasn't run it's own internal digest and hasn't been made aware that any changes have occurred within your model.

firebase.database().ref('/cases')
        .on('child_added', function(dataSnapshot) //FIREBASE EVENT NOT WITHIN ANGULAR CONTEXT

You need to use angular's $scope.$apply method.

https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply

$apply([exp]); $apply() is used to execute an expression in angular from outside of the angular framework. (For example from browser DOM events, setTimeout, XHR or third party libraries). Because we are calling into the angular framework we need to perform proper scope life cycle of exception handling, executing watches.

Here's how to use $scope.$apply to get into a valid angular context and then update the data:

firebase.database().ref('/cases')
        .on('child_added', function(dataSnapshot)
            {
                $scope.$apply(function() {
                    //we are now within the angular context, so any updates to the model with be handled correctly
                    $scope.cases.push({key: dataSnapshot.key,
                                     volunteer_name: dataSnapshot.val().volunteer_name,
                                     volunteer_email: dataSnapshot.val().volunteer_email,
                                     title: dataSnapshot.val().title,
                                     creationdate: '',//(new Date(dataSnapshot.val().creationdate)).toString(),
                                     snapshot: dataSnapshot.val().snapshot,
                                     message: dataSnapshot.val().message});
                });
});

You could theoretically call $scope.$digest manually, but this isnt' recommended according to the angular docs. $apply forwards exceptions correctly and also allows watchers to be processed. It's generally better to use this method than handling $digest on your own.

$digest(); Processes all of the watchers of the current scope and its children. Because a watcher's listener can change the model, the $digest() keeps calling the watchers until no more listeners are firing. This means that it is possible to get into an infinite loop. This function will throw 'Maximum iteration limit exceeded.' if the number of iterations exceeds 10.

Usually, you don't call $digest() directly in controllers or in directives. Instead, you should call $apply() (typically from within a directive), which will force a $digest().

Comments