You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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。

有三种解决办法:

  1. 使用箭头函数绑定上下文
    在注册Leaflet点击事件时,用箭头函数包裹,确保this指向组件实例:

    // 在MapInputComponent的初始化逻辑中(比如ngOnInit)
    const map = L.map(`map-input-${this.object.id}`);
    map.on('click', (e) => this.onMapClick(e)); // 箭头函数保留组件上下文
    
  2. 手动绑定this
    用bind方法把onMapClick的上下文绑定到组件实例:

    map.on('click', this.onMapClick.bind(this));
    
  3. 在类中定义方法时用箭头函数
    把onMapClick直接定义为箭头函数,这样它的this会始终指向组件实例:

    onMapClick = (e: LeafletMouseEvent) => {
      this.mapClickEvent.emit({lat: e.latlng.lat, lng: e.latlng.lng});
    }
    

这三种方法都能解决this指向错误的问题,不需要把地图直接集成到edit-component里。

内容的提问来源于stack exchange,提问作者Benjamin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 05:28:14