2017-10-20 7 views
1

Mon application Angular 4 utilise une bibliothèque tierce (mapbox) pour afficher du contenu sur une page. Cette bibliothèque dispose d'une fonctionnalité de dialogue pop-up qui permet de popups à afficher lorsqu'un utilisateur clique sur une broche sur la carte: https://www.mapbox.com/mapbox-gl-js/example/popup-on-click/Emplacement arbitraire Rendu de composant dans la détection Angular 4/Force ViewChild

La fenêtre contextuelle est appelée avec quelque chose comme le code suivant:

map.on('click', 'places', function (e) { 
    new mapboxgl.Popup() 
     .setLngLat(e.features[0].geometry.coordinates) 
     .setHTML('<div>this is a popup</div>') 
     .addTo(map); 
}); 

Je veux pour rendre un composant Angular 4+ dans la div popup. Ce que j'ai essayé:

1) Associer un @ViewChild à la div de ce popup.

// in the component that holds the map component 
@ViewChild("pop", { read: ViewContainerRef }) childContainer: ViewContainerRef; 

//when the user clicks the popup 
popup.setHTML("<div #pop></div>") 

Le childContainer est jamais affecté. J'ai essayé d'attendre quelques secondes après que le popup est ouvert, ou en appelant detectChanges() sur ChangeDetectorRef de mon composant. J'ai essayé d'ouvrir le popup dans un NgZone.runOutsideAngular() puis de lancer detectChanges() manuellement.

Si j'ajoute manuellement la div à mon modèle html, la popup apparaît comme prévu à l'emplacement de cette div.

2) rendu à l'aide d'une usine à une div avec cet ID

//when the user clicks the popup 
popup.setHTML("<div id="pop">HERE</div>") 

const element = this.elRef.nativeElement.querySelector("#pop"); //this is found 
const factory = this.componentFactoryResolver.resolveComponentFactory(PopupComponent); 
const component = factory.create(this.viewContainerRef.injector, null, element); 

Le contenu du div ICI sont remplacés par <!---->. Clairement Angular rend quelque chose à cet endroit, mais pas le composant réel. Ai-je besoin d'un injecteur différent?

3) Placer le composant élément rendu directement dans le menu contextuel:

popup.setHTML("<popup-component></popup-component>") 

Il ne se transforme en une composante réelle. Même avec des choses comme detectChanges() ou NgZone fonctionne.

4) Avez-vous d'autres idées? Comment rendre un composant angulaire à un emplacement html arbitraire défini en dehors de Angular?

+0

pouvez-vous créer un plunker.Je crois que votre complication peut être facilement réalisée. Exemple de [** modal enfant avec ViewChild **] (https://stackoverflow.com/questions/42735858/ng2-bootstrap-show-hide-modal-as-child-component/42736058#42736058) – Aravind

Répondre

0

J'ai été en mesure de résoudre ce problème en utilisant un appel standard .append. Je n'aime pas cette solution, mais elle semble fonctionner et ne pas avoir trop d'inconvénients, alors je l'affiche ici au cas où d'autres trouveraient cette question.

Ajouter un élément caché à la même page que votre carte:

//map.component.html 
<div id="map"></div> 
<div style="display:none"> 
    <info-popup #infopop [id]="selectedId" [renderTo]="popupLocation"></info-popup>  
</div> 

Bind le menu contextuel cliqué par ID à une propriété de classe est passé à l'info-pop composant ci-dessus

//map.component.ts 
popupLocation: any; 
selectedId: any; 
initializeMap(): void { 
    this.map.on("click", "pinLayer", (e: MapMouseFeaturesEvent) => { 
    this.selectedId = e.features[0].properties["id"]; // assumes a distinct id per pin 
    new mapboxgl.Popup({ closeButton: false }) 
      .setLngLat(e.lngLat) 
      .setHTML("<div id='popupLocation'></div>") 
      .addTo(this.map); 
    this.popupLocation = this.elementRef.nativeElement.querySelector("#popupLocation); 
    }); 
} 

utilisation que composant avec append:

//infopop.component.ts 
export class InfoPopupComponent implements OnChanges { 

    @Input() id: string; 
    @Input() renderTo: any; 
constructor(private elementRef: ElementRef) { } 


    ngOnChanges(changes: SimpleChanges): void { 
     if ("id" in changes) { 
      // load popup specific details and render 
     } 
     if (this.renderTo) { 
      this.renderTo.append(this.elementRef.nativeElement); 
     } 
    } 
} 

Ceci a fonctionné avec les événements [click] liés sur infopop.component.ht ml passant correctement.