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;
Related
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);
}
});
};
});
This is the service where im saving the data and returning the result
nurseService.js
(function () {
'use strict';
angular.module('app.services')
.factory('NurseService', NurseService);
NurseService.$inject = ['$http', '$q','Constants'];
function NurseService($http, $q, Constants){
var service = {
saveSample:saveSample
};
return service;
function saveSample(data) {
var deferred = $q.defer();
$http({method:"POST", data:data, url:Constants.API_URL_SAVE_SAMPLE_COLLECATION}).then(function(result){
return deferred.resolve(result.data);
});
};
return deferred.promise;
}
})();
This is the controller where im using the return value and based on the value returned im calling another http get method and printing it.
vm.saveSamples = function() {
var data = {
visitId: visitId,
orders: vm.gridTestApi.selection.getSelectedRows()
};
var url = Constants.API_URL_SAVE_SAMPLE_COLLECATION;
var barCodeResponse = null;
var sampleId = "";
var myDataPromise = NurseService.saveSample(data);
myDataPromise.then(function(result) {
console.log("data.name"+ JSON.stringify(result));
vm.printBarCode(result.sampleId);
// if(sampleId != ""){
printElement("printThisElement");
// }
});
//Barcode method this should call after saving the data and returned the sampleId
vm.printBarCode = function(sampleId) {
$http.get("master/barcode/"+sampleId).then(function (response) {
vm.barCodeImage = angular.copy(response.data.result);
});
}
But here before the saving print is calling. How can I hadle so that the first call should finish before the second http call to barcode and print it
//Print code
function printElement(elem) {
var printSection = document.getElementById('printSection');
// if there is no printing section, create one
if (!printSection) {
printSection = document.createElement('div');
printSection.id = 'printSection';
document.body.appendChild(printSection);
}
var elemToPrint = document.getElementById(elem);
// clones the element you want to print
var domClone = elemToPrint.cloneNode(true);
printSection.innerHTML = '';
printSection.appendChild(domClone);
window.print();
window.onafterprint = function () {
printSection.innerHTML = '';
}
};
You have to return the $http call in printBarCode and use a .then like so:
//Barcode method this should call after saving the data and returned the sampleId
vm.printBarCode = function(sampleId) {
return $http.get("master/barcode/"+sampleId).then(function (response) {
vm.barCodeImage = response.data.result;
});
}
myDataPromise.then(function(result) {
console.log("data.name"+ JSON.stringify(result));
return vm.printBarCode(result.sampleId)
}).then(
function() {
printElement("printThisElement");
},
function(error) {
// error handler
}
);
printElement will now wait for the printBarCode promise and .then to fulfil before executing.
You also don't have to use a $q.defer when doing a $http call, $http is already a promise so you can just return that like so:
function saveSample(data) {
return $http({method:"POST", data:data, url:Constants.API_URL_SAVE_SAMPLE_COLLECATION})
.then(
function(result) {
return result.data;
},
function(error) {
// don't forget to handle errors
}
);
}
First of all, $http internally implements promises you you dont have to explicitly create them.
Secondly, you should put all your http requests in the service/factory
The modified code looks like
angular.module('app.services')
.factory('NurseService', function($http){
var service = {
saveSample : function(data){
//first http implementation here
return $http.post(....);
}
getBarcode : function(sampleId){
//http implementation here for barcode
return $http.get(....);
}
}
return service;
});
and your controller can use the service like
angular.module('app.services')
.controller('saveSampleCtrl',function($scope,NurseService){
var postData = {
//your post data here
}
NurseService.saveSample(postData)
.then(function(data){
//read sampleId here from data
var sampleId = data.sampleId;
NurseService.getBarcode(sampleId)
.then(function(){
//your print statement here
});
});
}
there might be typos in the code but this is just a basic idea on how you could do that. Hope it helps
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
});
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.
});
});
I have downloaded HotTowel.Angular.Breeze from NuGet and set it up with a breeze api controller.
I have configured the datacontext.js to create a new manager and have created and exposed a function to create a new entity:
function datacontext(emManager) {
var manager = emManager.newManager();
var service = {
create: create,
save: save
};
return service;
function create(entityName) {
return manager.createEntity(entityName);
}
function save() {
return manager.saveChanges().to$q(saveSucceeded, saveFailed);
function saveSucceeded(result) {
// Save Successful
}
function saveFailed(error) {
// Save Failed
}
}
}
I then call this in my controller like so:
function getNewPerson() {
vm.person = datacontext.create(entityName);
return vm.person;
}
If I load this page directly in my browser, I get the following error:
Error: Unable to locate a 'Type' by the name: 'Person'.
Be sure to execute a query or call fetchMetadata first.
However, if I navigate to the page after executing a Query, it loads fine and I'm able to save the new entity etc..
So my question is, where would be a good place to call fetchMetadata? Would it be in the entityManagerFactory on the newManager method?
function newManager() {
var mgr = new breeze.EntityManager({
serviceName: serviceName,
metadataStore: metadataStore
});
return mgr;
}
Edit
I've tried as suggested by #Rob below, but I still get the same error:
function setupMetadata() {
var deferred = $q.defer();
metadataStore.fetchMetadata(serviceName).then(function () {
deferred.resolve();
}).fail(function (ex) {
deferred.reject(ex);
});
return deferred.promise;
}
Then I place setupMetadata(); inside app.run:
app.run(['$route', '$rootScope', '$q', 'entityManagerFactory', function ($route, $rootScope, $q, emFactory) {
// Include $route to kick start the router.
breeze.core.extendQ($rootScope, $q);
emFactory.setupMetadata();
}]);
At the top of your dataservice pass is breeze and create a new metadatastore
var servicePath = 'http://localhost/breeze/Blah';
var metadataStore = new breeze.MetadataStore({
//Configure Stuff
//namingConvention: breeze.NamingConvention.camelCase
});
Then fetch your metadata
$rootScope.initialized = false;
function setupMetadata() {
var deferred = $q.defer();
metadataStore.fetchMetadata(servicePath).then(function () {
$rootScope.initialized = trie;
deferred.resolve();
}).fail(function (exception) {
deferred.reject(exception);
});
return deferred.promise;
};
Controller
.controller('SomeCtrl', function($rootScope, $scope) {
$rootScope.$watch('initialized', function() {
//Make initial calls
});
}
I would expose this method so you can call it when you are bootstraping the app or authenticating to make sure the metadata store is loaded before you do anything else.
You should check first metadata exist then create new person, your create method look like this.
function create(entityName) {
var hasMetadata = checkMetadata(manager, serviceName);
if (hasMetadata) {
return manager.createEntity(entityName);
} else {
return getMetadata(manager, serviceName).then(createPerson);
}
}
function createPerson()
{
return manager.createEntity(entityName);
}
//Check wther Metadata is present in the given manager or not
var checkMetadata = function (manager, serviceName) {
try {
var result = false;
if (manager.metadataStore.hasMetadataFor(serviceName)) {
result = true;
}
return result;
} catch (e) {
throw e;
}
};
//Method to get Metadata from a given entityManager and serviceName
this.getMetadata = function (manager, serviceName) {
var store = manager.metadataStore;
return store.fetchMetadata(serviceName);
};
I think it will be help you.