AngularJS calling a method when its get new data in localStorage - angularjs

I am using angularJS localstorage and i injected this very well but problem in code.
I want when localstorage gets new data, so that its call a function $scope.getAllContact() automatically.
I am trying to solve this issue coz, for example, i opened two tab in browser, if i change anything in one tab, the latest change should reflect in other tab too without any reload and refresh.
At first see my code:
app.controller('crudCtrl', function($scope, $http, $timeout, $localStorage, $sessionStorage) {
$scope.getAllContact = function() {
var data = $http.get("http://127.0.0.1:8000/api/v1/contact/")
.then(function(response) {
$scope.contacts = response.data;
// show something if success
}, function(response) {
//show something error
});
};
$scope.getAllContact();
// below method will post data
$scope.formModel = {};
$scope.onSubmit = function () {
$http.post('http://127.0.0.1:8000/api/v1/contact/', $scope.formModel)
.then(function(response) {
$localStorage.name = response.data;
$timeout(function() {
$scope.successPost = '';
}, 4000);
//below $scope will push data in client site if the request is success
$scope.contacts.push(response.data);
//if any error occurs, below function will execute
}, function(response) {
// do nothing for now
});
};
});
above in $scope.onSubmit() methond, i send the submitted data to localstorage too, so i need if localstorage gets new data, it should execute $scope.getAllContact() always automatically without any refresh.
Can anyone fix me this issue?

Use the storage event:
angular.element(window).on("storage", $scope.getAllContact);
$scope.$on("$destroy", function() {
angular.element(window).off("storage", $scope.getAllContact);
});
For information, see
MDN Window API Reference - storage_event
MDN Web API Reference - StorageEvent

Related

AngularJS reload data in page without refresh

What i am doing is; i get the data from db and put it in
$scope.GetMyData = function () {
//http get request here
.then(function (result) {
$scope.myData = result.data;
});
}
and when i want to refresh
$scope.myData
I have a function which is ticking every 15 seconds to trigger my GetMyData function to get the updated data. The timer function is like;
$interval(function () {
$scope.GetMyData();
}, 15000);
The problem is whenever I've overwritten $scope.myData, Page is not actually refreshing but the view looks like it's refreshed. Is there a way to solve this ?
Use $watch service helps you to run some code when some value attached to the $scope has changed.
$scope.myData = "";
$scope.$watch('myData', function() {
/*Here you can bind changed value to your view, updated value can be reflected on $scope.myData change */
alert('myData has changed!');
});

Force a $http.get to run every time the view is loaded - Angular

I have a view within my App which does a database pull to show a user images they have previously uploaded.
The problem is that another view allows them to upload new images, but when switching back to the view of their uploaded images, they have to do a full page refresh to see their new uploads.
The question is how can I force the $http.get to run every time the view is loaded?
This is what I am trying but is not doing what I think it should:
capApp.controller('myUploadedPhotos', function ($scope, $http) {
$scope.nameFilter = "";
$http.get("/ajax/myUploadedPhotos.php", { cache: false})
.success(function(response) {
$scope.photos = response;
});
});
Is there a way to do this?
Your code looks correct so possibly the request is cached from the server? You can try appending a random string to your url to break the cache. e.g.
"/ajax/myUploadedPhotos.php" + new Date().getTime()
After thinking about it, I think you can also remove the { cache: false} because Angular also won't be able to cache the request if the timestamp changes. The old requests would just be sitting around somewhere taking up memory.
I'm not quite understand your question, but there isn't any issues with next initialization behaviour:
(function(angular) {
var capApp = angular.module('yourModule', []);
capApp.controller('myUploadedPhotos', ['$scope', '$http',
function ($scope, $http) {
$scope.nameFilter = "";
$scope.actions = angular.extend($scope.actions || {}, {
init: function () {
return $http.get("/ajax/myUploadedPhotos.php", {cache: false}).then(
function (response) {
$scope.photos = response;
}, function (reason) {
console.log('Error occured: ' + reason);
});
}
});
// You could even use it in $watch
$scope.actions.init();
}
]);
})(angular);

Controlling order of execution in angularjs

I have inherited an angular app and now need to make a change.
As part of this change, some data needs to be set in one controller and then used from another. So I created a service and had one controller write data into it and one controller read data out of it.
angular.module('appRoot.controllers')
.controller('pageController', function (myApiService, myService) {
// load data from API call
var data = myApiService.getData();
// Write data into service
myService.addData(data);
})
.controller('pageSubController', function (myService) {
// Read data from service
var data = myService.getData();
// Do something with data....
})
However, when I go to use data in pageSubController it is always undefined.
How can I make sure that pageController executes before pageSubController? Or is that even the right question to ask?
EDIT
My service code:
angular.module('appRoot.factories')
.factory('myService', function () {
var data = [];
var addData = function (d) {
data = d;
};
var getData = function () {
return data;
};
return {
addData: addData,
getData: getData
};
})
If you want your controller to wait untill you get a response from the other controller. You can try using $broadcast option in angularjs.
In the pagecontroller, you have to broadcast your message "dataAdded" and in the pagesubcontroller you have to wait for the message using $scope.$on and then process "getData" function.
You can try something like this :
angular.module('appRoot.controllers')
.controller('pageController', function (myApiService, myService,$rootScope) {
// load data from API call
var data = myApiService.getData();
// Write data into service
myService.addData(data);
$rootScope.$broadcast('dataAdded', data);
})
.controller('pageSubController', function (myService,$rootScope) {
// Read data from service
$scope.$on('dataAdded', function(event, data) {
var data = myService.getData();
}
// Do something with data....
})
I would change your service to return a promise for the data. When asked, if the data has not been set, just return the promise. Later when the other controller sets the data, resolve the previous promises with the data. I've used this pattern to handle caching API results in a way such that the controllers don't know or care whether I fetched data from the API or just returned cached data. Something similar to this, although you may need to keep an array of pending promises that need to be resolved when the data does actually get set.
function MyService($http, $q, $timeout) {
var factory = {};
factory.get = function getItem(itemId) {
if (!itemId) {
throw new Error('itemId is required for MyService.get');
}
var deferred = $q.defer();
if (factory.item && factory.item._id === itemId) {
$timeout(function () {
deferred.resolve(factory.item);
}, 0);
} else {
$http.get('/api/items/' + itemId).then(function (resp) {
factory.item = resp.data;
deferred.resolve(factory.item);
});
}
return deferred.promise;
};
return factory;
}

angular controller needing refresh to pickup new data in a factory promise

I have a pretty standard app which will display news items from a remote JSON feed. So basically I have decided to poll the remote server and store the JSON in localStorage (to enable offline usage). For the moment, I have a manual page/view I must click on to update the localStorage , this works fine.
The problem is that after I use my temporary manual update page, I then go to the news page/view and it is not updated. To view the current JSON contents I must hit refresh (while still developing in the browser.)
I'm totally new to Angular and have tried to find solutions to this myself - $watch or reload: true seem to be suggested as fixes, but I cannot get them to work in my case.
Route
.state('tab.news', {
url: '/news',
reload: true,
views: {
'news-tab': {
templateUrl: 'templates/news_home.html',
controller: 'newsCtrl'
}
}
})
factory
angular.module('schoolApp.services', [])
.factory('newsService', function($q) {
var newsHeadlines =localStorage.getItem('newsHeadlines') || '{"status":"READFAIL"}'; // get news as a JSON string. if newsHeadlines not found return a JSON string with fail status
var newsHeadlinesObj = JSON.parse(newsHeadlines);// convert to an object
console.log("factory newsService ran");
return {
findAll: function() {
var deferred = $q.defer();
deferred.resolve(newsHeadlinesObj);
return deferred.promise; // or reject(reason) to throw an error in the controller https://docs.angularjs.org/api/ng/service/$q
},
findById: function(newsId) {
var deferred = $q.defer();
var newsItem = newsHeadlinesObj[newsId];
deferred.resolve(newsItem);
return deferred.promise;
}
}
});
Controller
schoolApp.controller('newsCtrl', function($scope, newsService) {
console.log ( 'newsCtrl ran' );
newsService.findAll().then(function (newsHeadlinesObj) {
$scope.newsHeadlinesObj = newsHeadlinesObj;
}, function(error){
console.log(error)
});
})
Looking at my console, the first time I read the news, the factory then controller run, but if I go to pull more data down, then go hack to news, only the controller runs, unless I refresh, then both run again.
I do not need the news view to update 'live' while still on it (but if that can be easilly done all the better) - just to pick up new data when you go back to news after being elsewhere in the app.
Thank you.
Factories return singletons and only run once. The object newsService is cached by angular. The var declarations for newsHeadlines and newsHeadlinesObj will only ever run once; meaning your promise returning methods will always resolve the promise with the same data that was retrieved when your factory was first instantiated. You should put them in a function and call it from your find methods on the singleton object.
.factory('newsService', function($q) {
function getHeadlines() {
var newsHeadlines = localStorage.getItem('newsHeadlines') || '{"status":"READFAIL"}'; // get news as a JSON string. if newsHeadlines not found return a JSON string with fail
return JSON.parse(newsHeadlines);// convert to an object
}
return {
findAll: function() {
var headlines = getHeadlines();
var deferred = $q.defer();
deferred.resolve(headlines);
return deferred.promise; // or reject(reason) to throw an error in the controller https://docs.angularjs.org/api/ng/service/$q
},
findById: function(newsId) {
var headlines = getHeadlines();
var deferred = $q.defer();
var newsItem = headlines[newsId];
deferred.resolve(newsItem);
return deferred.promise;
}
}
});
PS - I'm sure you know and are planning to do things differently later or something, but just in case you don't: Using promises here is pointless and you have no need for $q here. You could simply return the data instead of returning the promises.
I solved this withouut promises, I just used $rootScope in the factory and $scope.$on in the controller; when I change the factory, i use $rootScope.$broadcast to tell the controller that I change it.
.factory('dataFactory', ['$http', '$rootScope', function ($http, $rootScope) {
var dataFactory = {
stock: null,
getStock: getStock
}
function getStock() {
$http.get("/api/itemfarmacia/").then(function success(res) {
dataFactory.stock = res.data;
$rootScope.$broadcast('changingStock'); //Ones who listen this will see it
}, function error(err) {
console.log("Bad request");
})
}
return dataFactory;
}])
and in the controller
.controller('atencion', ["$scope", "$state", "dataFactory", function ($scope, $state, dataFactory) {
$scope.stock = dataFactory.stock; //At first is null
dataFactory.getStock(); //wherever you execute this, $scope.stock will change
$scope.$on('changingStock', function () {//Listening
$scope.stock = dataFactory.stock; //Updating $scope
})
}])

Change controller variable from within a service

In the "ok"-function of a modal, I'm trying to update a variable from the scope that opened the modal. This
$scope.modalOptions.assets.length = 0;
perfectly works: the variable "assets" in the "parent" scope immediatly changes and, while the modal is still open, the data represantation of "assets" is updated and emptied in the main page.
What bugs me is that changing above to
$scope.modalOptions.assets = $scope.modalOptions.assetFactory.query();
has no effect at all. I can verify that the API Controller is called and that it returns new data which should, in effect, change the representation of "assets" as well.
The variable itself is defined in the controller like this:
$scope.assets = bondFactory.query();
And I pass it in to the Modal-Service like this:
assets: $scope.assets
I'd be thankful for tips and ideas..
EDIT
How the Modal is called:
$scope.postModal = function () {
//Pass view
var customModalDefaults = {
templateUrl: 'scripts/app/views/postBond.html'
}
//Pass data
var customModalOptions = {
asset: angular.copy($scope.asset),
assets: $scope.assets,
currencies: globalVariables.currencies,
assetFactory: bondFactory,
validationErrors: [],
action: 'POST'
};
//Show & Callback
ModalAssetService.showModal(customModalDefaults, customModalOptions).then(function (result) {
// fill me with usefull content
});
};
The Modal Service itself:
app.service('ModalAssetService', ['$modal',
function ($modal) {
var modalDefaults = {};
var modalOptions = {};
this.showModal = function (customModalDefaults, customModalOptions) {
//Create temp objects to work with since we're in a singleton service
var tempModalDefaults = {};
var tempModalOptions = {};
//Map angular-ui modal custom defaults to modal defaults defined in service
angular.extend(tempModalDefaults, modalDefaults, customModalDefaults);
//Map modal.html $scope custom properties to defaults defined in service
angular.extend(tempModalOptions, modalOptions, customModalOptions);
//Create controller
tempModalDefaults.controller = function ($scope, $modalInstance, $resource, errorService) {
$scope.modalOptions = tempModalOptions;
// exit modal with "ok"
$scope.modalOptions.ok = function (result) {
//POST
if ($scope.modalOptions.action == 'POST') {
// try and post asset
$scope.modalOptions.assetFactory.save($scope.modalOptions.asset, function () {
// success
$scope.modalOptions.assetFactory.query({}, function (data) {
$scope.modalOptions.assets = data;
// $modalInstance.close();
});
}, function (error) {
// error
$scope.modalOptions.validationErrors = errorService.fn(error);
});
};
//PUT
if ($scope.modalOptions.action == 'PUT') {
//TODO
alert("put");
};
};
// exit modal with "cancel"
$scope.modalOptions.close = function (result) {
$modalInstance.dismiss('cancel');
};
}
return $modal.open(tempModalDefaults).result;
};
}
]);
EDIT 2: the bondFactory
app.factory('bondFactory', ['$resource', function ($resource) {
return $resource('../api/bond/:id', null, {
'update': { method: 'PUT' }
})
}]);
SOLUTION
I still did not figure out exactly how to fix the problem of the scope not being notified about a change if the change results from an assignment of a $resource GET to a variable from said scope.
Anyways, for my specific case, I found a better solution: when clicking "OK" in the modal, the new asset is being sent to the API. If errors happen there (validation failed etc.), the Modal will stay open and some notifications will inform the user. If the new asset was posted successfully, then a new GET is sent to the API and only the last new asset is being added to the existing array of asset-elements. This means no "flashing", just one more row is added to the list of assets.
It's not a good form of programming to assume that the last element from the GET-Request is definitly the new asset and there is of course some overhead in retrieving the complete list of all assets when just the very last of these would suffice, but I guess it works and I'll just add some sort to the API to make sure it always is in right order. Code:
$scope.modalOptions.assetFactory.save($scope.modalOptions.asset, function () {
//success
$scope.modalOptions.assetFactory.query({}, function (result) {
$scope.modalOptions.assets.push(result[result.length - 1]);
$modalInstance.close();
});
}, function (error) {
//error
$scope.modalOptions.validationErrors = errorService.fn(error);
});
Your factory is performing an async operation, so you need to use callbacks to access the data and have the view update:
$scope.modalOptions.assetFactory.query({}, function(data) {
$scope.modalOptions.assets = data;
});
Without knowing how you are calling the modal, or if you are using a hand-rolled one or the one from UI-Bootstrap, I can only guess at how things are working. Having said that, if you are not using the modal from UI-Bootstrap you really should be. It uses promises and has the ability to return just about anything from the modal.
$scope.myModalPromise = $modal.open(modalConfig);
$scope.myModalPromise.result.then(function(data){
//things to do upon closing the modal with $close
//data is any value or object that you want to pass back
}, function(data){
//things to do upon closing the modal with $dismiss
//data is any value or object that you want to pass back
});
The setup lets your modal perform asynch operations and only return the value when they have completed. The beauty of this is that they are returned to the calling controller cleanly and in an Angular way.

Resources