dependency injection for interfaces - dart

I have a component which contains a generic popup, it implements therefore the interface PopupParent
#Injectable()
#Component(
//...
)
class SubjectListComponent implements OnInit, PopupParent {
}
The generic class InfoPopup needs to deal abstractly with its parent (that implements PopupParent), so I would like to take get the parent injected by its interface (instead of being injected by its concrete class SubjectListComponent)
class InfoPopup {
final PopupParent _parent;
InfoPopup(this._parent);
//...non relevant code
}
The issue is that the SubjectListComponent was registred by class in the injector, so the injector won't find what to inject into the InfoPopup class.
If I try to declare my SubjectListComponent manually, I found that it has to be done in the providers constants. but I still don't have my instance of SubjectListComponent when I declare my component...
How could I do that?
I also tried to pass the parent to an #Input:
#Component(
selector: 'info-popup',
templateUrl: 'info_popup.html',
styleUrls: const ['info_popup.css'],
)
class InfoPopup {
#Input()
final PopupParent parent;
InfoPopup(this._parent);
//...non relevant code
}
But then I got stuck on how to inject the this instance from the component client :
subject_list_comp.html:
<div>
<info-popup [parent]="this"></info-popup>
</div>
since dart angular doesn't recognize this as a keyword, but it searches for a property called this in SubjectListComponent.dart
Two issues were created for this question:
https://github.com/dart-lang/site-webdev/issues/514
https://github.com/dart-lang/site-webdev/issues/515

This can be accomplished by providing aliases. multi: true allows to add more than one alias. There is no way to make automatically derive the interfaces.
#Component(
providers: [
const Provider(PopupParent, useExisting: SubjectListComponent, multi: true),
const Provider(PopupParent, useExisting: FooComponent, multi: true)
]
)
class InfoPoupup ...
update
To make
[parent]="this"
work, you could add a getter to the component
get self => this;
and then use
[parent]="self"

Related

Can I inject a service into CanActivate?

Unlike the other hooks, CanActivate is a function separated from the component.
import 'dart:async';
import 'package:angular2/core.dart';
import 'package:angular2/router.dart';
#Component(selector: 'my-app', template: '<h1>Hello World!</h1>')
#CanActivate(someGuard)
class HelloComponent {}
FutureOr<bool> someGuard(ComponentInstruction next, ComponentInstruction prev) {
return true;
}
AFAIK, it is possible to intercept the root injector at bootstrap time and assign it to a global variable, so that I can access it anywhere. IMO, that sounds like a hack and I don't like it. Is there a proper way to inject a service in CanActivate hook?
Angulars DI only supports constructor injection. #CanActivate() is an annotation and therefore doesn't support DI.
There is a workaround though.
Create a variable in a library and import it
Injector injector;
assign the injector when the application is created
bootstrap(App, [
Auth,
HTTP_PROVIDERS,
ROUTER_PROVIDERS,
provide(LocationStrategy, useClass: HashLocationStrategy)
]).then((appRef: ComponentRef) => {
// store a reference to the application injector
injector = appRef.injector;
});
then you can import the library with the injector and access it from code someGuard()

Angular2 - inject singleton Service into Directive

I have problem with injecting singleton service into a directive.
I have service:
#Injectable()
export class AuthService{ ... }
I put it into bootstrapper.
bootstrap(AppComponent, [AuthService, ...]);
I made directive, that protects my component:
#Directive({
selector: '[protected]'
})
export class ProtectedDirective {
constructor(private authService:AuthService) { ... }
}
... and added to one of components
#Component({
selector: 'dashboard',
directives: [ProtectedDirective],
template: '<div protected></div',
})
export class DashboardCmp { }
In console i see an error:
ORIGINAL EXCEPTION: No provider for AuthService!
If I add a provider to DashboardCmp, everything works fine, but it's not a singleton service. I set its properties in other component and I don't see them when I'm in directive.
I resolved my problem. Everything was fine but
import {AuthService} from '../services/auth.service'; (in protected.directive.ts)
is not equal to
import {AuthService} from '../Services/auth.service'; ( in main.ts)
Yes, it's stupid, but it made the dependency injection impossible.

Angular2 Beta dependency injection

I have a NavBar Component which loads the QApi Service, the QApi Service loads the UserService, but I get the following error:
EXCEPTION: No provider for UserService! (NavBarComponent -> QApi -> UserService)
Either I simply don't get the concept of dependency injection, I made a stupid error, or this is just way to complicated compared to native development... Thanks for your help.
Here my code:
UserService:
import {Injectable} from 'angular2/core';
//import {User} from '../data-source-mocks/users';
#Injectable()
export class UserService {
public isAuthenticated = true;
}
QApi Service:
import {Injectable} from 'angular2/core';
import {UserService} from '../user/user.service';
#Injectable()
export class QApi {
constructor(private _userService: UserService) {}
}
NavBar Component:
import {Component} from 'angular2/core';
import {QApi} from '../../services/q-api/q-api';
#Component({
selector: 'nav-bar',
template: `Test NavBar`,
providers: [QApi]
})
export class NavBarComponent {
private _isAuthenticated = false;
constructor(private _QApi: QApi) {}
}
EDIT:
First of all: Thanks for alle the great answers each and every single one helped me to understand dependency injection better, especially this article: https://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html
I changed my QApi class to this:
import {Injectable, Inject, Injector} from 'angular2/core';
import {UserService} from '../user/user.service';
import {CardService} from '../card/card.service';
#Injectable()
export class QApi {
constructor() {
var _injector = Injector.resolveAndCreate([UserService,
CardService]);
this.userService = _injector.get(UserService);
this.cardService = _injector.get(CardService);
}
}
Now it works like I hoped it would. Cant thank you guys enough!!
Add UserService to the component providers:
#Component({
selector: 'nav-bar',
template: `Test NavBar`,
providers: [QApi, UserService] // <- add UserService here
})
export class NavBarComponent { /* ... */ }
Here are two good articles to better understand Angular2 Dependency Injection:
blog.thoughtram.io: Dependency Injection in Angular2
blog.thoughtram.io: Injecting services in services in Angular 2
In fact both previous responses are true! ;-)
You need to define the services:
Application level. Within the second parameter of the bootstrap function. It contains the list of the providers that are available for the whole application.
bootstrap(App, [UserService, QApi, ...]);
Component level. Within the providers attribute of the Component annotation. In this case, this is only configured for this component and you need to define this for each component where the QApi service.
#Component({
selector: 'nav-bar',
template: `Test NavBar`,
providers: [QApi, UserService]
})
You also mix things. I mean you can put the UserService provider at the application level and QApi at the component level. In fact what is important is that Angular can find providers for all the involved elements in the processing chaining (with dependency injection). They can come from either component level (1st) or application level (2nd).
Hope that it gives you some additional hints following alexpods and MichaelOryl great answers ;-)
Thierry
List the services in your bootstrap call (wherever you are handling that). Something like the following should work:
bootstrap(App, [UserService, QApi, COMMON_DIRECTIVES, ROUTER_DIRECTIVES, ROUTER_PROVIDERS, HTTP_PROVIDERS]);
providers// directives added here are available to all children
Then you will have a single instance of each of those services available to the rest of your application.

Angular2 Inject components into other components

I'm messing around with Angular2 and I'm wanting the ability to inject one component into another based on the bootstrapped bindings.
class HelloComponent {
name: string;
}
#Component({
selector: 'hello'
}
#View({
template: `<h3>Hello {{ name }}</h3>`
})
class HelloBobComponent extends HelloComponent {
constructor() {
this.name = 'Bob';
}
}
#Component({
selector: 'app'
}
#View({
directives: [HelloComponent]
template: `<h1>Welcome to my Angular2 app</h1>
<hello></hello>`
}
class AppComponent {
}
bootstrap(AppComponent, [
bind(HelloComponent).toClass(HelloBobComponent)
]);
Here I'm using HelloComponent as a token that I want Angular2's Injector to resolve HelloBobComponent. I'm doing this so that I can swap components in and out based on the current app configuration. The above example obviously doesn't work. Is this possible using one of the frameworks decorators? I haven't found an answer yet digging though blogs or the source.
edit: To clarify, how do I get the directives property on the View decorator to treat HelloComponent as a di token instead of a type.
This is currently not supported as of alpha37. The compiler resolves directives passed in the View decorator by either type or binding but does not look up from the parent injector.
For example:
#View({
url: '...',
directives: [
Directive1,
bind(Directive2).toClass(Directive2Impl),
]
})
The intention for the "directives" property here was only to prevent selector naming collision. Later bind support was added to aid in testing.
The only solution I can think of without editing the compiler function would be to maintain an external Injector and resolve types on component declaration.

Wait for dom ready without useShadowDom

I want to wait until my component is fully loaded. The current approach would be to implement the ShadowRootAware interface. However this does not work, if the component disables the use of shadow dom:
#Component(
selector: 'travel-step',
templateUrl: 'packages/TravelPlanner/travelstep/travel_step_component.html',
useShadowDom: false,
publishAs: 'cmp')
class TravelStepComponent extends AttachAware{
I need to disable the usage of ShadowDom, because I want to use styles from my parent object (e.g. Bootstrap). Is there another way to wait for the dom to be ready?
I want to reference a file upload input. At the moment (angular v.012) there seems to be no other way to upload a file.
You can implement ShadowRootAware interface. For example:
class NgFreeTree implements ShadowRootAware {
void onShadowRoot(ShadowRoot shadowRoot) { ... }
}
It should work regardless of useShadowDom attribute.
It does not give you the error message if you use the following signature:
void onShadowRoot(Node n) {
HtmlElement element = n;
...
}

Resources