I have an Angular table that should be updated anytime someone adds an item via form. Sometimes it will display the item immediately, but other times it requires a page refresh. I have tried to use $scope.$apply as follows to refresh a REST call that feeds the list, but it is giving me the following error:
Error: [$rootScope:inprog] http://errors.angularjs.org/1.3.8/$rootScope/inprog?p0=%24digest
My core question is can Angular listen to changes to data stored in a REST call to make a change without a page refresh, and if not, what is the best method for meeting my goal of refreshing page data after form entry?
Here is my controller for displaying data:
appControllers.controller('appHomeItemsCtrl', ['$scope', 'appItems', function ($scope, appItems) {
$scope.items = [];
appItems.query({}, function (data) {
$scope.items = data.value;
// Use scope apply to watch for changes instead of requiring a page reload for results
scope.$apply();
});
}]);
Here is my HTML that displays the data:
<table class="table table-striped">
<tr>
<th>Title</th>
<th>
Summary
</th>
<th>
Task Type
</th>
</tr>
<tr ng-repeat="item in items">
<td>
{{item.Title}}
</td>
<td>
{{item.Body}}
</td>
<td>
{{item.Task_x0020_Type}}
</td>
</tr>
</table>
Here is my controller for adding an item:
appControllers.controller('appItemPostCtrl', ['$scope', '$location', 'appItems', 'appTypes', function ($scope, $location, appItems, appTypes) {
var itemEntry = new appItems;
$scope.types = [];
// Get list of types for use in dropdown
appTypes.query(function (typedata) {
var itemTypes = typedata.value;
// Foreach type, push values into types array
angular.forEach(itemTypes, function (typevalue, typekey) {
$scope.types.push({
label: typevalue.Title,
value: typevalue.Title,
});
})
});
// Create item
$scope.createItem = function () {
console.log("Clicked");
itemEntry.Task_x0020_Type = $scope.selectedtype.value;
itemEntry.Title = $scope.itemtitle;
itemEntry.Body = $scope.itembody;
itemEntry.$save();
$location.path('/');
}
$scope.cancel = function () {
$location.path('/');
}
}]);
UPDATE: Using this, it will execute some, but not all of the time. The same is true of the answer provided regarding creating a function or push. Why does it work only "most" of the time?
appControllers.controller('appHomeItemsCtrl', ['$scope', 'appItems', function ($scope, appItems) {
$scope.items = [];
// Use $scope.$evalAsync to watch for changes instead of requiring a page reload for results
$scope.$evalAsync(function () {
appItems.query({}, function (data) {
$scope.items = data.value;
})
});
}]);
The reason you are not seeing anything updating is that $scope.items
doesn't change when you save a new item.
You can fix this issue in two ways:
push the new item into your items after saving
$scope.createItem = function () {
console.log("Clicked");
itemEntry.Task_x0020_Type = $scope.selectedtype.value;
itemEntry.Title = $scope.itemtitle;
itemEntry.Body = $scope.itembody;
itemEntry.$save(function(){
$scope.items.push(itemEntry);
});
}
OR
make a new request to retrieve all the items on the server after saving
function requestItem(){
appItems.query({}, function (data) {
$scope.items = data.value;
})
});
}
// Create item
$scope.createItem = function () {
console.log("Clicked");
itemEntry.Task_x0020_Type = $scope.selectedtype.value;
itemEntry.Title = $scope.itemtitle;
itemEntry.Body = $scope.itembody;
itemEntry.$save(function(){
requestItem();
});
}
The exception is because you put scope
, not $scope
But, you shouldn't really need to use apply here - unless the callback is from a non angular event. But if that is the problem, I believe you should modify $scope within the apply call:
appControllers.controller('appHomeItemsCtrl', ['$scope', 'appItems', function ($scope, appItems) {
$scope.items = [];
appItems.query({}, function (data) {
// Use scope apply to watch for changes instead of requiring a page reload for results
$scope.$apply(function() { $scope.items = data.value; });
});
]);
I have discovered why it was not reloading the data all the time, it was because sometimes the $location.path would execute before the $save was complete. Wrapping $location.path or $route.reload within a $save function allows me to control execution where the $save executes before the "reload" of data occurs. I have awarded the bounty to @pasine, since his post/assistance lead me to this conclusion.
Examples:
$scope.addType = function () {
console.log("Clicked");
typeEntry.Title = $scope.itemtype;
typeEntry.$save(function () { // Ensure execuation of the save befor reloading the view
$route.reload();
});
}
$scope.createItem = function () {
console.log("Clicked");
itemEntry.Task_x0020_Type = $scope.selectedtype.value;
itemEntry.Title = $scope.itemtitle;
itemEntry.Body = $scope.itembody;
itemEntry.$save(function () {
$location.path('/');
});
}