I have the below service:
import {Injectable} from 'angular2/core';
import {Service2} from './app.service2';
#Injectable()
export class Service1 {
constructor(service2:Service2) {
this.service2 = service2;
}
getData() {
return this.service2.getData();
}
}
I am using this service in one of my component, but i am not sure how does this service recieve the service2 as argument? Tradionally, in JS i do something like this.
var s = new Service1(service2);
But i don't see anything like this in below plunker, yet it works.
https://plnkr.co/edit/PsySVcX6OKtD3A9TuAEw?p=preview
Can anyone add some light on this.
Angular2 uses dependency injection (DI) to create instances. It creates class instances for components, directives, pipes, and services for you. For this it checks the constructor parameters and its registered list of providers for matchin instances and when DI calls new Xxx(...) for you it passes all providers found in its providers list and one was not found it will throw an exception.
If one of the classes has an constructor with parameters, the class needs to have a decorator (one of #Component(), #Directive(), #Pipe(), or #Injectable(). This indicates for DI that it needs to analyze the constructor.
Related
I am using microsoft adaptive card 2.0. I need to create some action service and pass that service to adaptive card module and from there it will execute. But while implementing I am facing issue. That action service has dependency on other services and we cant pass that dependencies from constructor.
**ActionService.js**
import ServiceX from '../'
import ServiceZ from '../'
export Class ActionService{
constructor(
private serviceX: ServiceX,
private serviceZ: ServiceZ
)
execute(){
this.serviceX.action()
}
}
**AdaptiveCard service**
import { ActionService } from '../'
import * as AC from 'adaptivecards';
export class AdaptiveCardService{
AC.GlobalRegistry.actions.register('name-of-action', ActionService);
}
Here the problem is, from adaptive module calling Action service using NEW keyword and not passing dependencies as arguments. Hence in action service inside execute function, servicex getting undefined.
Is there any way to inject services without calling in constructor .
I referred below url to inject it but it is supported only in Angular 2
Getting instance of service without constructor injection
You can use Injector.
constructor(private injector: Injector) {
let serviceX: ServiceX= this.injector.get(ServiceX);
}
For information Angular Injector
I'm creating a #Log() Decorator Function for debugging purposes;
I want that Decorator to delegate some of it's logic to a LoggingService that in turn depends on other services from the app...
I've been trying a lot of different things, the simplest/most straightforward way was to cache the Main (or Shared) Module's Injector as a static prop on the module itself (see StackBlitz example linked below), and that works for lazy-loaded modules, but not for eagerly loaded ones...
Non-working poc: https://stackblitz.com/edit/angular-j1bpvx?file=app%2Fdecorator.ts
Is there a way I could mkae use of that Service in there??
Thanks!
Class decorator is executed once on class definition. In order to avoid race condition when calling AppModule.injector.get(LoggingService) it should be moved to the place where AppModule.injector is already defined, i.e. class method.
It should be:
constructor.prototype[hook] = function (args) {
const loggingService = AppModule.injector.get(LoggingService);
loggingService.log({ ... })
...
This also creates tight coupling with AppModule and prevents the units from being reused or tested separately from it. It's recommended to use another object to hold injector property, e.g. assign injector not in main but in child module that is imported into AppModule:
export class InjectorContainerModule {
static injector: Injector;
constructor(injector: Injector) {
InjectorContainerModule.injector = injector;
}
}
Try stackblitz fixed
This will print
LoggingService: HelloComponent - ngOnInit was called
Minor changes - basically using ReflectiveInjector as in angular Injector#example
import { ReflectiveInjector } from '#angular/core';
const injector = ReflectiveInjector.resolveAndCreate([
{provide: 'loggingService', useClass: LoggingService}
]);
const loggingService = injector.get('loggingService');
I am sure you can use useExisting and use LoggingService as provider in your app module.
Problem Statment
I am learning Angular 4 and I have stumble upon a code where #Inject is being used in a constructor and I am not able to figure out why...
Code and Source
I am using Angular 4 Material
Code Source: https://material.angular.io/components/dialog/overview
In the code, they are injecting MAT_DIALOG_DATA
constructor(public dialogRef: MatDialogRef<DialogOverviewExampleDialog>,
#Inject(MAT_DIALOG_DATA) public data: any
) { }
Can anyone please elaborate what does it mean and when/where should we do this?
#Inject() is a manual mechanism for letting Angular know that a
parameter must be injected.
import { Component, Inject } from '#angular/core';
import { ChatWidget } from '../components/chat-widget';
#Component({
selector: 'app-root',
template: `Encryption: {{ encryption }}`
})
export class AppComponent {
encryption = this.chatWidget.chatSocket.encryption;
constructor(#Inject(ChatWidget) private chatWidget) { }
}
In the above we've asked for chatWidget to be the singleton Angular
associates with the class symbol ChatWidget by calling
#Inject(ChatWidget). It's important to note that we're using
ChatWidget for its typings and as a reference to its singleton.
We are not using ChatWidget to instantiate anything, Angular does
that for us behind the scenes
From https://angular-2-training-book.rangle.io/handout/di/angular2/inject_and_injectable.html
If MAT_DIALOG_DATA is a non-factory/class dependency (like string for your configs), you usually use #Inject.
Also check InjectionToken: https://angular.io/guide/dependency-injection#injectiontoken
One solution to choosing a provider token for non-class dependencies is to define and use an InjectionToken
Here's a plunker: http://plnkr.co/edit/GAsVdGfeRpASiBEy66Pu?p=preview
if you remove #Inject in these cases you will receive a
Can't resolve all parameters for ComponentName: (?)
IoC container in Angular uses the type declarations in the constructor to determine the objects to be injected to the constructor parameters.
In your example, "public data: any" parameter could not be determined by its type declaration because it's defined as "any". In order to solve this problem, you have to use "#Inject(MAT_DIALOG_DATA)" decorator to inform the IoC container about the object that must be injected to "data" parameter.
Also in your example, "#Inject" decorator is used with an InjectionToken to complicate things a little more :)
An InjectionToken is actually a class which is used to name the objects to be used by IoC container to inject in to other classes. Normally you could use any classes name as a token for IoC injection (like "MatDialogRef<DialogOverviewExampleDialog>" in your example) and this works fine. But when you start writing your UnitTests you realize that you need to use Mock objects instead of real objects to be injected into your classes and when you use real class names as your tokens, you could not do that.
To solve this problem you could use Interfaces as token names and this is actually the right solution, but since JavaScript does not support interfaces you could not use Interface names as tokens, because transpiled code does not contain Interface definitions.
As a result of all this, you need to use InjectionToken. An InjectionToken allows you to inject any object into your constructor. You just need to declare it in your modules and map to the real class that you want to be injected. This way you could use different classes for your production and test codes.
I have built a shared data service that's designed to hold the users login details which can then be used to display the username on the header, but I cant get it to work.
Here's my (abbreviated) code:
// Shared Service
#Injectable()
export class SharedDataService {
// Observable string source
private dataSource = new Subject<any>();
// Observable string stream
data$ = this.dataSource.asObservable();
// Service message commands
insertData(data: Object) {
this.dataSource.next(data)
}
}
...
// Login component
import { SharedDataService } from 'shared-data.service';
#Component({
providers: [SharedDataService]
})
export class loginComponent {
constructor(private sharedData: SharedDataService) {}
onLoginSubmit() {
// Login stuff
this.authService.login(loginInfo).subscribe(data => {
this.sharedData.insertData({'name':'TEST'});
}
}
}
...
// Header component
import { SharedDataService } from 'shared-data.service';
#Component({
providers: [SharedDataService]
})
export class headerComponent implements OnInit {
greeting: string;
constructor(private sharedData: SharedDataService) {}
ngOnInit() {
this.sharedData.data$.subscribe(data => {
console.log('onInit',data)
this.greeting = data.name
});
}
}
I can add a console log in the service insertData() method which shoes the model being updated, but the OnInit method doesn't reflect the change.
The code I've written is very much inspired by this plunkr which does work, so I am at a loss as to what's wrong.
Before posting here I tried a few other attempts. This one and this one again both work on the demo, but not in my app.
I'm using Angular 2.4.8.
Looking through different tutorials and forum posts all show similar examples of how to get a shared service working, so I guess I am doing something wrong. I'm fairly new to building with Angular 2 coming from an AngularJS background and this is the first thing that has me truly stuck.
Thanks
This seems to be a recurring problem in understanding Angular's dependency injection.
The basic issue is in how you are configuring the providers of your service.
The short version:
Always configure your providers at the NgModule level UNLESS you want a separate instance for a specific component. Only then do you add it to the providers array of the component that you want the separate instance of.
The long version:
Angular's new dependency injection system allows for you to have multiple instances of services if you so which (which is in contrast to AngularJS i.e. Angular 1 which ONLY allowed singletons). If you configure the provider for your service at the NgModule level, you'll get a singleton of your service that is shared by all components/services etc. But, if you configure a component to also have a provider, then that component (and all its subcomponents) will get a different instance of the service that they can all share. This option allows for some powerful options if you so require.
That's the basic model. It, is of course, not quite so simple, but that basic rule of configuring your providers at the NgModule level by default unless you explicitly want a different instance for a specific component will carry you far.
And when you want to dive deeper, check out the official Angular docs
Also note that lazy loading complicates this basic rule as well, so again, check the docs.
EDIT:
So for your specific situation,
#Component({
providers: [SharedDataService] <--- remove this line from both of your components, and add that line to your NgModule configuration instead
})
Add it in #NgModule.providers array of your AppModule:
if you add it in #Component.providers array then you are limiting the scope of SharedDataService instance to that component and its children.
in other words each component has its own injector which means that headerComponentwill make its own instance of SharedDataServiceand loginComponent will make its own instance.
My case is that I forget to configure my imports to add HttpClientModule in #NgModules, it works.
I have written two services in Angular 2. One of those is a basic, customised class of Http with some custom functionality in (it looks basic for now, but it will be expanding):
ServerComms.ts
import {Injectable} from 'angular2/core';
import {Http} from 'angular2/http';
#Injectable()
export class ServerComms {
private url = 'myservice.com/service/';
constructor (public http: Http) {
// do nothing
}
get(options) {
var req = http.get('https://' + options.name + url);
if (options.timout) {
req.timeout(options.timeout);
}
return req;
}
}
Another class, TicketService utilises this class above, and calls one of the methods in the service. This is defined below:
TicketService.ts
import {Injectable} from 'angular2/core';
import {ServerComms} from './ServerComms';
#Injectable()
export class TicketService {
constructor (private serverComms: ServerComms) {
// do nothing
}
getTickets() {
serverComms.get({
name: 'mycompany',
timeout: 15000
})
.subscribe(data => console.log(data));
}
}
However, I receive the following error whenever I try this:
"No provider for ServerComms! (App -> TicketService -> ServerComms)"
I do not understand why? Surely I do not need to inject every service that each other service relies upon? This can grow pretty tedious? This was achievable in Angular 1.x - how do I achieve the same in Angular 2?
Is this the right way to do it?
In short since injectors are defined at component level, the component that initiates the call services must see the corresponding providers. The first one (directly called) but also the other indirectly called (called by the previous service).
Let's take a sample. I have the following application:
Component AppComponent: the main component of my application that is provided when creating the Angular2 application in the bootstrap function
#Component({
selector: 'my-app',
template: `
<child></child>
`,
(...)
directives: [ ChildComponent ]
})
export class AppComponent {
}
Component ChildComponent: a sub component that will be used within the AppComponent component
#Component({
selector: 'child',
template: `
{{data | json}}<br/>
Get data
`,
(...)
})
export class ChildComponent {
constructor(service1:Service1) {
this.service1 = service1;
}
getData() {
this.data = this.service1.getData();
return false;
}
}
Two services, Service1 and Service2: Service1 is used by the ChildComponent and Service2 by Service1
#Injectable()
export class Service1 {
constructor(service2:Service2) {
this.service2 = service2;
}
getData() {
return this.service2.getData();
}
}
#Injectable()
export class Service2 {
getData() {
return [
{ message: 'message1' },
{ message: 'message2' }
];
}
}
Here is an overview of all these elements and there relations:
Application
|
AppComponent
|
ChildComponent
getData() --- Service1 --- Service2
In such application, we have three injectors:
The application injector that can be configured using the second parameter of the bootstrap function
The AppComponent injector that can be configured using the providers attribute of this component. It can "see" elements defined in the application injector. This means if a provider isn't found in this provider, it will be automatically look for into this parent injector. If not found in the latter, a "provider not found" error will be thrown.
The ChildComponent injector that will follow the same rules than the AppComponent one. To inject elements involved in the injection chain executed forr the component, providers will be looked for first in this injector, then in the AppComponent one and finally in the application one.
This means that when trying to inject the Service1 into the ChildComponent constructor, Angular2 will look into the ChildComponent injector, then into the AppComponent one and finally into the application one.
Since Service2 needs to be injected into Service1, the same resolution processing will be done: ChildComponent injector, AppComponent one and application one.
This means that both Service1 and Service2 can be specified at each level according to your needs using the providers attribute for components and the second parameter of the bootstrap function for the application injector.
This allows to share instances of dependencies for a set of elements:
If you define a provider at the application level, the correspoding created instance will be shared by the whole application (all components, all services, ...).
If you define a provider at a component level, the instance will be shared by the component itself, its sub components and all the "services" involved in the dependency chain.
So it's very powerful and you're free to organize as you want and for your needs.
Here is the corresponding plunkr so you can play with it: https://plnkr.co/edit/PsySVcX6OKtD3A9TuAEw?p=preview.
This link from the Angular2 documentation could help you: https://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html.
Surely you do.
In Angular2, there are multiple injectors. Injectors are configured using the providers array of components. When a component has a providers array, an injector is created at that point in the tree. When components, directives, and services need to resolve their dependencies, they look up the injector tree to find them. So, we need to configure that tree with providers.
Conceptually, I like to think that there is an injector tree that overlays the component tree, but it is sparser than the component tree.
Again, conceptually, we have to configure this injector tree so that dependencies are "provided" at the appropriate places in the tree. Instead of creating a separate tree, Angular 2 reuses the component tree to do this. So even though it feels like we are configuring dependencies on the component tree, I like to think that I am configuring dependencies on the injector tree (which happens to overlay the component tree, so I have to use the components to configure it).
Clear as mud?
The reason Angular two has an injector tree is to allow for non-singleton services – i.e., the ability to create multiple instances of a particular service at different points in the injector tree. If you want Angular 1-like functionality (only singleton services), provide all of your services in your root component.
Architect your app, then go back and configure the injector tree (using components). That's how I like to think of it. If you reuse components in another project, it is very likely that the providers arrays will need to be changed, because the new project may require a different injector tree.
Well, i guess you should provide both services globally:
bootstrap(App, [service1, service2]);
or provide to component that uses them:
#Component({providers: [service1, service2]})
#Injectable decorator adds necessary metadata to track dependecies, but does not provide them.