8
votes

I basically want to create a custom dialog component that I can utilize from anywhere in my Angular2 app regardless of where the using component is in the application tree. For simplicity lets call this my SayHello Component.

Consider the following application tree:enter image description here

So let's say i want SomeComponent.level3.component to call up the dialog in SayHello.component.

In Angular 1.x I would inject RootScope into a controller and light up a dialog that way. Now, I understand (more or less) that for Angular2 you can bubble events (with event emitters) up the component tree, but it seems tedious to bubble an event all the way from SomeComponent.level3.component up the tree and down to SayHello.component.

So I thought I would create a SayHello Service that I would inject anywhere I wanted to light up my dialog. Here is a sketch of the code I have formulated.

myApp.component.ts

import {SayHelloComponent} from "<<folder>>/sayHello.component";
import {BunchOfComponents} from "<<folder>>/bunchOfComponents";

@Component({
    directives: [SayHelloComponent],
    selector: "my-app",
    templateUrl: `<bunch-of-components>Within this component exists
                      SomeComponent.level3.component </bunch-of-components>
                      <say-hello showdialog="{{showDialog}}" message="{{message}}">
                      </say-hello>`

})
export class myAppComponent {
    showDialog = false;
    message = "";

    constructor(private sayHelloService: SayHelloService) {
        this.showDialog = sayHelloService.showDialog;
        this.message = sayHelloService.message;

    }
}

SayHelloService.ts

import {Injectable} from 'angular2/core';

@Injectable()
export class SayHelloService {
    public showDialog: boolean = false;
    public message: string ="";

    constructor() {

    }

}

SayHello.component.ts

import {Component} from "angular2/core";
import {SayHelloService} from "<<folder>>/SayHelloService";
@Component({
    directives: [],
    selector: "say-hello",
    template: "[do hello component]"
})
export class SayHelloComponent {
    @Input() showdialog: boolean;
    @Input() message: string;

       constructor(private sayHelloService: SayHelloService) {

    }
    //This idea here is to detect change in showDialog
    //If true then do an alert with the message
    ngOnChanges(changes: { [propName: string]: SimpleChange }) {
        var obj = changes["showdialog"];
        if (obj !== null) {
            if (changes["showdialog"].currentValue === true) {
                alert(this.message);
                this.sayHelloService.showDialog = false;
            }

        };
    }

}

SomeComponent.level3.component

import {Component} from "angular2/core";
import {SayHelloService} from "<<folder>>/SayelloService";

@Component({
    directives: [],
    selector: "some-component",
    template: "<button (click)='doHello()'>Do say hello</button>"
})
export class PageContactUsComponent {

    constructor(private sayHelloService: SayHelloService) {

    }


    doHello(): void {
        this.sayHelloService.message = "Hello world";
        this.sayHelloService.showDialog = true;
    }
}

appBoot.ts

import {bootstrap} from "angular2/platform/browser";
import {MyAppComponent} from "<<folder>/MyAppComponent";
import {SayHelloService} from "<<folder>>/SayHelloService";

bootstrap(MyAppComponent, [
    SayHelloService
]);

Needless to say, this doesn't work. I don't get any errors, but the SayHello.component does not detect any change in the value of 'showdialog'...so nothing happens. Any ideas as to how to do properly do this would be much appreciated.

2
Rather than (or perhaps in addition to) posting all these code snippets individually, having a plunkr with all these working parts together would make it much easier for me/us to play around with your implementation and show you what needs to changedrew moore
#drewmoore, good idea will work it in plunkr. #pixelbits, yes that is my strategy, but I am doing something wrong.brando
FYI, custom events (by event emitter) cannot be bubbled up (only DOM events can).pixelbits
Put an EventEmitter inside the service. Put a showDialog() API/method on the service that other components can call. The showDialog() method should emit() an event. Your dialog component can subscribe to the event and unhide/show itself when it receives an event. To wrap an EventEmitter in a service, see this answer.Mark Rajcok
Here is a bootstrap modal implementation that loads the content dynamically using DynamicComponentLoader. The modal component exists under the Root component, and the ExampleContent component can loaded from anywhere: plnkr.co/edit/xjXnHyHX2jrj7Nsp9h8H?p=preview. To be useful, it would need to be tweaked to support events, etc.pixelbits

2 Answers

8
votes

As mentioned in a comment above,

  • Put an Observable inside the service (note, not an EventEmitter)
  • Put a showDialog() API/method on the service that other components can call. The showDialog() method should call next() to send an event.
  • Your dialog component can subscribe to the event and unhide/show itself when it receives an event.

To wrap an Observable in a service, see this answer.

1
votes

An alternative to using a service is dynamically creating the component using ViewContainerRef/createComponent described here:

https://www.lucidchart.com/techblog/2016/07/19/building-angular-2-components-on-the-fly-a-dialog-box-example/