Angular调用API时父子组件数据传递异常问题
Angular父组件API数据传递给子组件的异常问题解决
问题现象
使用Angular前端+Node.js后端,通过API获取数据并展示。search-home父组件包含搜索栏,search-results为其子组件。提交表单时父组件能正常获取API数据,但首次提交无法传递给子组件,第二次提交才显示正常。通过ngOnChanges监听子组件属性,首次提交时属性前后值均为undefined,仅第二次提交才会触发属性变化。
父组件(search-home)代码
TypeScript代码
import { Component, ViewChild } from '@angular/core'; import { Router, ActivatedRoute } from '@angular/router'; import { AppServiceService } from '../app-service.service'; import { SearchResultsComponent } from './search-results/search-results.component'; @Component({ selector: 'app-search-home', templateUrl: './search-home.component.html', styleUrls: ['./search-home.component.css'] }) export class SearchHomeComponent { responseData: any; constructor(private router: Router, private route: ActivatedRoute, private service: AppServiceService) {} searchTicker(searchInputValue: string) { this.service.getData(searchInputValue).subscribe( response => { this.responseData = response; console.log('ResponseData property is:', this.responseData); this.router.navigate(['/search', searchInputValue]); }, error => { console.log('Error is', error); } ); } }
HTML代码
<p>search-home works!</p> <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" rel="stylesheet"> <div> stock search </div> <form class="form-inline" (ngSubmit)="searchTicker(searchInput.value)" #form="ngForm"> <div class="input-group"> <input #searchInput class="form-control border rounded-pill" type="search" placeholder="Enter Stock Ticker Symbol"> <button class="btn btn-outline-secondary border rounded-pill ms-n5" type="submit"> <i class="fa fa-search"></i> </button> </div> </form> <app-search-results [myresponseData]="responseData"></app-search-results>
子组件(search-results)代码
TypeScript代码
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; @Component({ selector: 'app-search-results', templateUrl: './search-results.component.html', styleUrl: './search-results.component.css' }) export class SearchResultsComponent implements OnChanges{ @Input() myresponseData: any; constructor(){ console.log("search-results constructor called"); console.log('property of constructor', this.myresponseData); } ngOnChanges(changes: SimpleChanges){ console.log('ngonchanges lifecycle hook called'); console.log('changes that are made', changes); } }
HTML代码
<p>search-results works!</p> <div *ngIf="myresponseData"> <h2>Data from API:</h2> <pre>{{ myresponseData | json }}</pre> </div>
问题根源
核心问题在于路由导航导致父组件重新实例化:
- 首次提交表单时,API请求异步返回后,先给当前
SearchHomeComponent实例的responseData赋值,随后调用this.router.navigate触发路由跳转。 - Angular会销毁旧的
SearchHomeComponent实例,创建新的实例,新实例的responseData恢复为初始的undefined,导致子组件接收到的是无数据状态。 - 第二次提交时,若路由参数未发生变化(或在已跳转后的页面再次提交),Angular不会重新创建组件实例,此时
responseData赋值后能正常传递给子组件。
解决方案
方案1:移除不必要的路由跳转(最简单)
如果不需要通过URL展示搜索参数,直接删除this.router.navigate(['/search', searchInputValue]);这行代码。父组件获取数据后,Angular的变更检测会自动将responseData传递给子组件。
方案2:通过路由参数自动触发数据获取
保留路由跳转逻辑,同时在父组件初始化时监听路由参数变化,自动发起API请求,确保新组件实例能获取到对应数据:
修改SearchHomeComponent的代码,添加ngOnInit生命周期钩子:
import { Component, OnInit } from '@angular/core'; // 其他导入不变 @Component({ // 组件配置不变 }) export class SearchHomeComponent implements OnInit { responseData: any; constructor(private router: Router, private route: ActivatedRoute, private service: AppServiceService) {} ngOnInit(): void { // 监听路由参数变化,自动获取数据 this.route.paramMap.subscribe(params => { const ticker = params.get('ticker'); if (ticker) { this.fetchData(ticker); } }); } searchTicker(searchInputValue: string) { // 仅做路由跳转,数据获取由路由监听触发 this.router.navigate(['/search', searchInputValue]); } private fetchData(ticker: string) { this.service.getData(ticker).subscribe( response => { this.responseData = response; }, error => { console.log('Error is', error); } ); } }
方案3:使用共享服务传递数据
创建一个共享服务,存储API返回的数据,子组件从服务中读取数据,替代@Input传递:
- 创建共享服务:
import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class DataShareService { private dataSubject = new BehaviorSubject<any>(null); public data$ = this.dataSubject.asObservable(); setData(data: any) { this.dataSubject.next(data); } }
- 修改父组件,将数据存入服务:
// 导入DataShareService constructor(private dataShareService: DataShareService, // 其他服务不变) {} searchTicker(searchInputValue: string) { this.service.getData(searchInputValue).subscribe( response => { this.dataShareService.setData(response); this.router.navigate(['/search', searchInputValue]); }, error => { console.log('Error is', error); } ); }
- 修改子组件,从服务订阅数据:
import { Component, OnInit } from '@angular/core'; import { DataShareService } from '../data-share.service'; @Component({ // 组件配置不变 }) export class SearchResultsComponent implements OnInit{ myresponseData: any; constructor(private dataShareService: DataShareService){} ngOnInit(): void { this.dataShareService.data$.subscribe(data => { this.myresponseData = data; }); } }
内容的提问来源于stack exchange,提问作者Negar
相关产品推荐
相关产品推荐

