- PVSM.RU - https://www.pvsm.ru -
В процессе работы над проектом на Angular 2 с использованием карт возникла следующая задача: требуется срендерить свой ангуляровский компонент в стандартный popup leaflet’а. В данной статье динамический рендеринг компонентов будет рассмотрен в разрезе именно этой задачи, однако аналогичным образом можно использовать эту информацию в собственных кейсах.
Начальный проект находится по ссылке [1]. Это angular 2+ приложение, к которому подключена библиотека для работ с картой leaflet.js. В MapService есть методы для создания карты, добавления маркеров на неё и центровки на маркерах. MapComponent – компонент для отображения карты. Для сборки проекта используется webpack 2. Если запустить приложение, то перед нами появится карта с маркером, к которому привязан popup следующего вида:
marker.bindPopup(`
<h3>Leaflet PopUp</h1>
<p>Some text</p>
<p *ngIf="false">Should be deleted from DOM if it was angular component because of ngIf = false<p>
`);
Кликнем на него и увидим следующую картину:
В DOM находится элемент с текстом “Should be deleted from DOM...”, который хотелось бы удалить используя *ngIf, однако в popup просто так нельзя записать код ангуляра, чтобы он тут же заработал. Именно здесь на сцену выходит динамический рендеринг компонентов ангуляра.
Для начала создадим компонент, который мы хотим динамически рендерить:
@Component({
selector: 'custom-popup',
template: require('./custom-popup.component.html')
})
export class CustomPopUpComponent {
public inputData: any;
private title: string = 'Angular component';
private array: Array<string> = ['this', 'array', 'was viewed', 'by', 'ngFor!'];
}
Его template:
<div>
<h1>{{title}}</h1>
<p>{{inputData}}</p>
<p *ngFor="let text of array">{{text}}</p>
<p *ngIf="false">Should be deleted from DOM if it was angular component because of ngIf = false</p>
</div>
Далее создадим новый сервис dynamic-render.service.ts:
@Injectable()
export class RenderService {
private componentRef: ComponentRef<any>;
constructor(private ngZone: NgZone,
private injector: Injector,
private appRef: ApplicationRef,
private componentFactoryResolver: ComponentFactoryResolver) { }
public attachCustomPopUpsToMap(map: Map) {
this.ngZone.run(() => {
map.on("popupopen",
(e: any) => {
const popup = e.popup;
const compFactory = this.componentFactoryResolver.resolveComponentFactory(popup.options.popupComponentType);
this.componentRef = compFactory.create(this.injector);
this.componentRef.instance.geoObject = popup.options.object;
this.appRef.attachView(this.componentRef.hostView);
let div = document.createElement('div');
div.appendChild(this.componentRef.location.nativeElement);
popup.setContent(div);
});
});
}
}
Так как addListener запускается вне зоны ангуляра, нам нужно самим вручную добавить его туда. Таким образом каждый раз при открытии popup’а вызывается componentFactory, которая создаёт компонент, который мы прокинули в поле options. Далее мы можем с помощью instance этого компонента записать в его поля данные, которые мы так же можем прокинуть в options popup’а. В данном примере мы назначаем полю inputData компонента данные из options.data. Затем создаем div элемент, к которому прикрепляем наш только что созданный компонент и назначаем его в качестве контента popup’у.
Замечание: этот код написан для angular 2.3.0+. Для более ранних версий это решение будет выглядеть следующим образом. Вместо
this.appRef.attachView(this.componentRef.hostView);
нужно будет написать
if (this.appRef['attachView']) {
this.appRef['attachView'](this.componentRef.hostView);
this.componentRef.onDestroy(() => {
this.appRef['detachView'](this.componentRef.hostView);
});
}
else {
this.appRef['registerChangeDetector'](this.componentRef.changeDetectorRef);
this.componentRef.onDestroy(() => {
this.appRef['unregisterChangeDetector'](this.componentRef.changeDetectorRef);
});
}
Запровайдим RenderService в MapModule. Также обязательно нужно добавить в MapModule в declarations и entryComponents наш CustomPopUpComponent. Вызовем renderService и добавим возможность для нашего элемента карты рендерить в popup’ах ангуляровские компоненты, после чего прикрепим к маркеру кастомный компонент:
this.renderService.attachCustomPopUpsToMap(this.mapService.getMap());
let options = {
data: 'you can provide here anything you want',
popupComponentType: CustomPopUpComponent
};
let myPopUp = L.popup(options);
marker.bindPopup(myPopUp);
В поле data прокинем данные для компонента, в popupComponentType – сам компонент. Такую конструкцию можно обернуть в интерфейс для удобства использования, но в рамках данного примера делать этого не будем, статья не об этом. Для корректного отображения немного подправим стили, после чего можно запускать приложение. Кликнув по маркеру, видим, что наш компонент среднерился в popup’е leaflet:
Нам удалось значительно расширить функционал стандратных popup’ов leaflet в связке с angular 2+. В качестве бонуса наши компоненты получают анимацию открытия/закрытия, изменение размера при зуме и другие стандартные вещи leaflet.
Исходники проекта, в котором реализовано всё описанное в статье находятся здесь [1].
Автор: bodryi
Источник [2]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/leaflet/258311
Ссылки в тексте:
[1] ссылке: https://github.com/bodryi/angular2-dynamic-component-rendering/tree/master/angular2-leaflet-start
[2] Источник: https://habrahabr.ru/post/331298/?utm_source=habrahabr&utm_medium=rss&utm_campaign=sandbox
Нажмите здесь для печати.