Dependency injection in Angular 2 when a constructor has arguments - javascript

I have a typescript class representing a model and I would like instances to communicate with an API via angular's Http service.
But the constructor of the model needs arguments when creating instances. For example something super simple:
class SomeModel{
constructor(public id:number, public name:string, ){
}
I would like to inject the Http service so it is available to my instances, but it seems like the canonical way to do this commandeers the constructor with:
constructor(http:Http)
I've been digging through the Injector docs, but it's a little sparse and I haven't found anything that works. Is there a way to get a reference to a service like Http from the DI system without using the constructor pattern?

I managed to solve the same problem using angular 4. First you create new injector that uses component injector. It knows about your SomeModel class and passes modelParams as instance of SomeModelParameters class. Then you use this newly created injector to create class instance.
#Injectable()
class SomeModel {
constructor(http: Http, someModelParamters: SomeModelParameters) { }
}
export class MyComponent {
constructor(injector: Injector) {
const modelParams = new SomeModelParameters();
const injectorWithModelParams = ReflectiveInjector.resolveAndCreate(
[
SomeModel,
{ provide: SomeModelParameters, useValue: modelParams }
],
injector);
this.someModel = injectorWithModelParams.resolveAndInstantiate([SomeModel]);
}
}

update
HTTP_PROVIDERS is long gone.
HttpClientModule is the current replacement.
original
If you inject a class that has constructor parameters the #Injectable annotation needs to be added.
#Injectable()
class SomeModel{
// constructor(public id:number, public name:string, ){
// }
constructor(http:Http) {}
}
For this to work HTTP_PROVIDERS needs to be added to bootstrap(AppComponent, [HTTP_PROVIDERS]);
See also Angular2 beta - bootstrapping HTTP_PROVIDERS - "Unexpected Token <"
If you need to pass other arguments from your component, youcoud pass them using a function instead.
Another way is to create the instance manually and request Http from the injector.
export class MyComponent {
constructor(injector: Injector) {
this.someModel = new SomeModel(Injector.resolveAndCreate(Http), 1, 2);
}
}

Related

What is the purpose of a `getInstance()` method?

I am looking at a user service, my understanding is it's similar to a user service in Nest, but not really.
In it I see the following:
export class UsersService {
private usersDao: UsersDao
constructor() {
this.usersDao = UsersDao.getInstance();
}
}
static getInstance(): UsersService {
if (!UsersService.instance) {
UsersService.instance = new UsersService();
}
return UsersService.instance;
}
What is that getInstance() doing exactly? And why not just:
export class UsersService {
constructor(private usersDao: UsersDao) {}
}
What is the goal of getInstance()?
Usually this is part of the singleton pattern. Basically one class that, once instantiated, any subsequent classes will refer to that instance, rather than creating a fresh instance each time.
https://en.wikipedia.org/wiki/Singleton_pattern
Its useful for a class where something complex needs to happen when it is first constructed, but all following calls just need access to the properties.
I'd also like to mention that you can (in JavaScript specifically) export an instance, and all modules that import the module will have access to the same instance.

Angular 8: Get instance of service to another service without using constructor

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

Angular: manually instantiate class with dependency injection

I am using Angular 10.0 and I have a problem with --prod compiling.
I need to instantiate classes manually and need to support dependency injection.
The following code works fine during development to instantiate my classes:
public instantiateWithDi(parentInjector: Injector, myClass: any): any {
const reflector = ReflectiveInjector.resolveAndCreate([], parentInjector);
const newInstance = reflector.resolveAndInstantiate(myClass);
return newInstance;
}
When I build my project with --prod (or --optimization=true), then I get the following error at runtime:
ERROR Error: Cannot resolve all parameters for 'e'(?). Make sure that all the parameters are decorated with Inject or have valid type annotations and that 'e' is decorated with Injectable.
Decorating the constructor parameters of the classes with #Inject did not work either. Using injection tokens does not help as well.
The classes are already decorated with #Injectable() and in the "providers" array of their respective angular module.
I know, the ReflectiveInjector is deprecated, but simply using the get method of the injector does not work either, because it seems to cache the classes once created and does not re-instantiate them each time I call my "instantiateWithDi" method.
Example usage
I've created a small demo at stackbliz for this: https://stackblitz.com/edit/angular-plugin-mechanism?file=src/app/plugin-execution.service.ts
Basically the magic happens here (plugin-execution.service.ts):
#Injectable({
providedIn: 'root'
})
export class PluginExecutionService {
public static readonly eventListeners = [];
constructor(private injector: Injector){}
private instantiateWithDi(parentInjector: Injector, myClass: any): any {
const reflector = ReflectiveInjector.resolveAndCreate([], parentInjector);
const newInstance = reflector.resolveAndInstantiate(myClass);
return newInstance;
}
public onApplicationEvent(event: ApplicationEvent){
const injector = Injector.create({
parent: this.injector,
providers: [{
provide: ApplicationEvent,
useValue: event
}]
});
PluginExecutionService
.eventListeners
.forEach(cls => this.instantiateWithDi(injector, cls));
}
}
This allows developers to create a class and push their class into a eventListener array. It gets executed every time, an application event occurs.
See the example "plugin" some.plugin.ts in the stackblitz example.
The real usecase is of course much more complex and involves custom decorators and stuff, but that would be quite an overkill for a demo.
You see the result in the console. The "plugins" work fine as intended. But when i build it using --prod, the app does not work any longer...
Any help is very much appreciated!
Thanks,
Manuel

What is the need for #Inject and Injectable in Angular DI?

constructor(private smartphoneService: smartphoneService) { }
I can run above code without an error. But why do we use #Inject and Injectable on services, Pipes and etc..Any handful use..? why and where we need to use this..??
import { Injectable } from '#angular/core';
import {SmartphoneListComponent} from './smartphone-list/smartphone-list.component';
#Injectable()
export class smartphoneService{
smartphones: any = [
{id: '1', name: 'iPhone6', status: 'active'},
{id: '2', name: 'iPhone6S', status: 'active'},
{id: '3', name: 'iPhone7', status: 'active'},
{id: '4', name: 'iPhone7Plus', status: 'active'},
{id: '5', name: 'iPhoneX', status: 'future'}
];
constructor() { }
}
The Angular documentation provides the answer to your question:
#Injectable() marks a class as available to an injector for instantiation. Generally speaking, an injector reports an error when trying to instantiate a class that is not marked as #Injectable().
Besides that, they recommend to put the annotation on every service class for the following reasons:
Future proofing: No need to remember #Injectable() when you add a dependency later.
Consistency: All services follow the same rules, and you don't have to wonder why a decorator is missing.
Here is the link to the documentation:
https://angular.io/guide/dependency-injection#why-injectable
#Injectable
You need to use #Injectable on services that inject dependencies and will be injected using class type:
#Injectable()
class A { constructor (b: B) {} }
class C { constructor (c: C) {} }
You don't need to use #Injectable if a service doesn't inject dependencies:
class A { constructor () {} }
class C { constructor (c: C) {} }
Or it's being injected using #Inject decorator:
class A { constructor (b: B) {} }
class C { constructor (#Inject(C) c) {} }
Basically, you can use any decorator instead of #Injectable. This will work:
function Custom(target) {}
#Custom
class A { constructor (b: B) {} }
class C { constructor (c: C) {} }
This is because when Angular tries to understand what injectables to pass to a class constructor, it uses metadata of a class. And this metadata is generated by TypeScript only if you have emitDecoratorMetadata: true in tsconfig.json and any decorator is applied to a class.
#Inject
Angular doesn't use metadata generated by TypeScript when you use #Inject decorator explicitly, that's why you don't need to apply #Injectable if you use #Inject. The most common use case of #Inject is to inject object by reference, not type:
const token1 = new InjectionToken('my');
const token2 = 's';
class C { constructor (#Inject(token1) t1, #Inject(token2) t2) {} }
A Component is instantiated by Angular and it is already decorated by #Component. Adding #Inject is implied. So why force developers to use #Inject for Components?
OTOH if Angular required #Inject for it's constructor then what does it mean to have a constructor arg that is not injected? It becomes pointless arg since it is not injected and Angular has to create instances? Then we would have to enforce that every argument is marked #inject.
Services look like first class JavaScript classes. So are domain entity classes. However services don't carry the state and so it is pointless to have several instances hanging around of same service class. OTOH entities can be multiple instances of same class.
Now since these 2 types of classes look alike how do we tell Angular which class is Singleton and most importantly how do we tell Angular that we need an instance injected somewhere.
In DI we are saying I want an instance of a class.

Angular Providers

Following the angular 2 tutorial # https://angular.io/docs/ts/latest/tutorial/toh-pt4.html
In the
#Component({
})
I inserted "providers: [HeroService]" which contains the getHeroes() method.
Created a constructor:
constructor(private heroService: HeroService) {}
Now the part I don't understand is how I am able to use
this.heroService.getHeroes()
The only propertes defined in this class are:
title = 'Tour of Heroes';
heroes: Hero[];
selectedHero: Hero;
Does the providers in the #Component decorator automatically create a property to access it through this.?
The App is working, just don't know how we magically were able to access heroService through this.
The private (could also be public) in
constructor(private heroService: HeroService) {}
also creates a property heroService and assigns the value passed to the constructor. That's a TypeScript feature and is not Angular2 or DI dependent.

Categories