Angular 17子组件@Output触发emit时显示未定义的问题
我用手风琴组件展示对象列表,展开时通过(click)="addToArray(id)"加载<edit-component *ngIf="idArray.includes(id)"></edit-component>——这么做是为了避免Leaflet地图在折叠状态下无法正常渲染。
现在edit-component里有输入框、验证逻辑,还有一个map-input-component。因为map-input-component自身模板找不到DOM元素,所以把.map-container放在了edit-component的模板里。
现在想让地图组件触发点击事件,通过@Output把坐标传给父组件edit-component,但访问this.mapClickEvent时显示undefined,报错Cannot read properties of undefined (reading 'emit')。怀疑是组件运行时加载导致的,难道只能把地图直接集成到edit-component里?
相关代码
edit.component.ts
@Component({ selector: "edit-component", templateUrl: "./edit.component.html" }) export class EditComponent { @Input() myObject!: myObjectInterface; // 其他输入框的处理方法 onMapClick(event: {lat: number, lng: number}){ console.log(event) } }
edit.component.html
<div class="row"> <!-- 其他包含输入框的列 --> <div class="col"> <div class="map-container" [attr.id]="'map-input-' + myObject.id"></div> <map-input-component [object]="myObject" (mapClickEvent)="onMapClick($event)" ></map-input-component> </div> </div>
map-input.component.ts
@Component({ selector: "map-input-component", template: "", standalone: true }) export class MapInputComponent { @Input() object!: myObjectInterface; @Output() mapClickEvent = new EventEmitter<{lat: number, lng: number}>(); // Leaflet地图设置,使用L.on("click", this.onMapClick) onMapClick(e: LeafletMouseEvent) { // console.log(e)可以输出所需数据 // console.log(this.mapClickEvent)输出undefined this.mapClickEvent.emit({lat: e.latlng.lat, lng: e.latlng.lng}); } }
错误信息
ERROR TypeError: Cannot read properties of undefined (reading 'emit') at NewClass.onMapClick (map-input.component.ts:132:24) at NewClass.fire (leaflet-src.js:606:11) at NewClass._fireDOMEvent (leaflet-src.js:4565:17) at NewClass._handleDOMEvent (leaflet-src.js:4514:10) at HTMLDivElement.handler (leaflet-src.js:2799:15) at _ZoneDelegate.invokeTask (zone.js:398:33) at core.mjs:14556:55 at AsyncStackTaggingZoneSpec.onInvokeTask (core.mjs:14556:36) at _ZoneDelegate.invokeTask (zone.js:397:38) at Object.onInvokeTask (core.mjs:14869:33)
问题出在Leaflet事件绑定的上下文丢失,和组件运行时加载无关。当你用L.on("click", this.onMapClick)绑定事件时,Leaflet会在全局上下文(而非组件实例)中调用onMapClick,导致this指向的不是MapInputComponent实例,自然找不到mapClickEvent。
有三种解决办法:
使用箭头函数绑定上下文
在注册Leaflet点击事件时,用箭头函数包裹,确保this指向组件实例:// 在MapInputComponent的初始化逻辑中(比如ngOnInit) const map = L.map(`map-input-${this.object.id}`); map.on('click', (e) => this.onMapClick(e)); // 箭头函数保留组件上下文手动绑定this
用bind方法把onMapClick的上下文绑定到组件实例:map.on('click', this.onMapClick.bind(this));在类中定义方法时用箭头函数
把onMapClick直接定义为箭头函数,这样它的this会始终指向组件实例:onMapClick = (e: LeafletMouseEvent) => { this.mapClickEvent.emit({lat: e.latlng.lat, lng: e.latlng.lng}); }
这三种方法都能解决this指向错误的问题,不需要把地图直接集成到edit-component里。
内容的提问来源于stack exchange,提问作者Benjamin

