Reputation: 4822
Use case: I am building a toast notification service, ToastService
. I want to be able to inject this service into components to create new toasts.
I have created a Toast
component which represents a single notification:
@Component({
selector: 'toast',
template: `<div>This is a notification</div>`
})
export class Toast {}
And the I want to be able to call toastService.show(myMessage);
to display a toast from one of my components.
So far my ToastService
looks like this:
@Injectable()
export class ToastService{
constructor(private loader: DynamicComponentLoader) {}
show(message: string) {
this.loader.loadNextToLocation(
Toast,
/* location: ElementRef - how do I get one? I want to load it into the body */
)
.then((ref: ComponentRef) => {
console.log('loaded toast!');
});
}
}
I would ideally like to load the toast as a child of the body
element, to ensure it is always on top of the rest of the app elements.
How is this possible? Or is there a better approach? I tried to figure out how they do it in the angular2 material repo, but it is just too hard for me to understand.
Upvotes: 1
Views: 534
Reputation: 42669
Look at angular-modal project. The requirements for modal are similar to modal dialog.
Specifically check modal.ts file, how open
method gets hold of the root component to show a dialog.
public open(componentType: FunctionConstructor, bindings: ResolvedProvider[],
config?: ModalConfig): Promise<ModalDialogInstance> {
// TODO: appRef.injector.get(APP_COMPONENT) Doesn't work.
// When it does replace with the hack below.
//let myElementRef = this.appRef.injector.get(APP_COMPONENT).location;
let elementRef: ElementRef = (<any>this.appRef)._rootComponents[0].location;
return this.openInside(componentType, elementRef, null, bindings, config);
}
appRef
is ApplicationRef
class injected in costructor and stores the reference to the running application.
constructor(private componentLoader: DynamicComponentLoader, private appRef: ApplicationRef,
@Optional() defaultConfig: ModalConfig) {
Upvotes: 1
Reputation: 657308
I wouldn't try to get ElementRef
into a service. You wouldn't even know if the ElementRef
is still valid when you try to use it.
Instead I would add a Toast
component "somewhere" that listens to updates from an Observable
in ToastService
and displays the components it gets passed from the Observable
as events.
If you want the Toast
component to be added outside of your AppComponent
you can bootstrap it as a distinct Angular application.
In this case you need to create the shared service instance before bootstrapping and provide the same instance to both "applications" like:
let toastService = new ToastService();
bootstrap(AppComponent, [provide(ToastService, {useValue: toastService})]);
bootstrap(Toast, [provide(ToastService, {useValue: toastService})]);
Upvotes: 1