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

查看:28
本文介绍了如何在 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 中,我可以对带有指令的模板使用 $compile (`<component>{{ text }}</component>`)按钮之类的,它会工作,但 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.

我问了一个问题 here 并且作者说我可以使用指令.我不确定这是否是正确的方法,甚至不确定如何将我自己的代码与他提出的解决方案混合在一起......所以我制作了一个基于 npm 的小型项目,其中已经设置了 Angular 4 和 Leaflet,以防你知道如何提供帮助我或想试一试(我非常感谢!).我已经为此苦苦思索了一个星期,我真的厌倦了尝试很多没有成功的替代方案:(

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 中的 repo 的链接:https://github.com/darkguy2008/leaflet-angular4-issue

Here's the link of my repo in GitHub: https://github.com/darkguy2008/leaflet-angular4-issue

这个想法是在 Marker 中生成 PopupComponent(或任何类似的东西),您可以在 src/app/services/map.service.ts 中找到代码,第 38 行.

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/b5e3881ffc9889645f2ae7e65f4eed4d4db6779b

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

我还用这个解决方案做了一个自定义编译服务,解释了 这里 并上传到同一个 GitHub 存储库.谢谢@yurzui!:)

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.检查详细信息 这里.谢谢@yurzui!:)

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 在同一个 repo 中,简化为几个文件:https://github.com/darkguy2008/leaflet-angular4-issue 但你只需要这个例子就可以了:

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天全站免登陆