Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mapbox GL Popup set content with custom tag

im trying to creater a marker with popup on click, so far so good, the problem is when im trying to set the content of the popup to be my custom tag, for example

let popup = new mapboxgl.Popup()
    .setHTML("<custom-tag></custom-tag>") 

I know about the option of setDOMContent but I didn't manage to get it right... it suppose to work with document.createElement('custom-tag') so if you can help me on how to use it with custom components. thank you for your help!

like image 787
michael ehrlich Avatar asked Jan 12 '18 15:01

michael ehrlich


1 Answers

I was able to get this to work using the Angular ComponentFactoryResolver. There's a bit of setup, but once you get it working, you can use it to render any component you want (and put it anyplace you'd like...including a mapbox popup).

I'm not sure if this is still the "right" way to do this (I'm still on Angular v5) but it does work.

1) Create Dynamic Component Service (can't recall where I got this...sorry for no attribution whoever you are)

import { Injectable, Injector, ApplicationRef, ComponentFactoryResolver, ComponentRef, Type } from '@angular/core'

@Injectable()
export class DynamicComponentService {

    private compRef: ComponentRef<any>;

    constructor(private injector: Injector,
                private resolver: ComponentFactoryResolver,
                private appRef: ApplicationRef) { }


    public injectComponent<T>(component: Type<T>, propertySetter?: (type: T) => void): HTMLDivElement {
        // Remove the Component if it Already Exists
        if (this.compRef) this.compRef.destroy();

        // Resolve the Component and Create
        const compFactory = this.resolver.resolveComponentFactory(component);
        this.compRef = compFactory.create(this.injector);

        // Allow a Property Setter to be Passed in (To Set a Model Property, etc)
        if (propertySetter)
            propertySetter(this.compRef.instance);

        // Attach to Application
        this.appRef.attachView(this.compRef.hostView);

        // Create Wrapper Div and Inject Html
        let div = document.createElement('div');
        div.appendChild(this.compRef.location.nativeElement);

        // Return the Rendered DOM Element
        return div;
    }
}

2) Use the service to render your custom component in the mapbox-gl popup

import { MyCustomMapboxPopup } from "../app/components/my-custom-mapbox-popup.component"
import { DynamicComponentService } from "../services/dynamic-component";

...
// Inside a map.on("click") or wherever you want to create your popup

// Inject Component and Render Down to HTMLDivElement Object
let popupContent = this.dynamicComponentService.injectComponent(
                MyCustomMapboxPopup,
                x => x.model = new PopupModel()); // This Is where You can pass
// a Model or other Properties to your Component

 new mapboxgl.Popup({ closeOnClick: false })
     .setLngLat(...wherever you want the popup to show) 
     .setDOMContent(popupContent)
     .addTo(map);
...

Just to avoid any confusion, the custom popup component might look something like:

import { Component } from '@angular/core';

@Component({
    selector: "custom-mapbox-popup",
    templateUrl: "./my-custom-mapbox-popup.component.html"
})
export class MyCustomMapboxPopup {
    public model: PopupModel; // Model Property
}

// HTML
<div class="my-custom-popup">
    <div *ngIf="model">
        <h3>{{this.model.SomeModelProperty}}</h3>
    </div>
</div>
like image 199
mikeo Avatar answered Sep 21 '22 11:09

mikeo