AngularJS Unable to repeat asynchronous function inside of service - angularjs

I am building a form that can send and clear cached JSON data using AngularJS to a RESTful web service. When I click submit, the current form data is cached in a JSON object, I then send that data through the web service. Once it is sent I clear the cache.
I have a function I wish to use in multiple controllers (sending cached data). It loops through the cache, sending each JSON object one at a time. When I have the function in a single controller, the code works fine. But once I wrap it in a service and call it in through a controller, my loop no longer works. It runs fine if there is only 1 cache object. But if there are more than 1, the "this.sendCache()" will not fire. I believe this has something to do with the asynchronous function and am not sure what to do about it. Help is greatly appreciated!!!
Controller
app.controller('FormCtrl', function($scope, $filter, $window, getData, Post, randomString, $cordovaProgress, $cordovaDialogs, scService) {
$scope.submitEntry = function() {
var frmData = new Post($scope.postData);
var postCount = window.localStorage.getItem("localPostCount");
postCount ++;
window.localStorage.setItem("localPostCount", postCount);
window.localStorage.setItem("post" + postCount, JSON.stringify(frmData));
scService.sendCache();
};
});
Service
app.service('scService', function ($window, Post, $cordovaProgress, $cordovaDialogs, $timeout) {
this.sendCache = function () {
if(window.Connection){
if(navigator.connection.type != Connection.NONE) {
var postCount = window.localStorage.getItem("localPostCount");
var curCacheObj = new Post(JSON.parse(window.localStorage.getItem("post" + postCount) || '{}'));
if (postCount > 0) {
curCacheObj.$save().then(function(response) {
var servResponse = JSON.stringify(response);
if (servResponse.indexOf("#xmlns:ns3") > -1) {
console.log("Post " + postCount + " sent!");
}
else {
console.log("Unable to post at this time!");
}
}).then(function() {
window.localStorage.removeItem("post" + postCount);
postCount --;
window.localStorage.setItem("localPostCount", postCount);
}).then(function() {
console.log(postCount); //log shows 1
if (postCount > 0) {
this.sendCache(); //yet this won't fire again!
}
else {
$cordovaDialogs.alert('Submission recorded successfully', 'Success', 'OK').then(function() {
console.log('Submission Success');
$window.location.href= 'index.html';
});
}
});
}
else {
$cordovaDialogs.alert('Submission recorded successfully', 'Success', 'OK').then(function() {
console.log('Submission Success');
$window.location.href= 'index.html';
});
}
}
else {
$cordovaDialogs.alert('Your current connection is too slow. Sync at a later time by returning to the app with a better connection.', 'Submission Stored', 'OK').then(function() {
console.log('Submission Cached');
$window.location.href= 'index.html';
});
}
}
};
});

It is actually a very common JS issue, and not related to AngularJS itself. this is not what you think it is. Inside the .then() callback, your context has changed so it is no longer pointing to your service but to the response context from the async call. Therefore, the function sendCache() does not actually exist and cannot be called that way.
All you need to do is take a reference to self at the top of your service:
var self = this;
Then use self instead of this in your call:
self.sendCache();
Note that this is not causing trouble for you with variables like postCount because they're locally defined within the closure and do not require this to be referenced. But if you were to define this.postCount or other variables in your service, you would need to do the same thing.

Related

AngularJS calling a method when its get new data in localStorage

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

AngularJS set constant according to the result of an http request

I want to set module.constant according to a tag which is the result of an HTTP request.
Below is the demo code in my project;
code in app.js
(function(){
var myApp = angular.module('myApp',['ui.router','ui.bootstrap']);
myApp.contant("API_URL","https://www.helloworld.com/API");
myApp.run(function($rootScope.$http){
some code;
});
})();
I want to config a special Id according to the result of an HTTP request like this;
$http.get(API_URL + "/configTag")
.then(function success(response){
var tag = response.data.tag;
if(tag === 0) {
myApp.constant("specialID","111111111");
} else if (tag === 1) {
myApp.constant("specialID","222222222");
} else {
myApp.constant("specialID","333333333");
}
},function error(response){
});
But I am new to front end and AngularJS. I don't know how to realize this?
Short answer: The $http service can't be used to create Angular constants.
The AngularJS framework operates in two phases: the "config" phase and the "run" phase. Once the "run" phase starts, providers can no longer be configured and constants can no longer be added. $http service operations can only be done in the "run" phase.
However a service provider can provide a promise of a constant:
app.factory("specialIDpromise", function(API_URL, $http) {
var promise = $http.get(API_URL + "/configTag")
.then(function success(response){
var tag = response.data.tag;
if(tag === 0) {
//return to chain data
return "111111111";
} else if (tag === 1) {
return "222222222";
} else {
return "333333333";
};
}).catch(function error(response){
//return converts rejection to success
return "something";
});
//return promise to factory
return promise;
});
To use in a controller:
app.controller("myCtrl", function(specialIDpromise) {
specialIDpromise.then(function(specialID) {
console.log(specialID);
//Do other things that depend on specialID
});
});
All operations in the config phase need to be synchronous (including adding constants.) Asynchronous operations such as XHRs by the $http service happen in the run phase of an AngularJS app.
It's possible to do want you want using server side data. You have to inject what modules you need, and run your function to get the data in a document.ready function. Here is how I am doing it, but the only issue I'm having is if the server is unreachable; then the app doesn't get bootstrapped. I placed this code in my app.js file outside of the angular.module code, as it will not work there since the app hasn't been bootstrapped yet.
angular.element(document).ready(function () {
getConstants();
});
//Get constants from server and manually bootstraps application to the DOM
function getConstants() {
var initInjector = angular.injector(["ng"]);
var $http = initInjector.get("$http");
$http.get('urlThatRetrievesConstants').then(function (result) {
/*Below I'm setting the constants on the app module and giving them a
name, which will be used to reference in whatever modules you will use it
in */
angular.module('appModuleName').constant('constantsName', result.data);
/*Here I'm bootstrapping the app*/
angular.bootstrap(document, ["appModuleName"]);
}, function (error) {
/*Handle server errors from request*/
});
}

Angularjs 1.5.x share data with service between components

I am fairly new to angularjs, and would like to ask a few questions.
I am working on a project where I need to get a form object from the server. The form is a complicated tree object with many layers, and I have created 4 different components/tabs to bind to the corresponding objects. I had created a Service to get the data.
angular.module('myService', ['restangular'])
.factory('FormService', ['Restangular', '$q', function(Restangular, $q) {
function FormService() {
var self = this;
self.form = null;
self.getForm = function getForm(id)
{
var deferred = $q.defer();
if (self.form !== null)
{
deferred.resolve(self.form);
console.log("Cache!");
}
else {
Restangular.one('form', id).get()
.then(function successCallback(response)
{
self.form = response;
deferred.resolve(response);
console.log("from server!");
}, function errorCallback(response) {
deferred.reject(response);
console.log("error, cannot resolve object");
});
}
return deferred.promise;
}
return new FormService();
}])
});
Then I had my components all with similar config below:
angular.module('page1Summary', ['formService']).component('page1Summary', {
templateUrl: 'page1-summary/page1-summary.template.html',
controller: ['FormService', function Page1SummaryController(FormService) {
var ctrl = this;
// ******* Init Params Start *********** //
this.$onInit = function() {
// init value when object ready
FormService.getForm()
.then(
/* on success */
function successCallback(data) {
console.log("page1-summary init");
ctrl.form = data;
console.log("page1-summary got the data");
},
/* on error */
function errorCallback(data)
{
console.log("failed to get form");
}
);
}
/* other stuff here */
}
I was printing either "cache!" or "from server" on the getForm service. So that I can figure out whether I am pulling the data from server or memory. However, everytime I refresh, the result is different. Sometimes, the data saved in the local variable in service, and got "cached", but sometimes, some of my pages will get the data "from server".
I would like to know what is going wrong? I thought only the first time the service would get from server, but it seems like it is not the case.
Can someone please help me out and point out what I did wrong?
Thanks in advance!
You are caching your result into self.form.
self.form is again a variable FormSerivce Factory member.
It will cache the result till you do not refresh the page.
Once you refresh the page the value in self.form will get reset just like all the other variable in your application.
What you want is instead of caching result in self.form, cache it in localstorage.
So you can get the result back even after your page refresh.

How to keep the AngularJS template updated while getting socket updates?

I get updates from the Backend via socket connections. I want to have an automatically updating Frontend with AngularJS while using a data object for the data I got from the Backend.
What do I have?
Template:
Status: {{unit.getStatus()}}
Controller 1:
function ($scope, unitFactory) {
// register to unit factory to get the updates and do
// $scope.unit = data.getUnit();
}
Controller 2:
function ($scope, unitFactory) {
// register to unit factory to get the updates and do
// $scope.unit = data.getUnit();
// $scope.foo = data.getFoo();
}
Service:
function(requestFactory) {
var unit = {},
foo = {};
Sockets.socket('unit', function (response) {
unit = new Unit(response['data']);
foo = new Foo(response['foo']);
// This is the data object which has to be send to the controllers
var Data = {
getUnit: function () {
return unit;
},
getFoo: function() {
return foo;
}
// some more functions...
}
});
}
Sockets:
channel.on('data', function (event) {
try {
event = JSON.parse(event);
// successCallback is what is given as second parameter in the `Service`.
$rootScope.$apply(successCallback(event));
} catch (e) {
console.log('Error: ' + e);
}
});
How should it work together?
Socket update comes in and gets handled by the Sockets object
Sockets call the function which is registered in the Service
The callback function in Service process the data
MISSING The processed data wrapped in an object has to be delivered to the controllers
MISSING The controllers can do whatever they want to do with the data whenever there is a new update.
The template gets auto updated.
Can anyone help me with the MISSING parts? I tried a lot of different approaches but I ran to dead ends every time.
Have you tried returning a promise for the data, and then $state.reload() ?
Got it solved using the 'data model pattern':
Template 1 (used by Controller 1):
Status: {{unit.data.getStatus()}}
Template 2 (used by Controller 2):
Status: {{foo.data.getBar()}}
Controller 1:
function ($scope, unitFactory) {
$scope.unit = unitFactory.getUnit();
}
Controller 2:
function ($scope, unitFactory) {
$scope.unit = unitFactory.getUnit();
$scope.foo = unitFactory.getFoo();
}
Service:
function(requestFactory) {
var unit = { data:{} },
foo = { data:{} };
Sockets.socket('unit', function (response) {
unit.data = new Unit(response['data']);
foo.data = new Foo(response['foo']);
});
return
getUnit: function ()
return unit;
},
getFoo: function() {
return foo;
}
// some more functions...
}
}
Sockets:
channel.on('data', function (event) {
try {
event = JSON.parse(event);
// successCallback is what is given as second parameter in the `Service`.
$rootScope.$apply(successCallback(event));
} catch (e) {
console.log('Error: ' + e);
}
});
Since the data is stored in an object the data in the templates is updated (since it is a reference). I have to get used to these extra attributes data and it is not looking nice but it does its job.

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;
}

Resources