Angular中通过emit向子组件传递对象的实现及报错排查
问题产生原因
- 组件通信装饰器用反:Angular中
@Input()用于父组件向子组件传值,向外抛出事件、传递数据必须用@Output()搭配EventEmitter,原代码把addProduct事件发射器声明为@Input属性,完全不符合通信规则。 - 事件绑定对象错误:原代码在父组件中给购物车子组件
<app-cart>绑定了addProduct事件,但这个事件根本不是CartComponent声明的输出事件,Angular无法识别自定义事件参数类型,会默认将$event解析为原生DOM的Event对象,和方法要求的Item类型不匹配,直接触发TS类型报错。 - 组件模板存在冗余结构:Angular组件模板只需要保留自身渲染的片段,不需要写
<!DOCTYPE html>、<html>、<head>、<body>这类全局页面标签,多余标签会引发渲染异常。 - 属性绑定写法不规范:模板里的图片
src用了插值写法,页面初始化时会出现无效资源请求。
正确实现方案
1. 修正商品项组件(ItemComponent)逻辑
将addProduct的装饰器从@Input改为@Output,声明为对外抛出的事件:
import { Component, Output, EventEmitter } from '@angular/core'; import { Item } from 'src/app/Model/Item'; import { StoreService } from 'src/app/service/store.service'; @Component({ selector: 'app-item', templateUrl: './item.component.html', styleUrls: ['./item.component.css'] }) export class ItemComponent { items: Item[] = [] // 用@Output声明对外抛出的加购事件 @Output() addProduct: EventEmitter<Item> = new EventEmitter() constructor(private storeService: StoreService) { this.items = this.storeService.getItem() } addProducts(item: Item) { const product: Item = { id: item.id, img: item.img, name: item.name, type: item.type, price: item.price, available: item.available } // 点击按钮时向外抛出商品对象 this.addProduct.emit(product) } }
清理组件模板里的冗余全局标签,把图片src改成属性绑定写法:
<section> <div class="products"> <ul *ngFor="let item of items"> <img [routerLink]="['/product', item.id]" [src]="item.img" alt="store pictures"/> <li>{{item.name}}</li> <li>{{item.type}}</li> <li>{{item.available}}</li> <li>{{item.price}}</li> <button (click)="addProducts(item)">Add to Cart</button> </ul> </div> </section>
2. 调整父组件的绑定逻辑
ItemComponent和CartComponent是平级的兄弟组件,需要父组件做通信中转:给CartComponent加模板引用变量,监听ItemComponent抛出的加购事件,拿到商品对象后直接调用CartComponent的加购方法即可。父组件模板代码如下:
<!-- 监听商品项组件抛出的加购事件,把拿到的商品传给购物车组件的加购方法 --> <app-item (addProduct)="cart.addProduct($event)"></app-item> <!-- 给购物车组件加模板变量#cart,代表组件实例 --> <app-cart #cart></app-cart>
3. 保留购物车组件(CartComponent)原有逻辑
CartComponent里的加购方法本身逻辑正确,不需要修改:
import { Component, OnInit } from '@angular/core'; import { Item } from 'src/app/Model/Item'; @Component({ selector: 'app-cart', templateUrl: './cart.component.html', styleUrls: ['./cart.component.css'] }) export class CartComponent implements OnInit { product: Item[] = [] ngOnInit(): void { } addProduct(post: Item){ this.product.push(post) } }
关键注意点
- Angular组件通信方向要记清:
@Input负责父到子传值,@Output + EventEmitter负责子到父抛事件,兄弟组件通信可以通过父组件中转,也可以用共享服务实现。 - 只有组件用
@Output声明过的事件,在模板上绑定的时候$event才会是你指定的泛型类型,否则Angular会默认按原生DOM事件解析,触发类型不匹配错误。
内容的提问来源于stack exchange,提问作者user19347248
相关产品推荐
相关产品推荐

