I am trying to set a service and want the controllers in my app get the data from the service.
I have something like
angular.module('myApp').service('testService', ['Product',
function(Product) {
var products
//Product is a $resource object to send an http request
Product$.query({
id: 123
}, function(object) {
setProducts(object);
});
var setProducts = function(object) {
products = object;
}
var getProducts = function() {
return products;
}
return {
setProducts: setProducts,
getProducts: getProducts
};
}
]);
in my another controller
angular.module('myApp').controller('productController', ['$scope', 'testService',
function($scope, testService) {
//return undefined...
console.log(testService.getProducts())
}
]);
//testService.getProducts() returns undefined.
I think the reason is because I am making $http request and it's asynchronous so the testService has no idea what the product is when the app first loads. How do I fix this issue? Thanks so much!
I use a promise "q$" to deal with asynch calls:
angular.module('myApp').service('testService', ['Product',
function(Product, $q) {
var products
var setProducts = function(object) {
products = object;
}
var getProducts = function() {
var deferred = $q.defer();
//Product is a $resource object to send an http request
Product$.query({
id: 123
}, function(object) {
setProducts(object);
deferred.resolve(object);
});
return deferred.promise;
}
return {
setProducts: setProducts,
getProducts: getProducts
};
}
]);
angular.module('myApp').controller('productController', ['$scope', 'testService',
function($scope, testService) {
//use a promise
testService.getProducts().then(function(data){
console.log(data);
},
function (error) {
console.log(error);
})
}
]);
The promise has two call backs one for complete and one for error. You can deal with the errors in your view model as needed.
Related
I have a service that grabs JSON from a URL and I would like to alter that data but I cant seem to do it. Right now I change this in the controller but this seems messy with the scope not reaching places I would like.
//SERVICE
app.service("servers", function ($http, $q)
{
// Grab json
var deferred = $q.defer();
$http.get('http://www.mocky.io/v2/58bea87e260000c318f07bfd').then(function (data)
{
deferred.resolve(data);
});
this.getItems = function ()
{
return deferred.promise;
}
})
// CONTROLLER
.controller("AppCtrl", function ($scope, servers, geoIP) {
var promise = servers.getItems();
promise.then(function (data)
{
$scope.items = data.data.items;
});
$scope.getSelectedItem = function() {
return servers.selectedItem;
}
$scope.selectServer = function(item)
{
servers.selectedItem = item;
servers.selectedItem.refactored_match_state = lowerCamelCaseUnderscoreRefactor(servers.selectedItem.session.attributes.match_state);
}
//COMPONENT/TEMPLATES
//dbServerTable
<tr data-ng-repeat="item in items | filter:search | orderBy:'name'" data-ng-click="selectServer(item)">
<td>{{item.display_name}}</td>
</tr>
//dbServerInfoSidebar
<li>{{getSelectedItem().refactored_match_state}}</li>
Could anyone show me with code how to alter data in a service that can be used anywhere by any controller that can access that service
The way the service has been coded is an anti-pattern and should be avoided. Refer this link.
Change your service like below and make modifications to the data before you return the data within the .then function.
app.service("servers", function ($http)
{
this.getItems = function ()
{
return $http.get('http://www.mocky.io/v2/58bea87e260000c318f07bfd')
.then(function (data)
{
// **** Modify data here ****
return data;
});
}
})
You can use transformResponse property of $http
service;
You can modify your data before resolving the promise
deferred.resolve(data);.
Are you trying to do something like:
app.service("servers", function ($http, $q)
{
this.parseData = data => {
//modify data;
return data;
};
this._request = $http.get('http://www.mocky.io/v2/58bea87e260000c318f07bfd')
.then(this.parseData);
this.getItems = () => this._request;
});
You don't need to use deferred at all. It's unnecessary. $http returns a promise for you. If you want to alter any data, you just need to chain after the request and return the modified data in the chained method.
app.service('services',['$q','$http','$rootScope',function($q,$http,$rootScope){
var obj ={};
obj.getData =function(x){
var defer = $q.defer();
$http.get('http://www.mocky.io/v2/58bea87e260000c318f07bfd')
.then(function(response){
defer.resolve(response);
},function(error){
defer.reject(error);
});
return defer.promise;
}
return obj;
}])
app.controller('ctrl',function($scope,services){
$scope.getItems = function(){
services.getData()
.then(function(response){
$scope.Items=response.data;
},function(error){
console.log(error);
});
}
})
I created an angular factory for $http service. I am getting the response and able to use the same in the controller but the problem is, when i check the network tab in the browser, the http request is made twice
Factory:
app.factory('myService', function ($http, $q) {
var deferred = $q.defer();
var responseData = null;
var obj = {};
obj.getData = function(){
$http.get('test.json').success(function(response){
responseData = response;
deferred.resolve(responseData);
}).error(function(response){
deferred.reject(responseData);
});
return deferred.promise;
}
obj.myData = function(){
return responseData;
}
return obj;
});
Controller:
app.controller('myController', function($scope,myService){
myService.getData().then(function(){
$scope.myDetails = myService.myData();
});
});
what's wrong in my approach. Please provide me a solution
The way you are making your caching scenario is quite complicated and not really helpful. How do you know if data has already been loaded?
Maybe you can create a simple Caching Service to handle your caching at a single point (nr of code lines will go down).
angular.module("YourApp").factory("CachingService", [
"$q",
"$http",
function ($q, $http,) {
var cache = {};
return {
getFromCache: getFromCache
};
function getFromCache(url) {
var deferred = $q.defer();
if (cache[url]) {
deferred.resolve(cache[url]);
} else {
return $http.get(url).then(function (result) {
cache[url] = result;
return result;
});
}
return deferred.promise;
}
}
]);
And then, you simply call it inside your other service :
angular.module("YourApp").factory("myService", [
"CachingService",
function(CachingService){
return {
getData: getData
};
function getData(){
return CachingService.getFromCache("test.json");
}
}
]);
And then, inside your controller :
app.controller('myController', function($scope,myService){
myService.getData().then(function(result){
$scope.myDetails = result.Data;
});
});
you can return $http not deferred.promise
I'm trying to cache response from $http into an object for a session in angular, so once the initial call has been made, every other call to service.getCategories() (e.g), will get the data from the object rather than to the api.
The service is being resolved at the route, but there is authentication, which will redirect to another route - calling service.getCategories() again.
I'm attempting this by setting an init variable on call, then all other calls will direct to the populated object - but it seems to reset the service somehow, and the returned object gets populated twice, so there's double of everything. See below:
var $ = require('jquery');
module.exports = angular.module('app.common.services.category', [])
.factory('categoryService', ['$http', '$q', '$rootScope', function($http, $q, $rootScope) {
// API Parameters
var deferred = $q.defer();
// Services
var Categories = {
init: false,
categories: [],
index: 0,
// Retrieve all data on load.
// Loaded into an array for all other services
// to use after init.
getCategories: function(page) {
if(!Categories.init) {
$http.get('api/core/get_category_index')
.then(function(result) {
var data = result.data.categories;
$.each(data, function(i, category) {
category.index = Categories.index;
Categories.categories.push(category);
Categories.index++;
});
Categories.init = true;
return deferred.resolve(Categories.categories);
});
// Return promise once catgories is resolved
return deferred.promise;
} else {
return Categories.categories;
}
},
allCategories: function() {
return Categories.categories;
}
}
return Categories;
}]);
A problem with your approach is when the service function getCategories is called for the second time, the first time server request may not is resolved, causing a second call to the server. So you should move the init flag directly after the function call getCategories.
An other problem is that in your case you don't know whether the function will return a promise or an Array. I Suggest always returning an Array
module.exports = angular.module('app.common.services.category', [])
.factory('categoryService', ['$http', '$q', '$rootScope', function($http, $q, $rootScope) {
// API Parameters
var deferred;
// Services
var Categories = {
categories: [],
index: 0,
// Retrieve all data on load.
// Loaded into an array for all other services
// to use after init.
getCategories: function(page) {
if(!deferred) {
// replacement for intit flag
deferred = $q.defer();
$http.get('api/core/get_category_index')
.then(function(result) {
var data = result.data.categories;
$.each(data, function(i, category) {
category.index = Categories.index;
Categories.categories.push(category);
Categories.index++;
});
deferred.resolve(Categories.categories);
});
}
// always return a promise
return deferred.promise;
},
allCategories: function() {
return Categories.categories;
}
}
return Categories;
}]);
Maybe you can return the service itself with the promise. Then you could write everywhere something like:
myService.load().then(
function success(theService) {
theService.allCategories()
}
);
Now it doesn't matter anymore whether the service was loaded before or not
I am trying to use promise and service to set the data from http request.
I have something like this
angular.module('myApp').controller('productController', ['$scope', 'testService',
function($scope, testService) {
testService.getProducts().then(function(products){
console.log(products);
})
//getFirstProduct is trigger by ng-click user action.
$scope.getFirstProduct = function(){
var t = testService.getFirstProduct();
console.log(t);
}
}
]);
angular.module('myApp').service('testService', ['Product', '$q',
function(Product, $q) {
var products, firstProduct;
var getFirstProduct = function(){
return firstProduct;
}
var setFirstProduct = function(product) {
firstProduct = product;
}
var getProducts = function() {
var deferred = $q.defer();
//Product is a $resource object to send an http request
Product.query({
id: 123
}, function(result) {
setFirstProduct(result.first);
deferred.resolve(classes);
});
return deferred.promise;
}
return {
setFirstProduct: setFirstProduct,
getProducts: getProducts,
getFirstProduct: getFirstProduct
};
}
]);
I need to be able to get First product but I am not sure how to fix this. Can anyone help me about it? Thanks a lot
I see a number of errors in the code such as missing semicolons, mistyped variable/function names, and that setProducts was clobbering its variable.
Also, added $q, as mentioned by #manube
The following should work better:
angular.module('myApp').controller('productController', ['$scope', 'testService',
function($scope, testService) {
testService.getProducts().then(function(products){
console.log(products);
})
//getFirstProduct is trigger by ng-click user action.
$scope.getFirstProduct = function(){
var t = testService.getFirstProduct();
console.log(t);
}
}
]);
angular.module('myApp').service('testService', ['Product', '$q',
function(Product, $q) {
var products, firstProduct;
var getFirstProduct = function(){
return firstProduct;
}
var setFirstProduct = function(product) {
firstProduct = product;
}
var getProducts = function() {
var deferred = $q.defer();
//Product is a $resource object to send an http request
Product.query({
id: 123
}, function(result) {
setFirstProduct(result.first);
deferred.resolve(classes);
});
return deferred.promise;
}
return {
setFirstProduct: setFirstProduct,
getProducts: getProducts,
getFirstProduct: getFirstProduct
};
}
]);
I have a service which will make a call to the server and returns the data. I am binding service to a variable on scope.
Example:
Let the service be DataModelService
in the controller : $scope.data = DataModelService
in the view <div ng-repeat="value in data.persons">{{value.name}}</div>
My Code :
This is how my code looks like:
/**DataModelService**/
factory('DataModelService', [
'DataService',
function (DataService) {
var service;
service = {
changeState: function (params) {
DataService.changePersonState(params)
.then(function (response) {
service.loadData(response.data);
});
},
loadData: function (responseData) {
service.persons = responseData.persons;
}
}
return service;
}
]);
/**DataService**/
factory('DataService', ['$http',
function ($http) {
return {
changePersonState: function (params) {
return $http.post("url", params);
}
}
}
]);
/**DataController**/
.controller('DataController', ['DataModelService',
function (DataModelService) {
$scope.data = DataModelService;
}
]);
/view/
<div ng-repeat = "person in data.persons" >{{person.name}} </div>
On the view I am doing a ng-repeat on a key in data i.e. ng-repeat="value in data.persons"
and also I have an option to change the state of person to active or inactive, so whenver i make a change to the state of the person, a call is sent to the server and data is set into the Service and as it is binded to the view, it should automatically update the data. But whats happening in my case, ng-repeat is not removing old data and instead it is appending new data to the old data.
For me its not good approach to write promise callback (then) into service. Because in your case, DataModelService returns data with some delay but not promise. And we don't know when.
So the way to make it work to add basic $timeout and fetch data from service by using other method.
So my suggestion is Demo
and your fixed example: Demo2
If we will take your example, it should be like:
JS
var fessmodule = angular.module('myModule', ['ngResource']);
fessmodule.controller('fessCntrl', function ($scope, DataModelService, $timeout) {
$scope.alertSwap = function () {
DataModelService.changeState('ff');
$timeout(function(){
$scope.data = DataModelService.getResponse();
}, 10);
}
});
fessmodule.$inject = ['$scope', 'Data', '$timeout'];
/**DataModelService**/
fessmodule.factory('DataModelService', [ 'DataService',function (DataService) {
var value = [];
var service = {
changeState: function (params) {
DataService.changePersonState(params)
.then(function (response) {
value = response.persons;
});
},
getResponse : function(){
return value;
}
}
return service;
}
]);
/**DataService**/
fessmodule.factory('DataService', ['$q',function ($q) {
var data = { // dummy
persons: [{
name: "Bob"
}, {
name: "Mark"
}, {
name: "Kelly"
}]
};
var factory = {
changePersonState: function (selectedSubject) {
var deferred = $q.defer();
deferred.resolve(data);
return deferred.promise;
}
}
return factory;
} //function
]);