Angular 2 / 传单地图,如何从标记弹出窗口 link 到组件? ...路由器链接?
Angular 2 / leaflet map, How to link to a component from marker popup ? ... routerLink?
在我的 angular 2 应用程序中,我有一个带有绑定到 onClick 事件的弹出窗口的传单地图。
弹出窗口的内容有一个 link 到一个 angular 组件。但是,当我在 .setContent() 函数中使用 routerLink 时, link 不显示。
我猜这是因为 .setContent() 无法呈现 angular 2 个有意义的指令。我可以用什么代替?
@Component({
selector: 'app-map',
templateUrl: './map.component.html',
styleUrls: ['./map.component.css']
})
export class MapComponent implements AfterViewInit {
openmap: any;
constructor() { }
ngAfterViewInit() {
let openmap = L.tileLayer("http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}", {
attribution: 'terms and feedback'
});
let map = L.map("map", {
center: [33.2148, -97.1331],
zoom: 5,
zoomControl: true,
maxZoom: 18
}).addLayer(openmap);
let marker = L.marker([39.2148, -98.1331]).addTo(map);
let popup = L.popup();
function onMapClick(e) {
popup
.setLatLng(e.latlng)
.setContent("Facility" + "<br/>" + "<a routerLink='/view2'>" + "View Two" + "</a>")
.openOn(map);
}
map.on('click', onMapClick);
}
}
针,要是我改成
.setContent("Facility" + "<br/>" + "<a href='../view2'>" + "View Two" + "</a>")
会做我想做的,但这会导致页面刷新,所以这不是一个选项。
**此答案无效。如果有人有解决方案,我会暂时保留它。我认为它显示了 Angular 需要 SafeHtml 用于 routerLink 的问题(参见 DomSanitizer
) 并且 Leaflet 只会将字符串传递给使用的弹出窗口
.setContent() or .bindPopup()
以下代码基于
export class MapComponent implements AfterViewInit {
constructor(private _sanitizer: DomSanitizer){}
private _popUpContent: string = '"Facility" + "<br/>" + "<a routerLink='/view2'>" + "View Two" + "</a>"';
private htmlProperty(): SafeHtml {
return this._sanitizer.bypassSecurityTrustHtml(this._popUpContent);
}
ngAfterViewInit() {
let safeLink = this.htmlProperty();
let openmap = L.tileLayer("http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}", {
attribution: 'terms and feedback'
});
let map = L.map("map", {
center: [33.2148, -97.1331],
zoom: 5,
zoomControl: true,
maxZoom: 18
}).addLayer(openmap);
let marker = L.marker([39.2148, -98.1331]).addTo(map);
let popup = L.popup();
function onMapClick(e) {
popup
.setLatLng(e.latlng)
.setContent(safeLink) //throws type error
.openOn(map);
}
map.on('click', onMapClick);
}
}
有一个非常简单的方法和一个非常复杂的方法。
简单的方法是使用原始 HTML 和 angular 之外的锚元素,而不使用 RouterLink。注册以点击该锚元素并使用 Router 服务进行导航。
任务是触发 links 但实际问题要深得多,现在 links 下次显示 angular 组件...
因此,对于复杂的解决方案:
这是一个非常高级的主题...它不仅涉及使用高级 angular 技术,而且在传单实施方面也很先进。
我会尽力传达信息,但由于复杂性,示例将非常简单,需要工作。
第一 - Angular 领域。
包含指令、组件或管道的 HTML 字符串将永远无法工作,唯一的方法是初始化一个 View
让我们将 View 定义为对 component 或 template[=83= 的视图实例的引用].
这些称为 ComponentRef 和 TemplateRef
所以,我们有两种方法来解决这个问题。由于我不能两者都做,所以我将使用 ComponentRef,但请注意,您也可以使用 TemplateRef。
对于模板,您首先需要获得在组件中定义的模板以及 ViewContainerRef 以将该模板附加到。
我们将构建一个接受传单 Marker 并绑定到标记的点击事件的服务,点击它会打开一个 angular 的弹出窗口组件。
组件很简单,它渲染了一个link。
@Component({
selector: 'facility-link',
template: `Facility <br/> <a routerLink="{{link}}"> View Two</a>`
})
export class FacilityLinkComponent {
public link: string;
constructor() { }
}
现在,对于服务:
@Injectable()
export class LinkPopupService {
constructor(private cfr: ComponentFactoryResolver,
private injector: Injector,
private appRef: ApplicationRef) { }
register(marker: leaflet.Marker, link: string): void {
marker.on('click', ($event: leaflet.MouseEvent) => this.popup($event.target, link) );
}
popup(marker: leaflet.Marker, link: string) {
const cmpFactory = this.cfr.resolveComponentFactory(FacilityLinkComponent);
const componentRef = cmpFactory.create(this.injector);
componentRef.instance.link = link;
this.appRef.attachView(componentRef.hostView);
const markerElement = marker.getElement();
markerElement.parentElement.appendChild(componentRef.location.nativeElement);
const markerPos = leaflet.DomUtil.getPosition(markerElement);
const markerClass = leaflet.DomUtil.getClass(markerElement);
leaflet.DomUtil.setTransform(componentRef.location.nativeElement, markerPos);
leaflet.DomUtil.setClass(componentRef.location.nativeElement, markerClass);
}
}
register 方法接受标记和 link 并注册到点击事件。
当 popup 方法触发时,它使用 angular 工具创建 FacilityLinkComponent 的视图实例,设置 link 为了将来绑定,将视图附加到它并附加到 DOM。
这一切都发生在前 5 行代码中。
一些注意事项:
- 我们必须附加一个视图,这样更改检测才能工作
- 正确的实现将允许设置 ViewContainerRef 和/或 Injector - 使用延迟加载时这是必须的。
- 最好通过 Injector 向组件发送数据,而不是通过赋值(ReflectiveInjector)
- 需要进行适当的清理(销毁组件并分离视图)
- 需要添加切换逻辑,还要清理导航。
传单
从第 6 行开始的代码执行弹出窗口的定位。
这是一个非常简单的逻辑,它只是复制标记中的所有内容。
这就是我使用标记的原因,所以我将有一个参考来定位。
在现实世界的示例中,您需要获得一个面板并将组件推入它们自己的层,计算位置。这并不难,因为 leaflet 有所有的帮助程序,但是对于这个来说太多了。
希望对您有所帮助。
我也有同样的要求。
我只是用简单的方法做到了。
根据您的代码,您可以添加这样的更改
private _popUpContent: string = '"Facility" + "<br/>" + "<a id="view_two">" + "View Two" + "</a>"';
constructor(private _sanitizer: DomSanitizer , public ngZone : NgZone,public elementRef : ElementRef){}
function onMapClick(e) {
popup
.setLatLng(e.latlng)
.setContent(safeLink) //throws type error
.openOn(map);
popup.on('popupopen' , () => {
this.initInfoWindowEventListner();
})
}
function initInfoWindowEventListner(){
this.elementRef.nativeElement.querySelector("#view_two")
.addEventListener('click', (e : any)=> {
this.ngZone.run(() => {
this.router.navigate(['/view2])
})
});
}
我的回答是基于@Vijay Kumar 的回答。我有多个标记,并希望按钮的操作根据标记而有所不同。
map.on('popupopen', (e) => {
const _latlng = e.target._popup._latlng;
const lat = _latlng.lat + 4;
map.setView([lat, _latlng.lng], e.target._zoom);
// Events listeners
addDeviceControlEventListener(this.elementRef, this.ngZone, this.router);
addDeviceAlarmsEventListener(this.elementRef, this.ngZone, this.dialogService);
});
addDeviceControlEventListener(elementRef: ElementRef, ngZone: NgZone, router: Router) {
const list = elementRef.nativeElement.querySelectorAll('a.device-control-btn');
for (const item of list) {
item.addEventListener('click', (e: any) => {
ngZone.run(() => router.navigateByUrl(`pages/device/${item.getAttribute('imei')}`));
});
}
}
addDeviceAlarmsEventListener(elementRef: ElementRef, ngZone: NgZone, dialogService: NbDialogService) {
const list = elementRef.nativeElement.querySelectorAll('a.device-alarms-btn');
for (const item of list) {
item.addEventListener('click', (e: any) => {
ngZone.run(() => dialogService.open(DeviceAlarmsComponent, {
context: { deviceKey: item.getAttribute('imei') },
}));
});
}
}
bindPopup
中的按钮如下
<a imei="${device.key}" class="device-control-btn"> Device Operation</a>
<a imei="${device.key}" class="device-alarms-btn"> Device Alarms</a>
在我的 angular 2 应用程序中,我有一个带有绑定到 onClick 事件的弹出窗口的传单地图。
弹出窗口的内容有一个 link 到一个 angular 组件。但是,当我在 .setContent() 函数中使用 routerLink 时, link 不显示。
我猜这是因为 .setContent() 无法呈现 angular 2 个有意义的指令。我可以用什么代替?
@Component({
selector: 'app-map',
templateUrl: './map.component.html',
styleUrls: ['./map.component.css']
})
export class MapComponent implements AfterViewInit {
openmap: any;
constructor() { }
ngAfterViewInit() {
let openmap = L.tileLayer("http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}", {
attribution: 'terms and feedback'
});
let map = L.map("map", {
center: [33.2148, -97.1331],
zoom: 5,
zoomControl: true,
maxZoom: 18
}).addLayer(openmap);
let marker = L.marker([39.2148, -98.1331]).addTo(map);
let popup = L.popup();
function onMapClick(e) {
popup
.setLatLng(e.latlng)
.setContent("Facility" + "<br/>" + "<a routerLink='/view2'>" + "View Two" + "</a>")
.openOn(map);
}
map.on('click', onMapClick);
}
}
针,要是我改成
.setContent("Facility" + "<br/>" + "<a href='../view2'>" + "View Two" + "</a>")
会做我想做的,但这会导致页面刷新,所以这不是一个选项。
**此答案无效。如果有人有解决方案,我会暂时保留它。我认为它显示了 Angular 需要 SafeHtml 用于 routerLink 的问题(参见 DomSanitizer ) 并且 Leaflet 只会将字符串传递给使用的弹出窗口 .setContent() or .bindPopup()
以下代码基于
export class MapComponent implements AfterViewInit {
constructor(private _sanitizer: DomSanitizer){}
private _popUpContent: string = '"Facility" + "<br/>" + "<a routerLink='/view2'>" + "View Two" + "</a>"';
private htmlProperty(): SafeHtml {
return this._sanitizer.bypassSecurityTrustHtml(this._popUpContent);
}
ngAfterViewInit() {
let safeLink = this.htmlProperty();
let openmap = L.tileLayer("http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}", {
attribution: 'terms and feedback'
});
let map = L.map("map", {
center: [33.2148, -97.1331],
zoom: 5,
zoomControl: true,
maxZoom: 18
}).addLayer(openmap);
let marker = L.marker([39.2148, -98.1331]).addTo(map);
let popup = L.popup();
function onMapClick(e) {
popup
.setLatLng(e.latlng)
.setContent(safeLink) //throws type error
.openOn(map);
}
map.on('click', onMapClick);
}
}
有一个非常简单的方法和一个非常复杂的方法。
简单的方法是使用原始 HTML 和 angular 之外的锚元素,而不使用 RouterLink。注册以点击该锚元素并使用 Router 服务进行导航。
任务是触发 links 但实际问题要深得多,现在 links 下次显示 angular 组件...
因此,对于复杂的解决方案:
这是一个非常高级的主题...它不仅涉及使用高级 angular 技术,而且在传单实施方面也很先进。
我会尽力传达信息,但由于复杂性,示例将非常简单,需要工作。
第一 - Angular 领域。
包含指令、组件或管道的 HTML 字符串将永远无法工作,唯一的方法是初始化一个 View
让我们将 View 定义为对 component 或 template[=83= 的视图实例的引用].
这些称为 ComponentRef 和 TemplateRef
所以,我们有两种方法来解决这个问题。由于我不能两者都做,所以我将使用 ComponentRef,但请注意,您也可以使用 TemplateRef。 对于模板,您首先需要获得在组件中定义的模板以及 ViewContainerRef 以将该模板附加到。
我们将构建一个接受传单 Marker 并绑定到标记的点击事件的服务,点击它会打开一个 angular 的弹出窗口组件。
组件很简单,它渲染了一个link。
@Component({
selector: 'facility-link',
template: `Facility <br/> <a routerLink="{{link}}"> View Two</a>`
})
export class FacilityLinkComponent {
public link: string;
constructor() { }
}
现在,对于服务:
@Injectable()
export class LinkPopupService {
constructor(private cfr: ComponentFactoryResolver,
private injector: Injector,
private appRef: ApplicationRef) { }
register(marker: leaflet.Marker, link: string): void {
marker.on('click', ($event: leaflet.MouseEvent) => this.popup($event.target, link) );
}
popup(marker: leaflet.Marker, link: string) {
const cmpFactory = this.cfr.resolveComponentFactory(FacilityLinkComponent);
const componentRef = cmpFactory.create(this.injector);
componentRef.instance.link = link;
this.appRef.attachView(componentRef.hostView);
const markerElement = marker.getElement();
markerElement.parentElement.appendChild(componentRef.location.nativeElement);
const markerPos = leaflet.DomUtil.getPosition(markerElement);
const markerClass = leaflet.DomUtil.getClass(markerElement);
leaflet.DomUtil.setTransform(componentRef.location.nativeElement, markerPos);
leaflet.DomUtil.setClass(componentRef.location.nativeElement, markerClass);
}
}
register 方法接受标记和 link 并注册到点击事件。
当 popup 方法触发时,它使用 angular 工具创建 FacilityLinkComponent 的视图实例,设置 link 为了将来绑定,将视图附加到它并附加到 DOM。
这一切都发生在前 5 行代码中。
一些注意事项:
- 我们必须附加一个视图,这样更改检测才能工作
- 正确的实现将允许设置 ViewContainerRef 和/或 Injector - 使用延迟加载时这是必须的。
- 最好通过 Injector 向组件发送数据,而不是通过赋值(ReflectiveInjector)
- 需要进行适当的清理(销毁组件并分离视图)
- 需要添加切换逻辑,还要清理导航。
传单
从第 6 行开始的代码执行弹出窗口的定位。
这是一个非常简单的逻辑,它只是复制标记中的所有内容。
这就是我使用标记的原因,所以我将有一个参考来定位。
在现实世界的示例中,您需要获得一个面板并将组件推入它们自己的层,计算位置。这并不难,因为 leaflet 有所有的帮助程序,但是对于这个来说太多了。
希望对您有所帮助。
我也有同样的要求。 我只是用简单的方法做到了。
根据您的代码,您可以添加这样的更改
private _popUpContent: string = '"Facility" + "<br/>" + "<a id="view_two">" + "View Two" + "</a>"';
constructor(private _sanitizer: DomSanitizer , public ngZone : NgZone,public elementRef : ElementRef){}
function onMapClick(e) {
popup
.setLatLng(e.latlng)
.setContent(safeLink) //throws type error
.openOn(map);
popup.on('popupopen' , () => {
this.initInfoWindowEventListner();
})
}
function initInfoWindowEventListner(){
this.elementRef.nativeElement.querySelector("#view_two")
.addEventListener('click', (e : any)=> {
this.ngZone.run(() => {
this.router.navigate(['/view2])
})
});
}
我的回答是基于@Vijay Kumar 的回答。我有多个标记,并希望按钮的操作根据标记而有所不同。
map.on('popupopen', (e) => {
const _latlng = e.target._popup._latlng;
const lat = _latlng.lat + 4;
map.setView([lat, _latlng.lng], e.target._zoom);
// Events listeners
addDeviceControlEventListener(this.elementRef, this.ngZone, this.router);
addDeviceAlarmsEventListener(this.elementRef, this.ngZone, this.dialogService);
});
addDeviceControlEventListener(elementRef: ElementRef, ngZone: NgZone, router: Router) {
const list = elementRef.nativeElement.querySelectorAll('a.device-control-btn');
for (const item of list) {
item.addEventListener('click', (e: any) => {
ngZone.run(() => router.navigateByUrl(`pages/device/${item.getAttribute('imei')}`));
});
}
}
addDeviceAlarmsEventListener(elementRef: ElementRef, ngZone: NgZone, dialogService: NbDialogService) {
const list = elementRef.nativeElement.querySelectorAll('a.device-alarms-btn');
for (const item of list) {
item.addEventListener('click', (e: any) => {
ngZone.run(() => dialogService.open(DeviceAlarmsComponent, {
context: { deviceKey: item.getAttribute('imei') },
}));
});
}
}
bindPopup
中的按钮如下
<a imei="${device.key}" class="device-control-btn"> Device Operation</a>
<a imei="${device.key}" class="device-alarms-btn"> Device Alarms</a>