Произвольное расположение компонентов рендеринга в Angular 4 / принудительное обнаружение ViewChild

Приложение My Angular 4 использует стороннюю библиотеку (mapbox) для рендеринга контента на странице. В этой библиотеке есть функция всплывающего диалогового окна, которая позволяет отображать всплывающие окна, когда пользователь щелкает булавку на карте: https://www.mapbox.com/mapbox-gl-js/example/popup-on-click/

Всплывающее окно вызывается примерно со следующим кодом:

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

Я хочу отобразить компонент Angular 4+ во всплывающем div. Вещи, которые я пробовал:

1) Привязка @ViewChild к div этого всплывающего окна.

// 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>")

childContainer никогда не назначается. Я пробовал подождать секунды после открытия всплывающего окна или вызвать detectChanges() на ChangeDetectorRef моего компонента. Я пробовал открыть всплывающее окно в NgZone.runOutsideAngular(), а затем запустить detectChanges() вручную.

Если я вручную добавлю div в свой html-шаблон, всплывающее окно отобразится, как и ожидалось, в местоположении этого div.

2) Рендеринг с использованием фабрики в div с этим идентификатором

//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);

Содержимое div ЗДЕСЬ заменяется на <!---->. Очевидно, что Angular отображает что-то в этом месте, но не сам компонент. Нужен ли мне другой инжектор?

3) Размещение элемента визуализации компонента непосредственно во всплывающем окне:

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

Он никогда не превращается в реальный компонент. Даже с такими вещами, как detectChanges() или NgZone работает.

4) Есть другие идеи? Как визуализировать компонент Angular в произвольном месте HTML, определенном вне Angular?


person Will Shaver    schedule 20.10.2017    source источник
comment
вы можете создать плункер. Я считаю, что ваше усложнение может быть легко достигнуто. Пример для дочернего модального окна с ViewChild   -  person Aravind    schedule 20.10.2017


Ответы (1)


Я смог решить эту проблему с помощью стандартного вызова .append. Мне не нравится это решение, но оно, похоже, работает и не имеет особых недостатков, поэтому я публикую его здесь на случай, если другие люди найдут этот вопрос.

Добавьте скрытый компонент на ту же страницу, что и ваша карта:

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

Привяжите всплывающее окно, по которому щелкнули, по идентификатору к свойству класса, переданному в компонент всплывающей подсказки выше

//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);
  });
}

Используйте этот компонент с добавлением:

//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);
        }
    }
}

Это работало с правильным прохождением связанных событий [click] на infopop.component.html.

person Will Shaver    schedule 26.10.2017
comment
Не могли бы вы уточнить load popup specific details and render, я действительно борюсь за это. Компонент загружается только первый раз, а после этого перестает работать - person MJK; 27.09.2019