I am absolutly new in AngularJS and I am studying it on a tutorial. I have a doubt related the use of the factory in Angular.
I know that a factory is a pattern used to create objects on request.
So in the example there is the following code:
// Creates values or objects on demand
angular.module("myApp") // Get the "myApp" module created into the root.js file (into this module is injected the "serviceModule" service
.value("testValue", "AngularJS Udemy")
// Define a factory named "courseFactory" in which is injected the testValue
.factory("courseFactory", function(testValue) {
// The factory create a "courseFactory" JSON object;
var courseFactory = {
'courseName': testValue, // Injected value
'author': 'Tuna Tore',
getCourse: function(){ // A function is a valid field of a JSON object
alert('Course: ' + this.courseName); // Also the call of another function
}
};
return courseFactory; // Return the created JSON object
})
// Controller named "factoryController" in which is injected the $scope service and the "courseFactory" factory:
.controller("factoryController", function($scope, courseFactory) {
alert(courseFactory.courseName); // When the view is shown first show a popupr retrieving the courseName form the factory
$scope.courseName = courseFactory.courseName;
console.log(courseFactory.courseName);
courseFactory.getCourse(); // Cause the second alert message
});
And this is the code, associated to this factoryController controller, inside the HTML page:
<div ng-controller="factoryController">
{{ courseName }}
</div>
So it is pretty clear for me that:
The factoryController use the courseFactory factory because is is injected
The first thing that happen when I open the page is that an alert mesage is shown because it is called the:
alert(courseFactory.courseName);
Then put into the $scope.courseName property (inside the $scope object) the value of the **courseName field of the courseFactory JSON object).
And here my first doubt. I have that the factory is defined by:
.factory("courseFactory", function(testValue)
that I think it means (correct me if I am doing wrong assertion) that my factory is named courseFactory and basically is a function that retutn the courseFactory JSON object.
So this is creating me some confusion (I came from Java) because in Java I call a factory class and I obtain an instance of an object created by the factory. But in this case it seems to me that doing:
$scope.courseName = courseFactory.courseName;
it seems to me that the courseName is retrieved directly by the courseFactory JSON object and I am not using the courseFactory.
How it exactly works? Why I am not calling some getter on the factory? (or something like this)
To simplify things There are two known methods to create functions ( and methods ) that are Usable among all states ( consider it as creating a function in global scope ) , these are factory and service.
you may want to read this first Factory vs Service
it seems to me that the courseName is retrieved directly by the
courseFactory JSON object and I am not using the courseFactory.
Actually this is partially true , as Angular use DI concept ( depency injection ) , you will have to inject your factory ( which by default returns an object , and corresponding functions as attributes ) , case you don't ; you will not be able to use your factory object methods.
Angular is not magic , it is just most people don't realize the most basic concepts of JavaScript , specially Developers coming from different programming environment such as C# , C++ , or Java
Easy.
When you return the courseFactory object in the factory statement, the injected courseFactory in the controller becomes THAT object. Therefore, when you do $scope.courseName = courseFactory.courseName; in the controller, you are actually referencing to the returned object that is injected into the controller as courseFactory. So you don't need a getter, because the courseFactory in your controller is already the object. You can do a console.log(courseFactory); in your controller to see what the courseFactory is. Hope this helps.
When an Angular application starts with a given application module,
Angular creates a new instance of injector, which in turn creates a
registry of recipes as a union of all recipes defined in the core "ng"
module, application module and its dependencies. The injector then
consults the recipe registry when it needs to create an object for
your application.
Ref. https://docs.angularjs.org/guide/providers
Factory is a Singleton service. When you inject the factory in the controller or in any other factory, u get the exact json object which u have defined. It will not create any new instance every time you call it. Factory is initialized only once
Related
I´m trying to inject a factory in a controller in Angular, but I can not do. It is my code.
app.controller('ManejadorEventosVista', ['AdministradorMarcador', ManejadorEventosVista]);
'app' is the variable that corresponds to the module with their respective dependencies. The controller is 'ManejadorEventosVista' and requires the services provided by the factory 'AdministradorMarcador'.
function ManejadorEventosVista(){}
but when I want to use the factory 'AdministradorMarcador' in this part of the code , the factory is not recognized.
ManejadorEventosVista.prototype.seleccionarMarcadorOrigen = function (){
AdministradorMarcador.setTipoMarcador(AdministradorMarcador.MARCADOR_ORIGEN);
};
How I can do to use the factory
'AdministradorMarcador' in ManejadorEventosVista.prototype.seleccionarMarcadorOrigen??..
Help or example to guide me??..Thanks..
ManejadorEventosVista needs to take an argument and you will be able to reference the AdministradorMarcador inside the function as whatever you named the first variable. Like so
function ManejadorEventosVista(AdministradorMarcador){/**your code here**/}
What you are doing with the line fragment ['AdministradorMarcador', ManejadorEventosVista] is declaring that your function depends on AdministradorMarcador, but without providing an argument to ManejadorEventosVista, AngularJS doesn't know how you intend to reference AdministradorMarcador inside your controller.
This is done in order to allow AngularJS scripts to be minified, especially by already existing solutions, as they would change the variables your function takes to single letter names, making it impossible for AngularJS to determine which service or factory to inject. Annotation uses strings and position-based ordering to allow your script to work, even after being minified since strings won't be altered by the process.
See also Latest Stable docs on Annotation
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.
With standard controller syntax in AngularJS, you can watch a variable like:
$scope.$watch(somethingToWatch, function() { alert('It changed!'); });
Using the controllerAs syntax, I want to react to this change in an active controller. What's the easiest way to do this?
More detail, if it helps. I have one controller in a side pane that controls the context of the application (user selection, start time, end time, etc.). So, if the user changes to a different context, the main view should react and update. I'm storing the context values in a factory and each controller is injecting that factory.
You can always use a watcher evaluator function, especially helpful to watch something on the controller instance or any object. You can actually return any variable for that matter.
var vm = this;
//Where vm is the cached controller instance.
$scope.$watch(function(){
return vm.propToWatch;
}, function() {
//Do something
}, true);//<-- turn on this if needed for deep watch
And there are also ways to use bound function to bind the this context.
$scope.$watch(angular.bind(this, function(){
return this.propToWatch;
//For a variable just return the variable here
}), listenerFn);
or even ES5 function.bind:
$scope.$watch((function(){
return this.propToWatch;
}).bind(this), listenerFn);
If you are in typescript world it gets more shorter.
$scope.$watch(()=> this.propToWatch, listenerFn);
Eventhough you can watch on the controller alias inside the controller ($scope.watch('ctrlAs.someProp'), it opens up couple of problems:
It predicts (or in other words pre-determines) the alias used for the controller in the view/route/directive/modal or anywhere the controller is used. It destroys the purpose of using controllerAs:'anyVMAlias' which is an important factor in readability too. It is easy to make typo and mistakes and maintenance headache too since using the controller you would need to know what name is defined inside the implementation.
When you unit test the controller (just the controller), you need to again test with the exact same alias defined inside the controller (Which can probably arguably an extra step if you are writing TDD), ideally should not need to when you test a controller.
Using a watcher providing watcher function against string always reduced some steps the angular $parse (which watch uses to create expression) internally takes to convert the string expression to watch function. It can be seen in the switch-case of the $parse implementation
I have a UI Router defined something like this (trimmed for simplicity):
$stateProvider
.state('someState', {
resolve: {
model: ['modelService', 'info', function (modelService, info) {
return modelService.get(info.id).$promise;
}]
},
controller: 'SomeController'
});
This someState state is using a factory / service that is dependent on that model resolve. It's defined something like this, and AngularJS throws an Unknown provider: modelProvider <- model <- someService error here:
angular
.module('someModule')
.factory('someService', someService);
someService.$inject = ['model'];
function someService(model) { ... }
However, using the same model resolve inside of this state's controller works fine:
SomeController.$inject = ['model'];
function SomeController(model) { ... }
So I'm understanding that UI Router is delaying the DI of the SomeController until the resolve is happening, which allows AngularJS to not throw an error. However, how come the same delay is not happening when putting that resolve as a dependency on someService? Do resolves only work on controllers? And if that is the case, how can I use a resolve inside a factory / service?
Do resolves only work on controllers?
Yes, resolves only work on controllers.
And if that is the case, how can I use a resolve inside a factory / service?
Remember that factories and services return singleton objects, i.e. the first time a factory is injected into a controller, it runs any instantiation code you provide and creates an object, and then any subsequent times that factory is instantiated, that same object is returned.
In other words:
angular.module('someModule')
.factory( 'SomeFactory' , function () {
// this code only runs once
object = {}
object.now = Date.now();
return object
);
SomeFactory.now will be the current time the first time the factory is injected into a controller, but it not update on subsequent usage.
As such, the concept of resolve for a factory doesn't really make sense. If you want to have a service that does something dynamically (which is obviously very common), you need to put the logic inside functions on the singleton.
For example, in the code sample you gave, your factory depended on a model. One approach would be to inject the model into the controller using the resolve method you've already got set up, then expose a method on the singleton that accepts a model and does what you need to do, like so:
angular.module('someModule')
.factory( 'SomeFactory', function () {
return {
doSomethingWithModel: function (model) {
$http.post('wherever', model);
}
});
.controller('SomeController', function (SomeFactory, model) {
SomeFactory.doSomethingWithModel(model);
});
Alternatively, if you don't need the resolved value in the controller at all, don't put it directly in a resolve, instead put the resolve logic into a method on the service's singleton and call that method inside the resolve, passing the result to the controller.
It's hard to be more detailed with abstract conversation, so if you need further pointers then provide a specific use-case.
You cannot use the resolved values in services or factories, only in the controller that belongs to the same state as the resolved values. Services and factories are singletons and controllers are newly instantiated for (in this case) a state or anywhere else where ng-controller is used.
The instantiation happens with the $controller service that is capable of injecting objects that belong only to that controller. Services and factories do not have this capability.
You should review the angular docs on dependency injection:
Components such as services, directives, filters, and animations are defined by an injectable factory method or constructor function. These components can be injected with "service" and "value" components as dependencies.
Controllers are defined by a constructor function, which can be injected with any of the "service" and "value" components as dependencies, but they can also be provided with special dependencies. See Controllers below for a list of these special dependencies.
The run method accepts a function, which can be injected with "service", "value" and "constant" components as dependencies. Note that you cannot inject "providers" into run blocks.
The config method accepts a function, which can be injected with "provider" and "constant" components as dependencies. Note that you cannot inject "service" or "value" components into configuration.
So each type of angular component has its own list of acceptable components to inject. Since services are singletons, it really wouldn't make any sense to inject a value as part of a resolve. If you had two separate places on your page that used a service with different resolves, the outcome would be indeterminate. It would make no more sense than injecting $scope into your service. It makes sense for controllers because the controller is responsible for the same area of the page that is being resolved.
If your someService needs to use the data in model, it should have a function that takes the data as a parameter and your controller should pass it.
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