Tib*_* C. 10 google-maps angular
我使用谷歌地图javascript api,我必须在InfoWindow中显示一个Angular组件.
在我的项目中,我使用该Jsonp
服务加载谷歌地图API .比我有google.maps.Map
可用的对象.稍后在组件中我创建了一些标记并将一个点击监听器附加到它们:
TypeScript:
let marker = new google.maps.Marker(opts);
marker.setValues({placeId: item[0]});
marker.addListener('click', (ev: google.maps.MouseEvent) => this.onMarkerClick(marker, ev));
Run Code Online (Sandbox Code Playgroud)
然后在click
处理程序中我想打开一个包含Angular组件的信息窗口:
TypeScript:
private onMarkerClick(marker: google.maps.Marker, ev: google.maps.MouseEvent) {
var div = document.createElement();
this.placeInfoWindow.setContent(div);
// Magic should happen here somehow
// this.placeInfoWindow.setContent('<app-info-view-element></app-info-view-element>');
this.placeInfoWindow.open(this.map, marker);
}
Run Code Online (Sandbox Code Playgroud)
我最终做的是一些香草JS:
TypeScript:
private onMarkerClick(marker: google.maps.Marker, ev: google.maps.MouseEvent) {
let div = document.createElement('div');
div.className = 'map-info-window-container';
div.style.height = '140px';
div.style.width = '240px';
this.placeInfoWindow.setContent(div);
this.placeInfoWindow.open(this.map, marker);
this.placesService.getPlace(marker.get('id')).subscribe(res => {
this.decorateInfoWindow(div, res.name, marker);
}, error => {
this.decorateInfoWindow(div, ':( Failed to load details: ', marker);
});
}
private decorateInfoWindow(containerEl: HTMLElement, title?:string, marker?:google.maps.Marker) {
let h3 = document.createElement('h3');
h3.innerText = title;
containerEl.appendChild(h3);
let buttonBar = document.createElement('div');
let editButton = document.createElement('button')
editButton.innerText = "Edit";
editButton.addEventListener('click', ev => {
this.editPlace(marker);
});
buttonBar.appendChild(editButton);
containerEl.appendChild(buttonBar);
}
Run Code Online (Sandbox Code Playgroud)
正如我所知,问题是创建动态组件的唯一可行方法是使用Angulars ViewContainerRef
:
但是没有文档或示例,描述了如何ViewContainerRef
从动态创建的元素创建.
是否可以强制框架以某种方式处理DOM?正如在很多线程中所述:"Angular不处理innerHTML
或appendChild
".这完全是死路一条吗?
第二:是否可以使用Renderer
实施?(不熟悉它),我看到了这个Canvas渲染器实验,理论上,我猜它也适用于谷歌地图,因为我们可以推断地图只是一种特殊的画布.它在上一个版本中是否仍然可用或更改?DomRenderer
不在文档中,但是可以在源代码中找到它.
yur*_*zui 14
这里的主要规则是动态创建组件,您需要获得它的工厂.
1)将动态组件添加到entryComponents
数组中除了包括declarations
:
@NgModule({
...
declarations: [
AppInfoWindowComponent,
...
],
entryComponents: [
AppInfoWindowComponent,
...
],
})
Run Code Online (Sandbox Code Playgroud)
即使我们不在某些模板中直接使用我们的组件,这也是角度编译器为组件生成ngfactory的提示.
2)现在我们需要注入ComponentFactoryResolver
我们想要获得ngfactory的组件/服务.您可以将ComponentFactoryResolver
其视为组件工厂的存储
app.component.ts
import { ComponentFactoryResolver } from '@angular/core'
...
constructor(private resolver: ComponentFactoryResolver) {}
Run Code Online (Sandbox Code Playgroud)
3)是时候AppInfoWindowComponent
出厂了:
const compFactory = this.resolver.resolveComponentFactory(AppInfoWindowComponent);
this.compRef = compFactory.create(this.injector);
Run Code Online (Sandbox Code Playgroud)
4)有工厂我们可以随意使用它.以下是一些案例:
ViewContainerRef.createComponent(componentFactory,...)
在viewContainer旁边插入组件.
ComponentFactory.create(injector, projectableNodes?, rootSelectorOrNode?)
只需创建组件,该组件可以插入匹配的元素中 rootSelectorOrNode
注意,我们可以在ComponentFactory.create
函数的第三个参数中提供节点或选择器.在许多情况下它可能会有所帮助.在这个例子中,我将简单地创建组件,然后插入到一些元素中.
onMarkerClick
方法可能如下所示:
onMarkerClick(marker, e) {
if(this.compRef) this.compRef.destroy();
// creation component, AppInfoWindowComponent should be declared in entryComponents
const compFactory = this.resolver.resolveComponentFactory(AppInfoWindowComponent);
this.compRef = compFactory.create(this.injector);
// example of parent-child communication
this.compRef.instance.param = "test";
const subscription = this.compRef.instance.onCounterIncremented.subscribe(x => { this.counter = x; });
let div = document.createElement('div');
div.appendChild(this.compRef.location.nativeElement);
this.placeInfoWindow.setContent(div);
this.placeInfoWindow.open(this.map, marker);
// 5) it's necessary for change detection within AppInfoWindowComponent
// tips: consider ngDoCheck for better performance
this.appRef.attachView(this.compRef.hostView);
this.compRef.onDestroy(() => {
this.appRef.detachView(this.compRef.hostView);
subscription.unsubscribe();
});
}
Run Code Online (Sandbox Code Playgroud)
5)不幸的动态创建组件不是变更检测树的一部分,因此我们还需要注意变更检测.它可以通过使用ApplicationRef.attachView(compRef.hostView)
上面的例子中所写的来完成,或者我们可以用我们创建动态组件的组件ngDoCheck
(示例)明确表达(AppComponent
在我的例子中)
app.component.ts
ngDoCheck() {
if(this.compRef) {
this.compRef.changeDetectorRef.detectChanges()
}
}
Run Code Online (Sandbox Code Playgroud)
这种方法更好,因为它只会在更新当前组件时更新动态组件.另一方面,ApplicationRef.attachView(compRef.hostView)
将变化检测器添加到变化检测器树的根,因此将在每个变化检测标记上调用它.
提示:
因为addListener
在angular2区域外运行,我们需要明确地在angular2区域内运行我们的代码:
marker.addListener('click', (e) => {
this.zone.run(() => this.onMarkerClick(marker, e));
});
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
4571 次 |
最近记录: |