I need an object to be globally accessible all throughout my Angular application, and I've gladly put this object in a value service.
Unfortunately, this object is computed by another service, and I've not been able to inject myService into same value service.
ATM, I've acheived my goal with something like this:
global service code
app.service('myService', function() {
this.foo = function() {
// some code that returns an object
return computedObject
}
})
app.run(function ($rootScope, myService) {
$rootScope.global = {dbObject: myService.foo()}
})
And which I can use in any controller that pleases me by simply injecting $rootScope in it
However, I don't like the need of injecting the whole $rootScope wherever I need that damn object, and I trust is not much safe (or efficient?) either, since the team specifies
Of course, global state sucks and you should use $rootScope sparingly, like you would (hopefully) use with global variables in any language. In particular, don't use it for code, only data. If you're tempted to put a function on $rootScope, it's almost always better to put it in a service that can be injected where it's needed, and more easily tested.
Conversely, don't create a service whose only purpose in life is to store and return bits of data.
Do you, perchance, happens to know any way I can inject a service into a service value?
Or maybe any other Angular best practice which I could exploit?
I forgot one very important notice
The computation of the object could be quite computational intense, so I absolutely don't want it to be recomputed everytime I move from page to page, or anything else really.
Ideally Using $rootScope for storing a few globally required values is totally ok. But if you are still adamant on using a module, I suggest you use a provider.
Make your 'myService' a provider and that will let you configure the variables in the service.
More info here
AngularJS: Service vs provider vs factory
You could use $broadcast to send the value from the value service to myService.
You would still need to inject $rootscope into each of the services, but from then on you could just inject myService around the rest of the app.
Reference here.
I need an object to be globally accessible all throughout my Angular application
I would use service. Since Service is singleton you can register the service to all your controllers and share any data over service.
Unfortunately, this object is computed by another service, and I've not been able to inject myService into same value service.
Just create one main service (Parent) and child service that will inherit the parent. (like abstract service in Java world).
Application.factory('AbstractObject', [function () {
var AbstractObject = Class.extend({
virtualMethod: function() {
alert("Hello world");
},
abstractMethod: function() { // You may omit abstract definitions, but they make your interface more readable
throw new Error("Pure abstract call");
}
});
return AbstractObject; // You return class definition instead of it's instance
}]);
Application.factory('DerivedObject', ['AbstractObject', function (AbstractObject) {
var DerivedObject = AbstractObject.extend({
virtualMethod: function() { // Shows two alerts: `Hey!` and `Hello world`
alert("Hey!");
this._super();
},
abstractMethod: function() {
alert("Now I'm not abstract");
}
});
return DerivedObject;
}]);
Now, we can add some logic into AbstractObject and continue use DerivedObject
Here is example Plunker
Related
I'm getting into Angularjs. I'm want to re-use a function, resetForm() function. My question is, do I still put that inside my controller $scope or create a factory or service?
app.controller('testController', [
'$scope',
'testService',
function($scope, testService) {
$scope.addTestForm = function() {
var body = document.getElementsByTagName('body')[0];
if (!body.classList.contains('test__add')) {
body.classList.add('test__add');
}
};
//do I add my function here?
function name() {};
}]);
if it is a resetForm() function then I assume it is dealing with DOM. I would suggest you to declare this function inside your controller since you will need access to $scope to reset form fields (direct DOM access is strictly prohibited in AngularJS). You can refer to below sample code
app.controller('testController', [
'$scope',
'testService',
function($scope, testService) {
var resetForm = function() {
// your logic to reset form with help of $scope
};
$scope.addTestForm = function() {
var body = document.getElementsByTagName('body')[0];
if (!body.classList.contains('test__add')) {
body.classList.add('test__add');
}
};
}]);
Note: You don't need to declare resetForm function as $scope.resetForm if you don't plan to call it from your template file.
If you want to re-use it across multiple controllers, a Factory or a Service is probably the best way to share it without duplication of code. You can then call on either one of these from all your controllers.
The added benefits to this pattern are that, not only do you save yourself from duplicating code, but you can also store variables and share those as well.
Both will work, but you can read some interesting discussion on Factory vs Service if you have trouble with which one to choose.
The things goes like this:
We will write functions in controllers if that function is normally manipulating model and is only relevant to that controller.
We write services normally for giving data to controllers such as from a asynchronous API call, and for sharing data in between controllers.
In your case, if you want a utility function you can use a service, but resetForm function is more like controller specific, because it's gonna clear some model values. In future you may want to add more conditions and operations in that function which may produce complex code, if you use a service for that.
If that function is a 'non-gonna change function' using a service is good way to go. (code re-usability and all), but otherwise, wrap all logic in one place is more good.
(write it in controller)
According to this source,
Why do the two styles exist if they can accomplish the same thing?
Factories offer slightly more flexibility than services because they
can return functions which can then be new'd. This follows the factory
pattern from object oriented programming. A factory can be an object
for creating other objects.
I'm having trouble connecting this piece of information to the concept of constructors in Javascript and object / prototype. Can someone help me connect the dot? Thanks.
Service VS Factory
A service is a constructor function that can be called with 'new' where as a factory returns an object that you may run some code before that is why it is more flexible.
To see a detailed example please check out this question. matys84pl talks about it in detail.
Factory
These are practically singletons. So for instance you can hold data across calls to it.
factory('myFactory', function () {
var internalList = [];
return {
add: function (value) { internalList.push(value); },
get: function () { return internalList; }
}
});
As you could imagine, if controller1 pushed Hi into the list and controller2 called get() immediately after, it would recieve [ 'Hi' ].
Service
From the angular docs
JavaScript developers often use custom types to write object-oriented code.
service('UnicornLauncher', function (apiToken) {
this.launchedCount = 0;
this.launch = function() {
// Make a request to the remote API and include the apiToken
...
this.launchedCount++;
}
});
Provider
For some bonus reading, I'll tell you about a provider. A provider wraps a factory. Why? Because there are two phases in an angular application, the configure phase and the run phase. Services, factories and values AREN'T available during the run phase.
So, say we need to configure a service before it is injected into the controllers. We need to use a provider.
The angular docs have a nice example:
provider('unicornLauncher', function UnicornLauncherProvider() {
var useTinfoilShielding = false;
this.useTinfoilShielding = function(value) {
useTinfoilShielding = !!value;
};
this.$get = ["apiToken", function unicornLauncherFactory(apiToken) {
// let's assume that the UnicornLauncher constructor was also changed to
// accept and use the useTinfoilShielding argument
return new UnicornLauncher(apiToken, useTinfoilShielding);
}];
});
so this.$get is basically the second argument of the factory('...', ....
So if we use this in the configure phase we do this:
config(["unicornLauncherProvider", function(unicornLauncherProvider) {
unicornLauncherProvider.useTinfoilShielding(true);
}]);
Notice how we ask for unicornLauncherProvider, we add that Provider suffix. Yet when we named our provider we didn't supply it. That is because angular automatically adds the suffix for you. If you want to access the service this provider creates, ask for the unicornLauncher.
I had an 'Eureka effect' when i realized that service & factory are only syntactic sugar for provider (https://docs.angularjs.org/api/auto/service/$provide#provider)
The only difference between these two is the way Angular processes the passed value before providing it with the injector.
When I get the object from the injector, I usually can't tell if it has been created using a service or a factory (or any of the other $provide methods)
So the Question is "How do I want to implement this?" and not "How do I want to use this?".
When creating an Angular Module one could essentially add global arrays or objects to the module. Like so..
var myApp = angular.module('myApp', ['myModule']);
myApp.run(function()
{
});
angular.module('myModule', [])
.run(function()
{
// global to module
var thisModule = angular.module('myModule');
thisModule.globalArray = [];
thisModule.globalObject = {};
});
So here's the question(s). Would it be a bad idea to do something like this? Is there anything in the documentation that recommends not doing this? And if so, why would or wouldn't you recommend not doing this?
Demo:
http://jsfiddle.net/hrpvkmaj/8/
In general, Angular goes to great lengths to avoid global state. You can observe this in the dependency injection system that the framework is based on. To use a component, you must inject it as a parameter that is wired up behind the scenes. The framework also has a powerful scoping system that allows for nice and easy encapsulation. Relying on global variables works against these systems.
In particular, it would be a bad idea to do something exactly like your code example because it isn't how Angular was designed to be used. You shouldn't be adding your own properties to Angular's module object. At the very least, you should be injecting the $rootScope object and adding your global variables to that.
app.run(function($rootScope)
{
$rootScope.globalArray = [];
$rootScope.globalObject = {};
});
From the Angular documentation:
Every application has a single root scope. All other scopes are descendant scopes of the root scope.
If you went this route, you could inject $rootScope wherever you need to use those global values.
However, the Angular team discourages using $rootScope.
Of course, global state sucks and you should use $rootScope sparingly, like you would (hopefully) use with global variables in any language. In particular, don't use it for code, only data. If you're tempted to put a function on $rootScope, it's almost always better to put it in a service that can be injected where it's needed, and more easily tested.
There is another way of defining global values in Angular that is even preferable over using $rootScope: defining a value provider.
A value provider is the simplest kind of provider. It defines a single value that can be injected throughout your app.
You can define one like this:
app.value("myValue", {
someProp: 'Hello World'
});
Then you can inject the value wherever you need it like this:
app.controller("myController", function ($scope, myValue) {
$scope.someValue = myValue.someProp;
});
Here's a fiddle using a value provider to inject a global value.
In the end, any answer you get, including this one, will include some level of subjectivity. There are many ways to handle global values, but Angular provides some really convenient ways of using the framework to accomplish this.
I read this style guide for angular from johnpapa. There is a snippet:
/*
* recommend
* Using function declarations
* and bindable members up top.
*/
function Avengers(dataservice, logger) {
var vm = this;
vm.avengers = [];
vm.getAvengers = getAvengers;
vm.title = 'Avengers';
activate();
function activate() {
return getAvengers().then(function() {
logger.info('Activated Avengers View');
});
}
function getAvengers() {
return dataservice.getAvengers().then(function(data) {
vm.avengers = data;
return vm.avengers;
});
}
}
So my question is in functions activate() and getAvengers(), they both reference variable (dataservice) and function (getAvengers()) outside of their scope. Is this proper use? Should I bind these 2 in the variable vm instead, e.g:
vm.getAvengers = getAvengers;
vm.dataservice = dataservice;
...
function activate() {
return vm.getAvengers().then(....);
}
function getAvengers() {
return vm.dataservice.getAvengers().then(.....);
}
Specifically for your case
Would say if you are meaning to use this within angular app would recommend not exposing the service, exposing it through this object does not add value and might down the road, when a less experienced developer modifies your code, might result in wonky access to shared dependencies.
If you want access to the dataservice objects functionality across multiple entities then register it as an angular service, and inject it to the different entities that need it.
In General
Both of the ways you are describing are perfectly correct use, but as is usually the case the answer which to use is "it depends."
Why you would use one for another would be if you wanted to expose the variable externally (i.e. if you wanted to let others access that object through the returned object, expecting others to dynamically change the service on your object)
So in this example you should ask yourself a few question
Do I want to expose this object through another object or do I want to let angular DI pass this along to the other controllers that need this functionality
Do I want to allow external entities to modify this object
Does exposing this service through my object make the use of the perceived use of this object more confusing?
But again for this particular case you should not expose it through your object ( through your variable vm, which is bound to the return object this, in this case )
The vm is a acronym for a view model (a object representation of your view) it is meant to be used within your view to bind elements, ui events to it. The dataservice and the logger seems to nothing to do with the view at all, they are just services used within a controller. If you assign them to the vm then you probably create a tightly coupling between your view and services thus it seems like a not a very good idea to me. You can think about the VM as a interface (glue) between your view and controller.
Here is a picture of the interactions between view model, controller, view and services.
I've been working a lot lately with AngularJS and I really like the dependency injection stuff. I'm using it all over the place, but just in Angular components, like controllers and the like.
This always calls on the angular object:
angular.module('app').controller(/**/);
Now I have this function:
var custom = function(MyService) {
// do stuff
};
I've declared the Service this way:
angular.module('app').factory('MyService', function($rootScope) {
return {
show: function() {
// do stuff
};
},
hide: function() {
// do stuff
}
};
});
I now want to use this service in my custom function. Is there a way of manually calling the angular DI container? (I couldn't find anything in the docs...)
I know that this works for controllers not defined with the angular.module()... thing:
function Controller(MyService) {
MyService.hide(); // works
}
But how to use it outside of AngularJS components, in completely independent functions?
Or do I have to take a completely different path to achieve my goal?
Cheers and thanks in advanced,
Christian
angularjs is pretty neat in the fact that it exposes its own internal services to the user. The service you're looking for is $injector
What you want to use to call your custom function is $injector.invoke(myCustomFunction,thisForCustomFunction,{named:locals});
If you are by chance wanting to invoke this function outside of angular you'll have to get the applications injector.
var injector = angular.element(elementWithNgApp).injector();
Note:
The invocation time may be reduced though. As the injector must annotate (find what services you need) using several regular expressions. The reason this is not an issue throughout angular is because it is done once. Because services,factories,providers, are all instantiated (newed) singletons that provide closure with the services and whatever uses the services inside your providers
To prevent this extra step you can provide a $inject property on your function.. Something like this:
myfunction.$inject = ['ServiceA','ServiceB'];
function myfunction(a,b){
//a is ServiceA
//b is ServiceB
};