a service to provide Restangular objects - angularjs

I'm trying to make a service that will load persons from the server on demand. The first version looked like this:
services.factory('PersonServiceOld', function(Restangular, ErrorService) {
var persons = [];
var requesting = [];
var get = function(id) {
if (requesting[id]) {
return persons[id];
}
requesting[id] = true;
persons[id] = {'id' : id, 'photoName' : '0.png'};
Restangular.one('persons', id).get().then(function(success) {
persons[id].firstName = success.firstName;
persons[id].lastName = success.lastName;
persons[id].photoName = success.photoName;
}, function(failure) {
requesting[id] = false;
ErrorService.serverError(failure);
});
return persons[id];
};
var reset = function() {
persons = [];
requesting = [];
};
return {
getPerson : get,
clearCache : reset,
};
});
That way I get a reference to an object right away and it will be filled with data slightly after. It worked well... until I noticed that in another use case, I also want to request the address of a person like
var person = PersonService.get(id);
person.one(address).get().then(.......
but the objects returned from my PersonService aren't Restangular objects. So I tried something else:
services.factory('PersonService', function(Restangular, ErrorService) {
var persons = [];
var get = function(id) {
if (!persons[id]) {
persons[id] = Restangular.one('persons', id);
persons[id].get().then(function(success) {
}, function(failure) {
ErrorService.serverError(failure);
persons[id] = null;
});
}
return persons[id]; // also tried: persons[id].$object
};
return {
getPerson : get
};
});
I hope somebody understands what I'm trying to do here and can give me a good pointer on how to achieve this.

Check this Plunkr for a complete example.
As Restangular returns promises, and your get function may be asynchronous or synchronous (in case you use your own cache), you need to create a promise for returning always the same type of object.
You can do it as described in the Angular documentation for $q service.
So your get function may look like :
var get = function (id) {
var deferred = $q.defer();
if (store[id]) {
deferred.resolve(store[id]);
} else {
Restangular.one('person', id).get().then(function (res) {
store[res.id] = res;
deferred.resolve(res);
}, function (err) {
deferred.reject(err);
});
}
return deferred.promise;
}
Then in your controller, for retrieving your data :
PersonService.get(475).then(function (person) {
// stuff
}, function (err) {
// err handling
});

Related

Call a asynchronous method after another in AngularJS

I have a $watch attached to the appId variable. SO, when appId get changed it called the init function
$scope.$watch('appId', function() {
if ($scope.appId != '') {
console.log('Inside appId watch');
$scope.init();
}
});
init() calls two service methods
$scope.init = function() {
if ($scope.appId === undefined || $scope.appId == '') return false;
$scope.getPrincipals($scope.loadPrincipals);
$scope.getSignaturesByAppId($scope.loadSignatures);
};
The methods are
$scope.getSignaturesByAppId = function (callback) {
ApplicationDataSource.getSignaturesByAppId($scope.appId, function (result) {
callback(result);
$scope.$apply();
});
};
$scope.loadSignatures = function (result) {
var signatureResultSet = angular.fromJson(result[0]);
$scope.appSignatures = signatureResultSet;
if($scope.appSignatures.length===0){
$scope.setDefaultValue();
}
else{
$scope.setValueFromObject();
}
};
$scope.getPrincipals = function (callback) {
ApplicationDataSource.getApplicationPrincipalList($scope.appId, function (result) {
callback(result);
$scope.$apply();
});
};
$scope.loadPrincipals = function (result) {
var guarantorResultSet = angular.fromJson(result[0]);
$scope.principals = guarantorResultSet;
};
The problem occurs here. In loadSignatures(), I have called a method setDefaultValue() which needs the data retrieve from loadPrincipals. So, when, loadSignatures called, principal data is not updated.
How to call the $scope.getPrincipals($scope.loadPrincipals) after $scope.getSignaturesByAppId($scope.loadSignatures) finish to retrieve data.
You can use Promises, here is an example:
var promise = callThatRunsInBackground();
promise.then(
function(answer) {
// do something
},
function(error) {
// report something
},
function(progress) {
// report progress
});
So in your code it might look like (I will leave it to you to fix as I'm not going to compile or check for syntax errors):
var getPrincipals = $scope.getPrincipals($scope.loadPrincipals);
getPrincipals.then(
function(datapayload) {
//do something with datapayload perhaps
$scope.getSignaturesByAppId($scope.loadSignatures);
});
This will make it wait until getPrincipals is finished before running getSignaturesbyAppId
What solve my issue is, I called $scope.getSignaturesByAppId($scope.loadSignatures); in loadPrincipals callback function, not in init()
$scope.loadPrincipals = function (result) {
var guarantorResultSet = angular.fromJson(result[0]);
$scope.principals = guarantorResultSet;
$scope.getSignaturesByAppId($scope.loadSignatures);
};

AngularJs Service called from two places returns same value

I am new to angular and pardon my ignorance if any. I am trying to create a simple service that will do a get funtionality and serve data into array.
The problem i am having is, no mattter what i do - i always get the same data for any parameter i pass.
Here is my sample service
function myService($http, $q) {
var service = {
getSomeData: getSomeData:
};
var def = $q.defer();
return service;
function getSomeData(category) {
if (category === 'books') {
url = 'http://www.someurl1';
} else {
url = 'http://www.someurl2'
};
$http.get(url, {
params: {
'type': category
}
}).success(function(data) {
def.resolve(data);
}).error(function() {
def.reject('Failed to get data');
});
return def.promise;
}
}
})();
Once i have this, in my controller, i am trying to call it for sample purposes like this
$scope.someData = [] ;
$scope.someData.push(myService.getSomeData('DVDs');
$scope.someData.push(myService.getSomeData('books');
Now when i my look at my $scope.someData,
i have an array of two objects - the problem being that they are always the same and doesnt have data specific to books and dvds.
Another minor issue I have is my object someData has
array --> Promise -- > $$state --> value
which then has the actual data. how can i get the data directly.
i tried return def.promise(data);
One problem is you are only creating one promise outside the getSomeData function.
A promise can only be resolved once. You need a new promise for each request.
Also $http already returns a promise but you can't push it directly into an array as data.
your code should look more like:
Service:
function myService($http, $q) {
var service = {
getSomeData: getSomeData
};
return service;
function getSomeData(category) {
if (category === 'books') {
url = 'http://www.someurl1';
} else {
url = 'http://www.someurl2'
};
return $http.get(url, {
params: {
'type': category
}
}).then(function(response) {
return response.data;
}).catch(function(err) {
console.log("Ooops", err)
});
}
}
Controller
$scope.someData = [] ;
myService.getSomeData('DVDs').then(function(data){
data.forEach(function(item){
$scope.someData.push(item);
});
});

Can't get data out of service in angularjs

I'm using a service in order to pass data between different instances of an AngularJS controller. I know that this is not the best way to do it but it's the way that fits my case. The problem is that I cannot get data out of that Service.
var app = angular.module('MovieApp', ['ngResource']);
app.factory('factMovies', function($resource) { //this returns some movies from MongoDB
return $resource('/movies');
});
app.service('SnapshotService', function(factMovies) {
//this is used to pass data to different instances of the same controller
//omitted getters/setters
this.snapshots = [];
this.init = function() {
var ctrl = this;
var resp = factMovies.query({}, function() {
if (resp.error) {
console.log(resp.error)
} else {
tempDataset = []
//do stuff and put the results in tempDataset
ctrl.snapshots.push(tempDataset);
console.log(tempDataset); //prints fine
return tempDataset;
}
});
};
});
app.controller('TileController', function(SnapshotService) {
this.dataset = [];
this.filters = [];
this.init = function() {
var ctrl = this;
var data = SnapshotService.init(function() {
console.log(ctrl.data); //doesn't even get to the callback function
});
};
});
I really can't figure out what I'm doing wrong..
SnapshotService.init() doesn't take any parameters - meaning the anonymous function you pass in with the SnapshotService.init() call in TileController does nothing.
What you need to do is add the parameter to the init function definition and then call it in the code:
app.service('SnapshotService', function(factMovies) {
//this is used to pass data to different instances of the same controller
//omitted getters/setters
this.snapshots = [];
this.init = function(cb) {
var ctrl = this;
var resp = factMovies.query({}, function() {
if (resp.error) {
console.log(resp.error)
} else {
tempDataset = []
//do stuff and put the results in tempDataset
ctrl.snapshots.push(tempDataset);
console.log(tempDataset); //prints fine
cb(ctrl.snapshots);
}
});
};
});

test if service exists

I'm opening a connection of my database in my ionic application. I want that this connection can be used in all tabs. For that I do a service and try save in them the connection variable.
.service('connect', function () {
var arr = {};
var connection = new ...
arr['connection'] = connection;
}
return arr;
})
Then, to access to them, I do:
connect.connection;
Problem: I want to do a new connection just the first time I do a connect.connection. The other ones I want just to get back the variable.
I tried anything like that:
if(connect == null){
var connection = new ...
arr['connection'] = connection;
}
inside my service, but it cannot work.
Do you have an idea?
Angular JS Services are singleton, there is only one object, but is injected into many places. (objects are passed by reference to a method)
For the documentation please look here.
Here I have example where I have initialized method of service to initialized the db.
.service('DBService', function($q, $http,$window,$cordovaSQLite) {
return {
initialize: function(dbname) {
var deferred = $q.defer();
var promise = deferred.promise;
var response = {result : '',error : ''};
try {
if ($window.cordova) {
//result = $cordovaSQLite.openDB("myapp.db");
response.result = $cordovaSQLite.openDB({name:"dbname.db",location: 'default'});
} else {
// Ionic serve syntax
response.result = $window.openDatabase("dbname.db", "1.0", "dbname", -1);
}
deferred.resolve(response);
} catch (error) {
response.error = error;
deferred.reject(response);
}
promise.success = function(fn) {
promise.then(fn);
return promise;
}
promise.error = function(fn) {
promise.then(null, fn);
return promise;
}
return promise;
}
}
});
And in the controller You need to call like as follows
$ionicPlatform.ready(function() {
DBService.initialize(db_name)
.success(function(data) {
//db object
$scope.db = data.result;
}).error(function(data) {
console.log(data);
});
});
Hope this will help you.
If I understand you correctly you could do something like:
.service('connect', function () {
var arr = {};
var connection = new ...
arr['connection'] = connection;
return arr;
})
A service is singleton so this will only be run once.
So when you need to retrieve the connection its something like
connect.connection;

Angular: Rewriting function to use promise

I'm using an Angular factory that retrieves data from a feed and does some data manipulation on it.
I'd like to block my app from rendering the first view until this data preparation is done. My understanding is that I need to use promises for this, and then in a controller use .then to call functions that can be run as soon as the promise resolves.
From looking at examples I'm finding it very difficult to implement a promise in my factory. Specifically I'm not sure where to put the defers and resolves. Could anyone weigh in on what would be the best way to implement one?
Here is my working factory without promise:
angular.module('MyApp.DataHandler', []) // So Modular, much name
.factory('DataHandler', function ($rootScope, $state, StorageHandler) {
var obj = {
InitData : function() {
StorageHandler.defaultConfig = {clientName:'test_feed'};
StorageHandler.prepData = function(data) {
var i = 0;
var maps = StorageHandler.dataMap;
i = data.line_up.length;
while(i--) {
// Do loads of string manipulations here
}
return data;
}
// Check for localdata
if(typeof StorageHandler.handle('localdata.favorites') == 'undefined') {
StorageHandler.handle('localdata.favorites',[]);
}
},
};
return obj;
});
Here's what I tried from looking at examples:
angular.module('MyApp.DataHandler', []) // So Modular, much name
.factory('DataHandler', function ($rootScope, $q, $state, StorageHandler) {
var obj = {
InitData : function() {
var d = $q.defer(); // Set defer
StorageHandler.defaultConfig = {clientName:'test_feed'};
StorageHandler.prepData = function(data) {
var i = 0;
var maps = StorageHandler.dataMap;
i = data.line_up.length;
while(i--) {
// Do loads of string manipulations here
}
return data;
}
// Check for localdata
if(typeof StorageHandler.handle('localdata.favorites') == 'undefined') {
StorageHandler.handle('localdata.favorites',[]);
}
return d.promise; // Return promise
},
};
return obj;
});
But nothing is shown in console when I use this in my controller:
DataHandler.InitData()
.then(function () {
// Successful
console.log('success');
},
function () {
// failure
console.log('failure');
})
.then(function () {
// Like a Finally Clause
console.log('done');
});
Any thoughts?
Like Florian mentioned. Your asynchronous call is not obvious in the code you've shown.
Here is the gist of what you want:
angular.module("myApp",[]).factory("myFactory",function($http,$q){
return {
//$http.get returns a promise.
//which is latched onto and chained in the controller
initData: function(){
return $http.get("myurl").then(function(response){
var data = response.data;
//Do All your things...
return data;
},function(err){
//do stuff with the error..
return $q.reject(err);
//OR throw err;
//as mentioned below returning a new rejected promise is a slight anti-pattern,
//However, a practical use case could be that it would suppress logging,
//and allow specific throw/logging control where the service is implemented (controller)
});
}
}
}).controller("myCtrl",function(myFactory,$scope){
myFactory.initData().then(function(data){
$scope.myData = data;
},function(err){
//error loudly
$scope.error = err.message
})['finally'](function(){
//done.
});
});

Resources