Angular 2 dependency injection: Host as parent class - dependency-injection

I have a quick question:
Can i bind Interface or abstract class (or just a parent class atleast) as #Host() of component? Seems like DI system can't resolve polymorphism.
I have interface like:
export interface Component {
}
and child component from this interface:
...
export class SomeComponent implements Component {
...
}
Now i want to create Directive, and use Component as host even when i put this directive on SomeComponent.
like:
constructor(private host: Component) { }

In fact, there is nothing at runtime corresponding TypeScript interfaces. This means that you can't use them as types.
If you try this in your service:
export interface SomeInterface {
someMethod();
}
export class HeroService implements SomeInterface {
(...)
}
We will have undefined when trying to import it from another module:
import {HeroService,SomeInterface} from './hello.service';
console.log('service = '+HeroService); // <---- not null
console.log('interface = '+SomeInterface); // <---- undefined
Here is a plunkr describing this: https://plnkr.co/edit/RT59B0tw40lnq85XMMi7?p=preview.
Hope it helps you,
Thierry

Related

How can I write to a global variable in Aurelia?

I'm writing an Aurelia application and I have registered an instance of a class inside the aurelia container. Now, when I authenticate a user, that instance has to be modified. How do I modify the original instance from anywhere in my code?
If you want to follow the dependency injection pattern, you need to inject that instance into any class that wants to modify the instance.
import { inject } from 'aurelia-framework'
import { MyClass } from './my-class';
#inject(MyClass)
export class MyViewModel {
constructor(MyClass) {
this.myClass = MyClass;
}
somethingSpecial() {
this.myClass.foo = 'bar';
}
}
How do I modify the original instance from anywhere in my code
Best that put the instance in a module. And then also expose functions that modify the instance. e.g.
let foo = {}
export function getFoo(){return foo}
export function setFoo(bar){ /* do something */}

Angular2 - Use Dependency Injection outside Components

In my app I use DI to pass information about the UserLogged around the different Components that need such info.
This means that I have a main.ts class like this
import {AppComponent} from './app.component';
import {UserLogged} from './userLogged'
bootstrap(AppComponent, [UserLogged]);
and the components that need to use the instance of UserLogged have a constructor like this
constructor(private _user: UserLogged)
Now I would like to use the same instance of UserLogged also in simple TypeScript classes (which are not #Component). Is this possible? In other words, can I get hold of the same instance of UserLogged injected by DI also if I am outside a #Component?
This constructor also works for services (other classes created by DI)
bootstrap(AppComponent, [OtherClass, UserLoggged]);
#Injectable()
export class UserLogged {
log(text) {
console.log(text);
}
}
#Injectable()
export class OtherClass {
constructor(private _user: UserLogged) {}
}
class SomeComponent {
constructor(private otherClass:OtherClass) {
this.otherClass._user.log('xxx');
}
}
If you create these classes using new SomeClass() then you can inject it like
class SomeComponent {
constructor(private _injector:Injector) {
let userLog = this._injector.get(UserLogged);
new SomeClass(userLog);
}
}
In the file where you bootstrap angular:
import { AppComponent } from './app.component';
import { UserLogged } from './userLogged';
declare global {
var injector: Injector;
}
bootstrap(AppComponent, [UserLogged]).then((appRef) => {
injector = appRef.injector;
});
In your other file:
import { UserLogged } from '../path/to/userLogged';
class TestClass {
private userLogged: UserLogged;
constructor() {
this.userLogged = injector.get(UserLogged);
}
}
To be able to use dependency injection in classes you need to have a decorator, the #Injectable one.
#Injectable()
export class SomeClass {
constructor(private dep:SomeDependency) {
}
}
The name Injectable isn't really self-explanatory. It's to make possible the injection within the class (and not into another class).
The provider for the SomeDependency class need to be visible from the component that initiates the call.
See this question for more details about dependency injection and hierarchical injectors:
What's the best way to inject one service into another in angular 2 (Beta)?
Im taking a wild guess here, but do you mean to say you want to Inject the class with having to use providers : [UserLogged] ?
If that is the case, this will work
providers: [ provide(UserLogged, {useClass: UserLogged} ]
add the above to your bootstrap and you are good to go when you 'do not want to use #Component'
sample.ts
export class Sample{
constructor(private ulog : UserLogged){}
}
In your case the bootstrap would be :
import {provide} from 'angular2/core';
import {HTTP_PROVIDERS} from 'angular2/http';
bootstrap(AppComponent,[HTTP_PROVIDERS,provide(UserLogged, { useClass : UserLogged})]);
Ive added the HTTP_PROVIDERS to demonstrate how to add multiple providers.
Cheers!

Angular 2 DI in base Component

Let's say I have a Base Component -
export class BaseComponent {
public constructor(public myService: MyService) {
}
}
And a Derived Component -
export class DerivedComponent extends BaseComponent {
public constructor(public myService: MyService) {
super(myService);
}
}
But I only really need the myService dependency in BaseComponent. Is there any way to avoid having to add the extra constructor to DerivedComponent?
Removing the dependency from the DerivedComponent seems to result in it not being injected.
There are already similar questions with extensive discussion.
The answer is basically - no, you can't.
https://github.com/angular/angular/issues/5675
https://github.com/angular/angular/issues/5155
How to extend a component with dependency injection in Angular 2?
Haven't found the others.
you can't remove the constructor from the DerivedComponent class. But with your code, you have two property named 'myService' in DerivedComponent class. You can simplify the constructor :
export class DerivedComponent {
public constructor(myService: MyService) {
super(myService);
}
}

Binding a class to an interface

Using typescript, I can easily bind classes to themselves:
bootstrap(MyAppComponent, [MyClass]);
However, I would like to bind my class to an interface, like such:
boostrap(MyAppComponent, [???]);
such that I can inject it as follows:
class MyAppComponent {
constructor(my_class : IMyClass){
}
};
Is this possible in Angular2? If yes, how to I have to specify the binding?
To make it short the problem is that Interfaces disappear when typescript is compiled. So you'd have to use #Inject with a string.
Or there's another option, if you check the last article of Victor Savkin you can find this in the comments :
Some background. In TypeScript, interfaces are structural and are not retained at runtime. So you have to use ILoginService as follows:
constructor(#Inject("ILoginService") s:ILoginService).
You don't have to use a string - any object can be passed in there. We actually provide an object called OpaqueToken that can be used for this purpose.
interface ILoginService { login(credentials);}
const ILoginService = new OpaqueToken("LoginService");
can be used like this:
constructor(#Inject(ILoginService) s:ILoginService).
I dont know if it is possible with interface as interface will not be available at runtime (javascript does not know about interface).
But it can be done using abstract classes.
//abstract-parent-service.ts
export class DatabaseService{
getService: ()=>string;
}
//hibernate.service.ts
import {DatabaseService} from "./abstract-parent-service";
export class HibernateService implements DatabaseService{
constructor() { }
getService() {
return "i am hibernate";
}
}
//jdbc.service.ts
import {DatabaseService} from "./abstract-parent-service";
export class JDBCService implements DatabaseService{
constructor() { }
getService() {
return "i am Jdbc";
}
}
//cmp-a.component.ts
import {DatabaseService} from "./abstract-parent-service";
import {HibernateService} from "./hibernate.service";
#Component({
selector: 'cmp-a',
template: `<h1>Hello Hibernate</h1>`,
providers: [{provide: DatabaseService, useClass: HibernateService}]
})
export class CmpAComponent {
constructor (private databaseService: DatabaseService) {
console.log("Database implementation in CompA :"+this.databaseService.getService());
}
}
//cmp-b.component.ts
import {DatabaseService} from "./abstract-parent-service";
import {HibernateService} from "./hibernate.service";
#Component({
selector: 'cmp-b',
template: `<h1>Hello Jdbc</h1>`,
providers: [{provide: DatabaseService, useClass: JDBCService}]
})
export class CmpAComponent {
constructor (private databaseService: DatabaseService) {
console.log("Database implementation in CompA :"+this.databaseService.getService());
}
}
But the problem with this implementation is HibernateService and
JDBCService are not able to extend any other class now because they
have already got married with DatabaseService.
class A{
constructor(){
console.log("in A");
}
}
class B extends A{
constructor(){
super();
console.log("in B");
}
}
class C extends A{
constructor(){
super();
console.log("in C");
}
}
let c = new C();
//This thing is not possible in typescript
class D extends B, C{//error: Classes can only extend a single class
constructor(){
super();// which constructor B or C
console.log("in D");
}
}
If you are using this pattern for DI, make it sure that your child class services are not going to extend any other functionality in future.

DI - No provider found for EventBus

Using Angular Dart, I define an event bus like this:
class MyModule extends Module {
MyModule() {
bind(EventBus, toImplementation: EventBus);
...
}
}
When I want to inject this event bus into a component by simply doing:
class MyComponent {
final EventBus _eventBus;
MyComponent(this._eventBus) {}
}
I am getting the error:
No provider found for EventBus!
I have no idea how to debug this...
The event bus is an external library, which looks like:
library event_bus;
import 'dart:async';
#MirrorsUsed(symbols: '*') // Do not keep any names.
import 'dart:mirrors';
class EventBus {
StreamController _streamController;
EventBus({bool sync: false}) {
_streamController = new StreamController.broadcast(sync: sync);
}
...
}
Any help welcome... thanks!
I'm a bit late, but in this case - when you can't put easily an #Injectable() annotation on the class - the simplest solution is to give the injector a value :
bind(EventBus, toValue: new EventBus());

Resources