如何在Leaflet标记的弹出窗口中生成Angular 4组件? [英] How to spawn Angular 4 component inside a Leaflet marker's popup?

查看:72
本文介绍了如何在Leaflet标记的弹出窗口中生成Angular 4组件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直是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: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> &copy; <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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆