如何在Leaflet标记的弹出窗口中生成Angular 4组件? [英] How to spawn Angular 4 component inside a Leaflet marker's popup?
问题描述
我一直是Angular 1.x的长期用户,现在我正在使用Angular 4制作新的应用程序.我仍然不掌握大多数概念,但是最终我有了一些非常不错的东西.但是,我遇到一个问题,我需要在使用Leaflet的标记弹出窗口中显示Angular 4组件(尽管在1.x中我只是使用指令).
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.
现在,在Angular 1.x中,我可以对带有内部指令(`<component>{{ text }}</component>`
)的模板使用$ compile和按钮,这样就可以了,但是Angular 4与它的AoT和编译完全不同.在运行时似乎非常困难,并且没有简单的解决方案.
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 :(
这是我在GitHub中的仓库的链接: https://github.com/darkguy2008/leaflet -angular4-issue
Here's the link of my repo in GitHub: https://github.com/darkguy2008/leaflet-angular4-issue
这个想法是在Marker(在src/app/services/map.service.ts,第38行中找到)中生成PopupComponent(或类似的东西).
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.
提前谢谢! :)
编辑
我设法解决了问题:)看到带标记的答案以获取详细信息或差异.有一些警告,并且Angular 4和Leaflet的过程有些不同,不需要太多更改: https://github.com/darkguy2008/leaflet-angular4-issue/commit/b5e3881ffc9889645f2ae7e65f4eed4d4db4779b
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! :)
推荐答案
好的,所以感谢@ghybs的建议,我再次尝试了该链接,并设法解决了问题:D. Leaflet与Google Maps略有不同(它也更短),并且建议的解决方案可能更小且更易于理解,因此这是我使用Leaflet的版本.
Alright, so thanks to @ghybs's suggestion I gave that link another try and managed to solve the issue :D. Leaflet is a bit different from Google Maps (it's also shorter) and the proposed solution there could be a bit smaller and easier to understand, so here's my version using Leaflet.
基本上,您需要将弹出组件放在主应用程序模块的entryComponents
字段中.关键内容在m.onclick()
中,在那里,我们创建一个组件,将其呈现在div
内部,然后将div
的内容传递给传单弹出容器元素.有点棘手,但有效.
Basically, you need to put your popup component in the main app module's entryComponents
field. The key stuff is in m.onclick()
, there, we create a component, render it inside a div
and then we pass that div
's content to the leaflet popup container element. A bit tricky, but it works.
我花了一些时间并将此解决方案转换为Angular 4的新$ compile.检查详细信息
I got some time and converted this solution to a new $compile for Angular 4. Check the detailed info here. Thanks @yurzui! :)
这是核心代码...其他内容(css,webpack等)与OP处于同一存储库中,简化为几个文件:
This is the core code... The other stuff (css, webpack, etc.) is in the same repo as the OP, simplified into few files: https://github.com/darkguy2008/leaflet-angular4-issue but you just need this example to make it work:
import 'leaflet';
import './main.scss';
import "reflect-metadata";
import "zone.js/dist/zone";
import "zone.js/dist/long-stack-trace-zone";
import { BrowserModule } from "@angular/platform-browser";
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
import { Component, NgModule, ComponentRef, Injector, ApplicationRef, ComponentFactoryResolver, Injectable, NgZone } from "@angular/core";
// ###########################################
// App component
// ###########################################
@Component({
selector: "app",
template: `<section class="app"><map></map></section>`
})
class AppComponent { }
// ###########################################
// Popup component
// ###########################################
@Component({
selector: "popup",
template: `<section class="popup">Popup Component! :D {{ param }}</section>`
})
class PopupComponent { }
// ###########################################
// Leaflet map service
// ###########################################
@Injectable()
class MapService {
map: any;
baseMaps: any;
markersLayer: any;
public injector: Injector;
public appRef: ApplicationRef;
public resolver: ComponentFactoryResolver;
public compRef: any;
public component: any;
counter: number;
init(selector) {
this.baseMaps = {
CartoDB: L.tileLayer("http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png", {
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> © <a href="http://cartodb.com/attributions">CartoDB</a>'
})
};
L.Icon.Default.imagePath = '.';
L.Icon.Default.mergeOptions({
iconUrl: require('leaflet/dist/images/marker-icon.png'),
shadowUrl: require('leaflet/dist/images/marker-shadow.png')
});
this.map = L.map(selector);
this.baseMaps.CartoDB.addTo(this.map);
this.map.setView([51.505, -0.09], 13);
this.markersLayer = new L.FeatureGroup(null);
this.markersLayer.clearLayers();
this.markersLayer.addTo(this.map);
}
addMarker() {
var m = L.marker([51.510, -0.09]);
m.bindTooltip('Angular 4 marker (PopupComponent)');
m.bindPopup(null);
m.on('click', (e) => {
if (this.compRef) this.compRef.destroy();
const compFactory = this.resolver.resolveComponentFactory(this.component);
this.compRef = compFactory.create(this.injector);
this.compRef.instance.param = 0;
setInterval(() => this.compRef.instance.param++, 1000);
this.appRef.attachView(this.compRef.hostView);
this.compRef.onDestroy(() => {
this.appRef.detachView(this.compRef.hostView);
});
let div = document.createElement('div');
div.appendChild(this.compRef.location.nativeElement);
m.setPopupContent(div);
});
this.markersLayer.addLayer(m);
return m;
}
}
// ###########################################
// Map component. These imports must be made
// here, they can't be in a service as they
// seem to depend on being loaded inside a
// component.
// ###########################################
@Component({
selector: "map",
template: `<section class="map"><div id="map"></div></section>`,
})
class MapComponent {
marker: any;
compRef: ComponentRef<PopupComponent>;
constructor(
private mapService: MapService,
private injector: Injector,
private appRef: ApplicationRef,
private resolver: ComponentFactoryResolver
) { }
ngOnInit() {
this.mapService.init('map');
this.mapService.component = PopupComponent;
this.mapService.appRef = this.appRef;
this.mapService.compRef = this.compRef;
this.mapService.injector = this.injector;
this.mapService.resolver = this.resolver;
this.marker = this.mapService.addMarker();
}
}
// ###########################################
// Main module
// ###########################################
@NgModule({
imports: [
BrowserModule
],
providers: [
MapService
],
declarations: [
AppComponent,
MapComponent,
PopupComponent
],
entryComponents: [
PopupComponent
],
bootstrap: [AppComponent]
})
class AppModule { }
platformBrowserDynamic().bootstrapModule(AppModule);
这篇关于如何在Leaflet标记的弹出窗口中生成Angular 4组件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!