I'm working on an Ionic Framework app. I've installed LocalForage, which I've decided to use as my local storage solution for offline storage.
Here are the LocalForage settings in my config in my app.js:
$localForageProvider.config({
driver : 'localStorageWrapper',
name : 'myApp',
storeName : 'keyvaluepairs',
description : 'some description'
});
$localForageProvider.setNotify(true, true); // itemSet, itemRemove
Next, I created a "table" to store my information using the following code in my services.js file:
dataFactory.getUserProjects = function(){
return $http.get(baseURL + 'getUserProjects.cfm').then(function(response) {
// store locally so that we can access when we are offline
return $localForage.setItem('userprojects', response.data).then(function() {
//console.log("userprojects: ", response.data);
return response.data;
});
}, function() {
// the error indicates that we are offline -> get the locally stored list
console.log("catch failed, get it locally");
return $localForage.getItem('userprojects').then(function(item) {
return item;
});
});
};
Next, I call the function in my Controller:
HoursService.getUserProjects().then(function(response){
$scope.tags = response;
});
This page is the Master page of a Master-Detail pattern. On the Detail page, I again "get" the $localForage object and iterate down to get the single file I need to display on the Detail page:
// Get the userprojects and iterate through them to find the one we need
HoursService.getUserProjects().then(function(response){
var data = response;
angular.forEach(data, function(value, key){
angular.forEach(value.Activity, function(value, key){
if(value.RecordID == $rootScope.pageID){
$scope.singleRecord = value;
}
});
});
});
Ok, this all works great! The problem is when I attempt to update the 'userprojects' JSON object. I use an ng-click to call and execute this function:
$scope.updateHours = function(index, id){
console.log("I clicked delete");
console.log("Index:" + $rootScope.outerID + ", " + "ID: " + $rootScope.pageID);
$localForage.getItem('userprojects').then(function(item){
console.log("My Item:", item);
$scope.item = item;
$scope.path = $scope.item[$rootScope.outerID].Activity[$rootScope.innerID];
$scope.path.ActivityName = 'Magic Dolphin Event';
$rootScope.$broadcast('LocalForageModule.setItem', {
key: $scope.path,
newvalue: $scope.path.ActivityName,
driver: $localForage.driver
});
$state.go("tab.projects", {}, {reload: true});
});
};
If I set a "console.log" to check the object after running the Broadcast event, I can see that the correct field has updated and become "Magic Dolphin Event". Ok, great!
The problem is when I either navigate or use the "$state.go" to redirect BACK to the Master page. The core object does not update and, if I set a console.log, call $localForage.getItem: "userprojects", and look, it has not updated. I've checked my code and I only set the "userprojects" object once, in the factory. I've gone over the documentation again and again and run every search I can think of and haven't had any luck. I'm an Angularjs newb and I suspect I'm not understanding something, or have overlooked or misunderstood something. Any help is appreciated. What am I doing incorrectly?
The key to understanding the problem here is to identify the hidden assumption you are making about the in-memory state of your app. You assume that the instance of Activity
that you modify via your Detail controller is the same instance that will be displayed via the Master controller. This assumption is wrong. The objects might look the same because they came from the same source but they are in fact different instances; updating one will not cause the other to change. See this jsfiddle for a demonstration.
To fix, simply call $localforage.setItem
with your updated object graph before calling $state.go
from your Detail controller. As it is, your code abandons the updated object graph completely. Making this change will ensure that your next call to $localforage.getItem
will get the data you expect.
Side note: you would be better off putting all the $localforage
operations behind some method on your HoursService
. Don't pollute your controllers with the implementation details of your service layer!
Does that help?