Get data from Factory - angularjs

What I am trying to achieve:
Pass the parameter specId from the controller to the factory
In the factory: perform a $http.get to get JSON data
Return the JSON data to the controller
Displaying the information by assigning it to the $scope.formData
I tried many different ways but it only returns undefined. Before I created the factory I performed the $http.get directly in the controller without any issues, but now I am trying to structure the app in a better way.
Factory:
app.factory("dataFactory", function($http) {
var factory = {};
factory.getSpec = function(specId) {
return $http.get('getSpec.aspx?specId=' + specId)
};
return factory;
});
Controller
app.controller('EditSpecController', function ($scope, $stateParams, $http, dataFactory) {
$scope.specId = $stateParams.specId;
$scope.formData = [];
if($scope.specId) { //If EDIT MODE
dataFactory.getSpec($scope.specId).then(function(response) {
$scope.formData = response.data;
$scope.use_unit = response.data.use_unit;
});
}

As you noticed $http returns promise already, so you should do something more like this
factory.getSpec = function(specId) {
return $http.get('getSpec.aspx' + specId)
};
and then in controller
dataFactory.getSpec().then(function(response) {
$scope.formData = response.data;
});

Related

AngularJS GET petition in a Factory

I've seen several posts about this, but I cannot adapt them to what I want to do.
I have a GET petition to a server to get a JSON response, and works like a charm.
.controller('ListController', ['$scope', '$http', function($scope, $http){
$http.get('js/data.json').success(function(data){
$scope.elements = data;
});
}])
Now I can print in the view with ng-repeat, all the elements within $scope.elements.
But now I need to do two different GETs, depending if has Internet, check online, if has not, check local DB.
I've created a factory like follows:
.factory('AttemptFactory', function($http){
var self = this;
this.elements = [];
this.returnFunction = function() {
$http.get('js/data.json').success(function(data){
self.elements = data;
});
}
}
I've firstly tried to fill this.elements, and then in the first controller access to it with $scope.newList = = AttemptFactory.elements.
Also tried make a return self.elements, and receive it with a AttemptFactory = AttemptFactory.returnFunction ();
I added the dependency injection of AttemptFactory to the .controller().
How can I archieve it??
if (online)
data = Get(from_server);
else
data = Get(from_local_DB);
return data;
Something like this. Thanks a lot.
I think you can try separating your concerns a bit. I'd recommend creating two factories: one that interacts with your server and one that interacts with the local db. Each could have a method that fetches data from the respective data store and returns a Promise for that data.
Then in your controller, you can choose which function to call based on the connection status. The function calls will both return a Promise that you can .then off of to get the data for your view. For example:
.factory('ServerFactory', function($http){
var serverFactory = {};
serverFactory.fetchJson = function() {
// note that we're returning the promise that $http get returns that will resolve with our data
return $http.get('js/data.json').success(function(res){
return res.data;
});
}
return serverFactory;
})
.factory('LocalDbFactory', function() {
var localDbFactory = {};
localDbFactory.fetchJson = function() {
// Return promise for JSON data here
}
return localDbFactory;
})
.controller('ListController', ['$scope', '$http', 'ServerFactory', 'LocalDbFactory', function($scope, ServerFactory, LocalDbFactory){
var result;
if (/* test if online */) {
result = ServerFactory.fetchJson();
} else {
result = LocalDbFactory.fetchJson();
}
result.then(function(data) {
$scope.elements = data;
})
}])

Send path from controller to a service which will return a promise back to controller

I have a service which get data from a file(path is given by the controller) and return a promise - then another service that create a object with properties using the returned data from the last service.
My problems are:
The getDataService runs before controller so it has no path from which to fetch data => nothing in return(an error)
Provider 'GetDataService' must return a value from $get factory method.
I need to keep this structure because I'll have more controllers with different paths to give
I'm also opened to other solutions but I need to make sure that datas are loaded before the template get populated. I've tried to call SetProperties service first with getData service into it - but still getData.js is executed first
getdata Service
angular.module('myApp').factory('GetDataService',['$http', function($http) {
var getData = function(path){
return $http.get(path).then(function(result) {
return result.data;
});
};
}]);
setProperties service
angular.module('myApp').service('PageProperties',['$http', function($http) {
this.setProps = function(page, data) {
some code here
var properties = {
isCenterActive : isActive_val,
//header elements
titleClass : page,
title : data.titles[page],
//footer elements
leftLink : leftLink_val,
leftFooterClass: leftLink_val,
leftTitle: data.titles[leftLink_val],
centerLink : centerLink_val,
centerFooterClass: data.titles[centerLink_val],
centerTitle : centerTitle_val,
rightLink : rightLink_val,
rightFooterClass: rightLink_val ,
rightTitle : data.titles[rightLink_val],
}
return properties;
}
}]);
controller
angular.module('myApp', [])
.controller('meniuController', ['$http', '$stateParams', '$scope', 'GetDataService', 'PageProperties',
function($http, $stateParams, $scope, GetDataService, PageProperties){
var page = "meniu";
$scope.language = $stateParams.lang;
var path = '_global/views/services/json/' + $stateParams.lang + '_data.json';
/*PageProperties.setProps(page, path).then(function(data){
//some code here
});*/
GetDataService.getData(path).then(function(data){
$scope.props = PageProperties.setProps(page, data);
}).catch(function(){
$scope.error = 'Unable to get data';
});
}])
Thanks in advance!!
The error says your GetDataService provider (defined as factory) doesn't return anything
angular.module('myApp').factory('GetDataService',['$http', function($http) {
var getData = function(path){
return $http.get(path).then(function(result) {
return result.data;
});
};
// you need to actually return something
return { getData: getData };
}]);
Then you can make your PageProperties use GetDataService
angular
.module('myApp')
.service('PageProperties',['GetDataService', function(GetDataService) {
this.getProperties = function(path) {
return GetDataService.getData(path).then(/*transform here*/)
}

Factorising AngularJS - How to properly use a factory to request and update data represent data

I have been struggling to figure out the 'angular' way of working for using factories to represent ajax data (I do a lot of sharing between controllers using config). I have done some extensive research and all of the answers seem to fall in to two categories:
Use the factory to represent the data, then in the controller fetch the data and update the factory:
{not intended to be actual runable angular}
var app = angular.module('main', [])
app.factory('data', function($http){
var data = []
return data
})
app.controller('cntrl', [$scope, $http, data]){
$scope.data = data
$http.get('/data').success(
function(idata){
$scope.data = idata
)
}
Use the factory to represent a http request with a promise and then assign that data to the $scope
var app = angular.module('main', [])
app.factory('data', function ($http, $q){
var factory = {};
factory.getdata = function(){
var defer = $q.defer();
$http.get('/data').success(function(idata) {
defer.resolve(idata);
})
return defer.promise;
}
return factory;
});
app.controller('cntrl', [$scope, data]){
$scope.data = []
data.getdata().then(function(idata) { $scope.data = idata });
}
I have several issues with this, in the first category the factory looks mega lame and like its not adding much value and each controller needs to have the code to manipulate the factory. With the second controller, the factory is very complicated and doesn't actually store the data (for sharing between controllers), just represents a long winded way to write a http.get
My question is this:
How can I use a factory to represent my data and manage the manipulation of that data in a clean way? What I would like to do is something like this:
var app = angular.module('main',[])
app.factory('data', function($http){
var factory = {}
factory.data = []
factory.initialise = function(){
$http.get('/data').success(
function(data){
factory.data = data
}
)
}
return factory
})
app.controller('cntrlA', [$scope, data]) {
$scope.data = data.data
$data.initialise()
}
app.controller('cntrlB', [$scope, data]) {
$scope.data = data.data
}
I am working on a project with a similar issue. What I ended up doing is using the service as a repository of the data. Instead of the initialize function getting fresh data each time, it is getting fresh data only if the data repository is empty or a getFresh flag is tripped (manual override). This way each controller can call the same function to get the data and manipulate it as the controller needs without tampering with the original data.
var app = angular.module('main',[])
app.factory('dataService', function($http, $q){
var factory = {}
factory.data = []
factory.getFresh = false
factory.initialise = function(){
var deferred = $q.defer()
if(factory.data.length===0 || factory.getFresh) {
$http.get('/data').success(function(data){
factory.data = data
deferred.resolve(data)
})
}
else{
deferred.resolve(factory.data)
}
return deferred.promise
}
return factory
})
app.controller('cntrlA', [$scope, dataService]) {
$scope.data = []
dataService.initialise().then(function(data){
$scope.data = data
}
}
app.controller('cntrlB', [$scope, dataService]) {
$scope.data = []
dataService.initialise().then(function(data){
$scope.data = data
}
}

Problems using $http inside a Service

I have a basic data Service which will be used across Controllers. But I'm having an issue grabbing some data that's been added via $http.
Service:
angular.module('core').service('FormService', ['$http', function($http) {
var _this = this;
_this.dropdownData = {
contactTimes: ['Anytime','Morning','Afternoon','Evening'],
industries: {},
};
$http.get('/json').success(function(resp){
_this.dropdownData.industries = resp.industries;
});
}]);
Controller:
angular.module('core').controller('SignupController', ['$scope', '$http', '$state', 'FormService', function($scope, $http, $state, FormService) {
console.log(FormService.dropdownData); // Shows full object incl industries
console.log(FormService.dropdownData.industries); // empty object {}
}]);
How do I get FormService.dropdownData.industries in my controller?
Create a service like below
appService.factory('Service', function ($http) {
return {
getIndustries: function () {
return $http.get('/json').then(function (response) {
return response.data;
});
}
}
});
Call in controller
appCtrl.controller('personalMsgCtrl', ['$scope', 'Service', function ($scope, Service) {
$scope.Industries = Service.getIndustries();
}]);
Hope this will help
Add a method to your service and use $Http.get inside that like below
_this.getindustries = function (callback) {
return $http.get('/json').success(function(resp){
_this.dropdownData.industries = resp.industries;
callback(_this.dropdownData)
});
};
In your controller need to access it like below.
angular.module('core').controller('myController', ['$scope', 'FormService', function ($scope, FormService) {
FormService.getDropdownData(function (dropdownData) {
console.log(dropdownData); // Shows full object incl industries
console.log(dropdownData.industries); // object {}
});
} ]);
Given that your console log shows the correct object, that shows your service is functioning properly. Only one small mistake you have made here. You need to access the data attributes in your return promise.
angular.module('core').service('FormService', ['$http', function($http) {
var _this = this;
_this.dropdownData = {
contactTimes: ['Anytime','Morning','Afternoon','Evening'],
industries: {},
};
$http.get('/json').success(function(resp){
//note that this is resp.data.industries, NOT resp.industries
_this.dropdownData.industries = resp.data.industries;
});
}]);
Assuming that you're data is indeed existing and there are no problems with the server, there are quite a few possible solutions
Returning a promise
angular.module('core').service('FormService', ['$http', function($http) {
var _this = this;
_this.dropdownData = {
contactTimes: ['Anytime','Morning','Afternoon','Evening'],
industries: {},
};
_this.dropdownData.industries = $http.get('/json');
}]);
//Controller
FormService.industries
.then(function(res){
$scope.industries = res.industries
});
Resolving with routeProvider / ui-route
See: $http request before AngularJS app initialises?
You could also write a function to initialize the service when the application starts running. At the end of the day, it is about waiting for the data to be loaded by using a promise. If you never heard about promises before, inform yourself first.
The industries object will be populated at a later point in time when the $http call returns. In the meantime you can still bind to the reference in your view because you've preserved the reference using angular.copy. When the $http call returns, the view will automatically be updated.
It is also a good idea to allow users of your service to handle the event when the $http call returns. You can do this by saving the $promise object as a property of industries:
angular.module('core').service('FormService', ['$http', function($http) {
var _this = this;
_this.dropdownData = {
contactTimes: ['Anytime','Morning','Afternoon','Evening'],
industries: {},
};
_this.dropdownData.industries.$promise = $http.get('/json').then(function(resp){
// when the ansyc call returns, populate the object,
// but preserve the reference
angular.copy( resp.data.industries, _this.dropdownData.industries);
return _this.dropdownData.industries;
});
}]);
Controller
app.controller('ctrl', function($scope, FormService){
// you can bind this to the view, even though the $http call has not returned yet
// the view will update automatically since the reference was preserved
$scope.dropdownData = FormService.dropdownData;
// alternatively, you can hook into the $http call back through the $promise
FormService.dropdownData.industries.$promise.success(function(industries) {
console.log(industries);
});
});

Cache Data with $resource promises pattern

Assuming my service is returning a promise from a $resource get, I'm wondering if this is the proper way to cache data. In this example, after hitting the back arrow and returning to the search results, I don't want to query the webserver again since I already have them. Is this the proper pattern to handle this situation? The example below is querying the Flixter (Rotten Tomatoes) Api.
Boilded down code:
Controller:
function SearchCtrl($scope, $route, $routeParams, $location, DataService) {
DataService.search($routeParams.q).then(function(data){
$scope.movies = data.movies;
});
}
Service:
angular.module('myApp.services', []).
factory('DataService', ['$q', '$rootScope', 'JsonService', function ($q, $rootScope, JsonService) {
var movie = {};
var searchResults = {};
var searchq = '';
var service = {
search: function(q) {
var d = $q.defer();
// checking search query, if is the same as the last one,
//resolve the results since we already have them and don't call service
// IS THIS THE CORRECT PATTERN
if (q==searchq) {
d.resolve(searchResults);
} else {
// returns a $resource with defined getdata
JsonService.search.movieSearch(q, 20, 1).getdata(function(data){
searchResults = data;
searchq = q;
d.resolve(searchResults);
});
}
return d.promise;
},
getSearchResults: function() {
return searchResults;
}
};
return service;
}]);
I can't provide a working example as it would expose my API key.
I've faked out the actual ajax request but I think the general idea should apply, you can see the full demo here
Here is the controller, it just executes the search and then sets the results:
myApp.controller('MyCtrl', function($scope, DataService) {
$scope.search = function(){
DataService
.search($scope.q)
.then(function(response){
$scope.fromCache = response.fromCache;
$scope.results = response.results;
});
};
});
In the DataService I am just saving results into an object keyed off the query. It is simplistic but hopefully will get you started. You could save it in html5 storage or something if you want something like that.
You will need to put in your actual ajax call here, but the principle remains.
myApp.factory('DataService', function($q){
var resultsCache = {};
return {
search: function(query){
var deferred = $q.defer();
if (resultsCache[query]) {
resultsCache[query].fromCache = true;
}
else {
resultsCache[query] = {results: [{name: 'result one'}, {name: 'result two'}]};
}
deferred.resolve(resultsCache[query]);
return deferred.promise;
}
};
});
Hope that helps

Resources