Angular 2 dependecy injection - how to know where to insert dependencies - javascript

I am learning angular2 and find myself a bit confused where to inject dependencies sometimes. Like for example when using RouteParams
import {RouteParams} from 'angular2/router';
we just pass it to constructor method and we are good to go:
constructor(private _photoService: PhotoService, private _routeParams: RouteParams){
}
But, when we use ROUTER_DIRECTIVES from the same module 'angular2/router'
import {ROUTER_DIRECTIVES} from 'angular2/router';
We need to pass it to directives array of the component:
directives: [ROUTER_DIRECTIVES]
So, my question is, how can I know, where I need to pass it, when do I pass dependencies to the directives or providers array, or just to constructor method?

Dependencies are defined based on what they do.
There is a method to the madness but it is overwhelming when you are starting out for sure. Take a look at the docs at https://angular.io/docs/ts/latest/guide/architecture.html
Basically directives manipulate the DOM, dependency injection injects a complete class (with all of its dependencies), etc. So there are specific ways/places to tell Angular what is what.
Hopefully those docs help sort it out for you. About halfway down the page it starts discussing directives and injection.

A providers is used by DI to create and hold a value.
Where you register a provider #NgModule({providers: []}), #Component({providers: []}), or #Directive({providers: []}) defines what value or instances will be passed to constructors that depend on them.
If you register a provider in a non-lazy-loaded #NgModule() DI will provide a single instance for the whole application in its root scope.
If you register a provider on a #Component() or `#Directive() then DI will provide a single instance per component or directive instance.
If MyComponent provides service A and there are 5 instances of MyComponent on your page, then there will be up to 5 instances of A (a provider only creates an instance when it is requested the first time).
When a component, directive, or pipe has a constructor parameter, then DI will try to find a provider that matches the parameters type or #Inject() decorator (if there is any).
DI starts looking at the host component for providers, and passes the instance it holds to the constructor. If the host component doesn't provide a matching provider DI keeps looking at parent components until it finds one. If it reaches the root component (AppComponent) and still hasn't found a provider it checks the root scope (#NgModule() providers).
Lazy loaded module are a bit special because providers registered there can't be added to the root scope. Once a scope (injector) is created, new providers can't be registered.
Lazy loaded modules create a child scope to the applications root scope.
Providers registered there will only be visible to components, directives, pipes and services which are part of the lazy loaded module (and imported modules loaded with the lazy loaded module).

Related

Using same service with providedIn: root as singleton and providers of component with the service

I would like to know what happens if the following criteria happen.
Created a service as single-ton with providedIn property as root. So that it will be available to the application globally. At the same time, providers meta data in component or module set to the created service. Can any one tell me what is the effect on this situation?
To get more about Singletons you need to refer to official documentation.
Answering your question its quite simple. Singletons are globals for entire application and share the same data. For example, you have a service which adds a script to the head or authentication service which holds global auth status - you need to ensure that they have only one instance across entire application.

Angular 5 global components and their services

Looking through Angular's docs, specifically the style guide, they lay out both a shared module and a core module. According to the docs, services should be placed in the core module and components in the shared module. My question is if I have a component, say a custom file upload component, and it also has a corresponding service, where should that component and service reside? It seems to make sense to keep the two together since the service is used with the component, however, this doesn't seem to follow Angular standards. Anyone come across this issue?
According to Angular Style Guide, Angular doesn't really recommend providing a service in the SharedModule. The reason for that is, whichever module imports the SharedModule, will get a separate instance of that shared service. This isn't really acceptable in case of a Stateful Service. But in your case, I'm not really sure if the Service will contain any state data. So I think, it should be safe for you to add this Component and this Service to the SharedModule itself.
Again, considering that this service is not going to have any state data.
Also, Angular recommends providing services inside the CoreModule because it also recommends making the CoreModule only importable by just a single module(generally the AppModule). That's why it also recommends creating a Guard to prevent re-importing of the CoreModule.
Look up to Providers and singleton-services. In usual way that:
if you want singleton service you must provide it in root module, e.g: router
if you need service instance per component instance, then you provide that service in component definition, and you can pass that service instance to all child components:
#Component({
/* . . . */
providers: [UserService]
})
In case of yours example: if every upload component needs new instance of service, service is defined as #Injectable class, imported to upload component source and registered as a provider in component decorator providers section.
This is a good question and I've got it constantly asked by colleagues in my company. As you said the best practice in official docs is to have CoreModule and SharedModule that each in charge of different concerns. Most of the time it makes sense to put your services into "Core" and components/directives/pipes etc. into "Shared".
But sometimes we do have exceptions and want to do the following:
A service in SharedModule to manage the states/configurations/behaviors for the counterpart components/directives/pipes
A component in CoreModule that only exists in the root level or dynamically created during runtime.
For scenario 1 you can have the legacy forRoot() method for your module to make sure your service only gets initialized once. Also it's worth mentioning that with Angular 6 you can use the new provideIn: 'root' syntax which simplifies this purpose. You can find many resources online.
For scenario 2 you usually put it into entryComponents.

Angular2 Allow Components and its Sub Components to access single instance of ngModel in service

I am building complex view with a lot of functionality on it. To keep things organized, I have broken it up into several custom components.
I'll try to keep this brief as I do have it working but I cant imagine that its the "right way".
Structure:
- AppComponent(has binding to dataModel)
--LayoutComponent
---HeaderComponent
---SideMenuComponent
---MainContentComponent
I need to be able to give each of these components access the same single instance of the data model. But when I add the service as a provider and to the constructor in each Component, it generates a new instance of the data. So edits done in one component, don't reflect in the data of a different one.
So what I did was give the top most parent AppComponent component access to the dataModel and then passed it down the chain through the template [(dataModel)]="dataModel".
Then in each Component I added a dataModel variable which starts off as null, but after I build my template, I bind it to that local variable.
This works as I can bind inputs in each component to the same field i.e. dataModel.name and when I update it, all components reflect the change.
This just doesn't seem like a clean way of doing it. Any help would be appreciated.
If you add the service to providers of every component, every component gets its own service instance. Angular2 DI maintains a single instance per provider.
If you add it to providers of a comment ancestor, then this component and all descendants get an instance from this provider.
In >= RC.5 if you add the service to providers: [...] of any #NgModule(...) that is not lazy loaded it will be added as provider to the root injector and therefore shared with the whole application (as long as any of the descendant components also has it as provider which overrides it for its descendants).
For lazy loaded modules providers are shared within this lazy loaded module because lazy loaded modules have their own DI child scope.

AngularJS - module().provider() vs $provide.provider()

Note: I'm not seeking the differences between the value, factory, service, and provider 'recipes', as explained here.
I'm looking for clarification on the different ways to define them: Correct me if I'm wrong, but it appears as though
myApp = angular.module('myApp', [])
.value(...)
.factory(...)
.service(...)
.provider(...)
map to
$provide.value()
$provide.factory()
$provide.service()
$provide.provider()
And you can use either way. I asked this question, and realize I can use $provide as a dependency to module().config(). My questions:
When/why would I use angular.module().provider() vs using the $provide dependency?
Is there any way (or reason) to access/change a provider after definition?
Using AngularJS Batarang for Chrome, I'm looking at the various angular $scope properties, and don't see $provide or $injector. Where do these live?
The provider methods off the module definition are just short cuts. Use them as often as you like because it leads to shorter, easier to read and understand code. Less ritual/ceremony is involved than injecting the $provider service and calling that directly. The main reason to use $provide directly is to access a method on it that is not a short cut from module (such as the decorator) or if you have to do something from within a service or component that is not up at the module definition level.
The common case for changing a provider after it's definition is when you are integrating a third-party component and want to add or change the behavior. The third-party module will define the service and then you step in and override or extend it in some way that is specific to your app. A common case for example is to take the built-in Angular exception handler and extend that to interface with your own components.
$scope is a special "glue" used for data-binding and only exposes properties/functions that you explicitly set on the $scope. All of the other miscellaneous modules/services are stored within Angular's dependency injection container. The very first thing Angular does is create an $injector instance to keep track of dependencies. Therefore $injector === $injector.get('$injector'). Same with $provide. Anything prefixed with a $ is by convention a service that Angular places in the $injector for you to use.

How to use Angular.js service instance inside module config and keep the same instance

I am trying to use an service inside my app module config and a little research pointed that I need to do it using an injector.
This works pretty fine, except that when the service is loaded inside the controllers it is not the same instance anymore as the one previously loaded by the injector.
Does anyone know how to get the same instance? Or is there a better way to do it?
I created a plnkr to show the behavior: http://plnkr.co/edit/BGUa3H
From the doc - Services as singletons
All Angular services are application singletons. This means that there
is only one instance of a given service per injector. Since
Angular is lethally allergic to global state, it is possible to create
multiple injectors, each with its own instance of a given service, but
that is rarely needed, except in tests where this property is
crucially important.
It mean if you do injector.get for a single injector, it will always returns the singleton like this
var injector = angular.injector(['app.services']);
var singletonService1 = injector.get('singletonService');
var singletonService2 = injector.get('singletonService');
console.log(singletonService1 === singletonService2) // prints true
However if you inject it to another the controller in the meantime, a brand new instance will be created.
Hope it helps.

Categories