While the question is more I suppose javascript type question more so than an Angular question, I was creating a service in which I would call it like this
// controller injects activityApi , then service function call is made
var activities = activityApi.getActivityById(1122);
I get an error if I do not put "this" keyword in front of function name
1. Why do I need "this."
2. What other alternatives? ( I am trying to get into the habit of not doing old school javascript with function blah() { ... }
this.getActivityById = function(id) {
$http.get('/Umbraco/Api/ActivityApi/GetActivity').
success(function (data, status, headers, config) {
console.log(data);
return data;
}).
error(function (data, status, headers, config) {
// log error
});
};
If you are writing a factory make use of an object inside the factory ,and put all your calls into it.like
(function() {
'use strict';
angular
.module('app')
.factory('activityApi', activityApi);
activityApi.$inject = ['$http'];
function activityApi($http) {
var service = {
getActivityById : getActivityById ,
};
return service;
function getActivityById (id) {
$http.get('/Umbraco/Api/ActivityApi/GetActivity').
success(function (data, status, headers, config) {
console.log(data);
return data;
}).
error(function (data, status, headers, config) {
// log error
});
};
};
})();
here you are returning factory object in which calling function is available.
Here you can call the factory function as
var activities = activityApi.getActivityById(1122);
Regarding angular's services and factories. Both .service and .factory return functions. However, angular's $injector calls the function returned by .service with new keyword (using $instantiate method), whereas it calls .factory function as is using invoke method. Since it uses new keyword for service functions, you have to have this keyword inside your function to reference the object, created by new and returned from the function.
As #NikhilVM shows, you can use factory instead of service if you want to avoid using this. Both service and factory return values are cached, so if the factory returns an object it becomes global. So in this sense it's no different from service using this to add methods. Using factory has the advantage in that you can return a function constructor to be later called to instantiate multiple class instances, while you can't do that with .service.
Related
I'm fairly new to AngularJS and have just begun to grasp many of the concepts I especially like the MVC design pattern. But I am having a difficult time implementing the Service layer in my application.
What I am finding is that after my Controller calls a method within the service, it continues with code execution before the service returns the data; so that by the time the service does return the data, it isn't of any use to the controller.
To give a better example of what I'm saying, here is the code:
var InsightApp = angular.module('InsightApp', ['chart.js']);
// Module declaration with factory containing the service
InsightApp.factory("DataService", function ($http) {
return {
GetChartSelections: function () {
return $http.get('Home/GetSalesData')
.then(function (result) {
return result.data;
});
}
};
});
InsightApp.controller("ChartSelectionController", GetAvailableCharts);
// 2nd Controller calls the Service
InsightApp.controller("DataController", function($scope, $http, DataService){
var response = DataService.GetChartSelections();
// This method is executed before the service returns the data
function workWithData(response){
// Do Something with Data
}
}
All the examples I've found seem to be constructed as I have here or some slight variation; so I am not certain what I should be doing to ensure asynchronous execution.
In the Javascript world, I'd move the service to the inside of the Controller to make certain it executes async; but I don't how to make that happen in Angular. Also, it would be counter intuitive against the angular injection to do that anyway.
So what is the proper way to do this?
http return a promise not the data, so in your factory your returning the $http promise and can use it like a promise with then, catch, finally method.
see: http://blog.ninja-squad.com/2015/05/28/angularjs-promises/
InsightApp.controller("DataController", function($scope, $http, DataService){
var response = DataService.GetChartSelections()
.then(function(res) {
// execute when you have the data
})
.catch(function(err) {
// execute if you have an error in your http call
});
EDIT pass params to model service:
InsightApp.factory("DataService", function ($http) {
return {
GetChartSelections: function (yourParameter) {
console.log(yourParameter);
return $http.get('Home/GetSalesData')
.then(function (result) {
return result.data;
});
}
};
});
and then :
InsightApp.controller("DataController", function($scope, $http, DataService){
var response = DataService.GetChartSelections('only pie one')
.then(function(res) {
// execute when you have the data
})
.catch(function(err) {
// execute if you have an error in your http call
});
You should proceed like this :
DataService.GetChartSelections().then(function (data) {
workWithData(data);
}
Actually $http.get returns a Promise. You can call the method then to handle the success or failure of your Promise
Should it not be like this, when your $http returns a promise or you pass a callback.
With passing callback as a param.
InsightApp.factory("DataService", function ($http) {
return {
GetChartSelections: function (workWithData) {
return $http.get('Home/GetSalesData')
.then(function (result) {
workWithData(result.data);
});
}
};
});
Controller code:
InsightApp.controller("DataController", function($scope, $http, DataService){
var response = DataService.GetChartSelections(workWithData);
// This method is executed before the service returns the data
function workWithData(response){
// Do Something with Data
}
}
Or use then or success:
var response = DataService.GetChartSelections().then(function(res){
//you have your response here... which you can pass to workWithData
});
Return the promise to the controller, dont resolve it in the factory
var InsightApp = angular.module('InsightApp', ['chart.js']);
// Module declaration with factory containing the service
InsightApp.factory("DataService", function ($http) {
return {
GetChartSelections: function () {
return $http.get('Home/GetSalesData');
}
};
});
In the controller,
var successCallBk =function (response){
// Do Something with Data
};
var errorCallBK =function (response){
// Error Module
};
var response = DataService.GetChartSelections().then(successCallBk,errorCallBK);
I'm using the $cacheFactory to save some data in my cache and everything was working very good until today that I've decided to separate my cacheFactory into a single class called MyFactory.js. Now I'm getting the error:
TypeError: tableCacheFactory is not a function
Because it's taking the injection as a simple method or something, does anybody know if I'm missing something here?
main.js
angular.module('main-component',
['my-component',
'my-cache-factory'
]);
MyFactory.js
angular.module('my-cache-factory', [])
.factory('tableCacheFactory', [
'$cacheFactory', function ($cacheFactory) {
return $cacheFactory('my-cache');
}
]);
MyService.js
angular.module('my-component', [])
.factory('my-component-service',
['$rootScope',
'$http',
'$q',
'tableCacheFactory',
function ($rootScope, $http, $q, tableCacheFactory) {
function getMyData (prodNro) {
var deferred = $q.defer();
var promise = deferred.promise;
var dataCache = tableCacheFactory.get('tablecache');
if (!dataCache) {
dataCache = tableCacheFactory('tablecache'); // TypeError: tableCacheFactory is not a function
}
var summaryFromCache = dataCache.get('tablecache' + prodNro);
if (summaryFromCache) {
deferred.resolve(summaryFromCache);
} else {
$http({
method: ...
data : ...
url: ...
}).success( function (data, status, headers, config) {
var objectResult = {
"data": data,
"status": status,
"headers": headers,
"config": config
}
if (data.response) {
// Save to cache
dataCache.put('tablecache'+prodNro, objectResult);
}
deferred.resolve(objectResult);
}).error(function (data, status, headers, config) {
...
});
}
return promise;
}
You seem to have some misconception about how the $cacheFactory works.
In var dataCache = tableCacheFactory.get('tablecache'); you are using it like it was a initialized Cache object containing another Cache object.
On the other hand dataCache = tableCacheFactory('tablecache'); uses it like it was the $cacheFactory itself.
And both of them try to access record 'tablecache' in something that I think should already be the tableCache itself.
The error is exactly what it says it is. As per the docs, calling $cacheFactory('my-cache'); does not return a function to create more caches.
It returns a $cacheFactory.Cache object which has methods like put(key, value) and get(key). Use those instead.
I'd change the whole structure of the caching (note that the name of the factory is changed):
.factory('tableCache', [
'$cacheFactory', function ($cacheFactory) {
//Return what the name of the factory implies
return $cacheFactory('tablecache');
}
]);
And then use that without needing to do more weird 'tablecache' namespacing
function getMyData (prodNro) {
...
var summaryFromCache = tableCache.get(prodNro);
...
tableCache.put(prodNro, objectResult);
}
The tableCacheFactory was wrapped inside my-cache-factory module. So you need to inject the module first into your my-component module before using it. So it should be like this:
angular.module('my-component', ['my-cache-factory'])
you defined your cache factory in the module my-cache-factory, but then never injected that module to your main component-service module. Do angular.module('my-component', ['my-cache-factory']) instead.
Here is my controller and service:
var app = angular.module('myApp', ['ui.bootstrap']);
app.service("BrandService", ['$http', function($http){
this.reloadlist = function(){
var list;
$http.get('/admin.brands/getJSONDataOfSearch').
success(function(data, status, headers, config) {
list = data;
}).
error(function(data, status, headers, config) {
});
return list;
};
}]);
app.controller('BrandsCtrl', ['$scope','$http','$controller','BrandService', function($scope, $http, $controller, BrandService) {
$scope.brands = BrandService.reloadlist();
angular.extend(this, $controller("BrandCtrl", {$scope: $scope}));
}]);
I searched for this issue and tried answers of questions but I couldn't get solution. I am new at angular so can you explain with details; why I couldn't get the data from service to controller this way ?
The return used for data is for the callback of your function.
You must use the promise returned by $http like this.
In your service return the promise :
return $http.get('/admin.brands/getJSONDataOfSearch').
success(function(data, status, headers, config) {
return data;
}).
error(function(data, status, headers, config) {
});
Use then() on the promise in your controller :
BrandService.reloadlist()
.then(function (data){
$scope.brands = data;
});
It's not angular, it's the Javascript. The function you put in this.reloadlist does not return any value. It has no return at all, so the value returned will be undefined. The success handler does return something, but it will be run long after reloadlist finished working.
Besides what #fdreger already pointed out (missing return value), $http.get(...) is an async method. The return value is a promise not the actual value.
In order to access the value you need to return it from reloadlist like this:
this.reloadList = function() {
return $http.get('/admin.brands/getJSONDataOfSearch');
// you need to handle the promise in here. You could add a error handling here later by catching stuff...
}
and in the controller you can add it to the $scope like this:
BrandService
.reloadlist()
.then(function(res) {
$scope.brands = res.data;
});
The callback passed to then() is called as soon as the HTTP request has successfully completed, this makes the call asynchronous.
Besides the angular documentation for promises the article on MDN is a good read too.
I'm pretty new to angularjs and I need to do the following:
I have few partial views, each containing simple configuration form, which needs to be fetched/PUT to server.
Each view has a controller, each controller has a corresponding service which is responsible for doing GET/PUT request to a given backend endpoint.
Services differs right now only in endpoint url.
The question is:
how would you avoid the following?
var providerConfigService = function ($http) {
this.fetchConfig = function (endpointUrl, success, failure) {
$http.get(endpointUrl)
.success(function (data) {
success(data)
})
.error(function (data, status) {
failure(status)
});
};
this.updateConfig = function (endpointUrl, config, success, failure) {
$http.put(endpointUrl, config)
.success(function (data, status) {
success()
})
.error(function (data, status) {
failure(status)
});
}
};
var facebookConfigService = function (providerConfigService) {
var facebookEndpoint = "";
this.fetchConfig = function (success, failure) {
providerConfigService.fetchConfig(facebookEndpoint, success, failure)
};
this.updateConfig = function (config, success, failure) {
providerConfigService.fetchConfig(facebookEndpoint, config, success, failure)
};
};
// POTENTIALLY DUPLICATED CODE FOR OTHER VIEWS
// SERVICE REGISTERING
I'm more like a Java guy, so I would do something like this in Java and Spring world:
Provide endpointUrl as a constructor parameter, then either create a Factory class or just declare preconfigured beans.
I'm looking for a similar solution in angular world. Should I use angular's factories/providers? If so, how? It's probably straightfroward, but I'm quite confused with angular's services/controllers/factories/providers.
Typically in the angular world you would try and use ng-resource. If your API's are RESTful and basic I would recommend ng-resource. If not, then it's ok what you've done, but typically those gets and puts would just be within the provide service itself. You're only DRY-ing up the success and failure but the reality is that those would probably be different anyway.
You can use a Factory (read more) and configure it in your controller. You can also improve the flow by using promises instead of passing callbacks.
angular.module('yourapp')
.factory('configFactory', function ($http) {
return {
endpointURL: "", // this is what you modify
fetchConfig: function () {
$http.get(this.endpointURL)
},
updateConfig: function (config) {
$http.put(this.endpointURL, config)
}
};
})
//and your controller
.controller('someCtrl', function($scope, configFactory){
configFactory.endpointURL = 'http://customurl.com';
// configFactory will use the endpointURL defined by this controller
$scope.fetchConfig = function(){
configFactory.fetchConfig().then(function(){}).catch(function(){});
}
})
// and another
.controller('someCtrl', function(configFactory){
configFactory.endpointURL = 'http://anotherurl.com
})
Ok, so I ended up with the following:
My ng-resources:
var ConfigurationResource = function ($resource, endpointUrl) {
var allowedMethods = {
'get': {
method: 'GET'
},
'update': {
method: 'PUT'
}
};
return $resource(endpointUrl, {}, allowedMethods);
};
var FacebookProperties = function ($resource) {
return ConfigurationResource($resource, 'my.facebook.endpoint.url');
};
And there was a problem with duplicating code in my controller, so it now looks like the following:
var PropertiesController = function (scope, propertiesResource) {
this.fetchProperties = function () {
propertiesResource.get(function (fetchedProperties) {
scope.properties = fetchedProperties;
});
};
this.updateProperties = function () {
propertiesResource.update(scope.properties)
};
scope.properties = {};
scope.fetchProperties = this.fetchProperties;
scope.updateProperties = this.updateProperties;
scope.fetchProperties();
};
var facebookConfigController = function ($scope, FacebookProperties) {
new PropertiesController($scope, FacebookProperties);
};
Maybe that's not the proper way to do this, but at least it works and there's almost no boilerplate.
My basic premise is I want to call back to the server to get the logged in user in case someone comes to the site and is still logged in. On the page I want to call this method. Since I am passing the user service to all my controllers I don't know which controller will be in use since I won't know what page they're landing on.
I have the following User Service
app.factory('userService', function ($window) {
var root = {};
root.get_current_user = function(http){
var config = {
params: {}
};
http.post("/api/user/show", null, config)
.success(function(data, status, headers, config) {
if(data.success == true) {
user = data.user;
show_authenticated();
}
});
};
return root;
});
Here is an empty controller I'm trying to inject the service into
app.controller('myResourcesController', function($scope, $http, userService) {
});
So on the top of my index file I want to have something along the lines of
controller.get_current_user();
This will be called from all the pages though so I'm not sure the syntax here. All examples I found related to calling a specific controller, and usually from within another controller. Perhaps this needs to go into my angularjs somewhere and not simply within a script tag on my index page.
You could run factory initialization in run method of your angular application.
https://docs.angularjs.org/guide/module#module-loading-dependencies
E.g.
app.run(['userService', function(userService) {
userService.get_current_user();
}]);
And userService factory should store authenticated user object internaly.
...
if (data.success == true) {
root.user = data.user;
}
...
Then you will be able to use your factory in any controller
app.controller('myController', ['userService', function(userService) {
//alert(userService.user);
}]);
You need to inject $http through the factory constructor function, for firsts
app.factory('userService', function ($window, $http) {
var root = {};
root.get_current_user = function(){
var config = {
params: {}
};
$http.post("/api/user/show", null, config)
.success(function(data, status, headers, config) {
if(data.success == true) {
user = data.user;
show_authenticated();
}
});
};
return root;
});
in your controller you can say
$scope.get_current_user = UserService.get_current_user();
ng attributes in your html if needed. besides this, i am not sure what you need.