i am little bit familiar with angular. still on learning process. working with ng version 1.4.8. so i like to know how could i define constructor function in service and factory.
here is one sample service .now tell me how to define constructor in service or factory ?
angular.module('myApp').service('helloService',function($timeout){
this.sayHello=function(name){
$timeout(function(){
alert('Hello '+name);
},2000);
}
});
angular.module('myApp').controller('TestController',
function(helloService){
helloService.sayHello('AngularJS'); // Alerts Hello AngularJS
});
The function you pass to .service gets called with new, thus it is already basically a constructor. It is a "constructor function" and it implicitly returns an object, which is the singleton:
angular.module('myApp').service('helloService',function($timeout){
// This constructor function implicitly returns an object. It is called
// only once by Angular to create a singleton.
this.sayHello = function(name) {
// etc
}
});
Just to illustrate, if you passed an ES6 class to .service (which does have a constructor) instead of a constructor function, that constructor would be called when the singleton is created:
class HelloService {
constructor($timeout) {
// Called once when the singleton is created
}
sayHello(name) {
// etc
}
}
angular.module('myApp').service('helloService', HelloService);
Using .factory is similar, but it doesn't get called with new. So the function you use in this case has to return a singleton object explicitly:
angular.module('myApp').factory('helloService',function($timeout){
// This factory function explicitly returns an object. It is called
// only once by Angular to create a singleton.
return {
sayHello: function(name) {
// etc
}
};
});
Edit: As mentioned by #Vladimir Zdenek, these "constructors" cannot be used to configure the singleton externally. However, I interpreted the question to mean "Where can I put code that runs when the singleton is created?". Singletons may need to initialize data, so that initialization can go in the "constructor".
There is (probably in most cases) no need for a constructor when it comes to singletons. To require such a thing might be just pointing to a bad architectural design of your application.
That said, you can make a global configuration available for your service/factory by using a provider. You can find more on providers in the Official Documentation.
If you do not need a singleton and you wish to create a reusable piece of code, you can use something (in JavaScript) known as factory functions. You can see an example of such function below.
function MyFactory(myParams) {
const Factory = {
// Properties
myProperty: myParams.myProperty,
// Methods
getMyProperty: getMyProperty
};
return Factory;
function getMyProperty() {
return Factory.myProperty;
}
}
// usage
const myObj = MyFactory({ myProperty: 'Hello' });
I have tried every example I have found, and am still unable to call an NG1 Service from my Angular Component.
Is this even possible in the way I am describing below?
Service is defined like so:
'use strict';
angular.module('app.shared.services').factory('ng1TestService', ng1TestService);
function ng1TestService() {
var getGreeting = function () {
return "HELLO DOM! GOOD JOB!";
}
return {
getGreeting: getGreeting
}
}
I am calling upgrade on this service in my component:
upgradeAdapter.upgradeNg1Provider('ng1TestService')
Component Constructor:
constructor(#Inject('ng1TestService') ng1TestService : any) {
var hello = ng1TestService.getGreeting();
}
PROBLEMS:
I get an error saying that there is no Provider for ng1Test Service
I cannot call the NG1 Service
I had to add the "any" cast to resolve an error
It is not possible for me to add this Service to my "Providers" list since it is not in TS format
Any input would be appreciated.
Thanks!
Here's the piece of code I'm writing in angularjs 1.5.5:
export default angular.module(
'myApp', [])
.provider('finder', function finderProvider() {
this.$get = $get;
function $get() {
console.log('GET');
}
this.callMe = () => {
console.log('CALLED');
};
})
.config(['finderProvider', function(finderProvider) {
//finderProvider.$get();
finderProvider.callMe();
}])
.name;
I'm trying to inject a service into this provider later. I read on documentation that I can only inject a service inside a provider using $get() method. However, I cannot call $get() automatically. The only way I can get this working is to call $get() manually in the section where I commented the call. But it doesn't seem right to me to call $get manually.
In this example only 'CALLED' is getting logged. Any idea what I'm missing here?
EDIT. I tried to use another syntax using the arrow function, but got the same result:
.provider('finder', () => {
return {
$get: () => {
console.log('GET');
},
callMe: () => {
console.log('CALLED');
}
};
})
Thanks in advance.
Angular services are lazily instantiated.
Service provider constructor function is called when it is injected during config phase (config block or other provider).
$get is called when service instance is injected after config phase.
In the example above finder service isn't injected anywhere, service instance isn't created and $get function isn't called.
I have some doubts about providers.
Can someone explain me why I cannot access from the controller the "setText" provider function?
I can only access functions inside the $get block.
var myMod = angular.module('myApp', []);
myMod.controller("mainCtrl", [ "$scope","greeting", function($scope, greeting){
greetingProvider.setText("Hi, ");
}]);
myMod.provider('greeting', function() {
var text = 'Hello, ';
this.setText = function(value) {
text = value;
};
this.$get = function() {
return function(name) {
console.log(text + name);
};
};
});
myMod.config(function(greetingProvider) {
greetingProvider.setText("Howdy there, ");
});
myMod.run(function(greeting) {
greeting('Ford Prefect');
});
Thanks
The setText function is only exposed on your app.config. The only thing you'll have when you access the provider within your controller is what you've included inside the $get function.
For a more detailed answer, check this article:
http://tylermcginnis.com/angularjs-factory-vs-service-vs-provider/
Providers are accessible only in configuration phase of application life cycle. Their specific purpose is to provide a way to configure the future service, which provider should return via this.$get method.
In your case it doesn't really feel like you need a provider, simple service (factory) would have been enough. Or your can make use of factory and add one more method set to change text variable, stored in closure.
All I need to do is to download a json file and assign it to OCategories in PCategory provider after I set the path. However I get an error that $http doesnt exist. How can I inject it into my provider and download inside of the setPath function?
var app = angular.module('NSApp',
[
'ui.bootstrap',
'MDItem',
'MDUser',
'MDNotification',
'MDUpload'
]
);
app.config(function(PCategoriesProvider)
{
PCategoriesProvider.setPath('data/csv/categories.json');
});
MDItem/provider/category.js
angular.module('MDItem').provider('PCategories',function(){
var OCategories;
var OPath;
return{
setPath: function(d){
OPath = d;
console.log('Path is set. Trying to download categories.');
OCategories = $http.get(oPath);
},
$get : function() {
return {
categories : OCategories
}
}
}
});
You can never inject service instances into config functions or providers, since they aren't configured yet. Providers exist to configure specific services before they get injected. Which means, there's always a corresponding provider to a certain service. Just to clarify, here's a little example configuring $location service using $locationProvider:
angular.module('myModule').config(function ($locationProvider) {
$locationProvider.html5Mode(true);
});
So what happens here, is that we configure $location service to use its html5mode. We do that by using the interfaces provided by $locationProvider. At the time when config() is executed, there isn't any service instance available yet, but you have a chance to configure any service before they get instantiated.
Later at runtime (the earliest moment ist the run() function) you can inject a service. What you get when injecting a service is what its providers $get() method returns. Which also means, each provider has to have a $get() function otherwise $injector would throw an error.
But what happens, when creating custom services without building a provider? So something like:
angular.module('myModule').factory('myService', function () {
...
});
You just don't have to care about, because angular does it for you. Everytime you register any kind of service (unless it is not a provider), angular will set up a provider with a $get() method for you, so $injector is able to instantiate later.
So how to solve your problem. How to make asynchronous calls using $http service when actually being in configuration phrase? The answer: you can't.
What you can do, is run the $http call as soon as your service gets instantiated. Because at the time when your service get instantiated, you're able to inject other services (like you always do). So you actually would do something like this:
angular.module('myModule').provider('custom', function (otherProvider, otherProvider2) {
// some configuration stuff and interfaces for the outside world
return {
$get: function ($http, injectable2, injectable3) {
$http.get(/*...*/);
}
};
});
Now your custom provider returns a service instance that has $http as dependency. Once your service gets injected, all its dependencies get injected too, which means within $get you have access to $http service. Next you just make the call you need.
To make your this call is getting invoked as soon as possible, you have to inject your custom service at run() phrase, which looks like this:
angular.module('myModule').run(function (custom, injectable2) {
/* custom gets instantiated, so its $http call gets invoked */
});
Hope this makes things clear.
Since all services are singletons in angular you could simply store a variable in a factory with the $http promise. And then when the factory is called at startup it will download the json.
You can then also expose a method on the factory that refreshes the data.
I know this is not the exact answer to your question, but I thought I'd share how I would do it.
angular.module('MDItem').factory('PCategories', function ($http, PCategoriesPath) {
var service = {
categories: [],
get: function () {
if (angular.isUndefined(PCategoriesPath)) {
throw new Error('PCategoriesPath must be set to get items');
}
$http.get(PCategoriesPath).then(function (response) {
service.categories = response.data;
});
}
};
// Get the categories at startup / or if you like do it later.
service.get();
return service;
});
// Then make sure that PCategoriesPath is created at startup by using const
angular.module('MDItem').const('PCategoriesPath', 'data/csv/categories.json');
angular.module('app').controller('myCtrl', function ($scope, PCategories) {
$scope.categories = PCategories.categories;
// And optionally, use a watch if you would like to do something if the categories are updated via PCategories.get()
$scope.$watch('categories', function (newCategories) {
console.log('Look maa, new categories');
}, true); // Notice the true, which makes angular work when watching an array
})
You have to inject $http in the function $get, because that's the function called by the injector.
However, to download the categories you would be better off using promises:
angular.module('MDItem').provider('PCategories',function(){
var OCategories;
var OPath;
return{
setPath: function(d){
OPath = d;
console.log('Path is set');
},
$get : function($http) {
return {
fetch: function () {
var deferred = $q.defer();
$http.get(oPath).then(function (value) {
deferred.resolve(value);
}
return deferred.promise;
}
}
}
}
});
I implemented what I wanted with a diffrent approach which is quite simple and effective. Just add a dummy controller in the main index.html(NOT PARTIAL). Data is now shared between all my modules and controllers and everything is downloaded once. :) Oh I love AJ.
...
<div ng-controller="initController" hidden></div>
...
initController:
angular.module('NSApp',[]).controller("initController",function($scope, $http, FCategory, FLocation){
$http.get('data/json/categories.json').then(function (response) {
FCategory.categories = response.data;
});
$http.get('data/json/cities.json').then(function (response) {
FLocation.cities = response.data;
});
$http.get('data/json/regions.json').then(function (response) {
FLocation.regions = response.data;
});
});
And now you can access it:
angular.module('MDTest', []).controller("test",function($scope, FCategory, FLocation){
$scope.categories = FCategory.categories;
FCategory factory
angular.module('MDItem').factory('FCategory', function ($http) {
var service = {
categories: [],
....
};
return service;
});