How to spawn Angular 4 component inside a Leaflet marker's popup?

Viewed 6841

I've been a long Angular 1.x user and now I'm working on make a new app using Angular 4. I still don't grasp most of the concepts but I finally have something working really nice. However, I'm having an issue where I need to display an Angular 4 component (although in 1.x I just used directives) inside a Marker's popup using Leaflet.

Now, in Angular 1.x I could just use $compile against a template with the directive inside it (`<component>{{ text }}</component>`) with buttons and such and it would work, but Angular 4 is totally different with its AoT thing and compiling at runtime seems to be really hard and there's no easy solution for it.

I asked a question here and the author says I could use a directive. I'm not sure if this is the correct approach or even how to mix my own code with his proposed solution... so I made a small npm-based project with Angular 4 and Leaflet already set up in case you know how to help me or want to give it a try (I greatly appreciate it!). I've been banging my head around this for maybe a week and I'm really tired of trying a lot of alternatives without success :(

Here's the link of my repo in GitHub: https://github.com/darkguy2008/leaflet-angular4-issue

The idea is to spawn PopupComponent (or anything similar to it) inside a Marker, code which you can find in src/app/services/map.service.ts, line 38.

Thanks in advance! :)

EDIT

I managed to solve it :) see the marked answer for details, or this diff. There are a few caveats and the procedure for Angular 4 and Leaflet is a bit different and it doesn't require as much changes: https://github.com/darkguy2008/leaflet-angular4-issue/commit/b5e3881ffc9889645f2ae7e65f4eed4d4db6779b

I've also made a Custom Compile Service out of this solution explained here and uploaded to the same GitHub repo. Thanks @yurzui! :)

2 Answers
  1. You will need to create a component for the popup content in case you don't have it already. Lets assume it is called MycustomPopupComponent.
  2. Add your component to the app.module.ts in the entry components array. This is needed when creating components dynamically:
   entryComponents: [
      ...,
      MycustomPopupComponent
   ],
  1. In your screen, add these two dependencies to the constructor:
constructor(
    ...
    private componentFactoryResolver: ComponentFactoryResolver,
    private injector: Injector
) {
  1. Now in that screen we can define a function that creates the component dynamically.
private createCustomPopup() { 
    const factory = this.componentFactoryResolver.resolveComponentFactory(MycustomPopupComponent);
    const component = factory.create(this.injector);

    //Set the component inputs manually 
    component.instance.someinput1 = "example";
    component.instance.someinput2 = "example";

    //Subscribe to the components outputs manually (if any)        
    component.instance.someoutput.subscribe(() => console.log("output handler fired"));

    //Manually invoke change detection, automatic wont work, but this is Ok if the component doesn't change
    component.changeDetectorRef.detectChanges();

    return component.location.nativeElement;
}
  1. Finally, when creating the Leaflet popup, pass that function as parameter to bindPopup. That function also accepts a second parameter with options.:
const marker = L.marker([latitude, longitude]).addTo(this.map);
marker.bindPopup(() => this.createCustomPopup()).openPopup();
Related