Angular:生命周期钩子是组件可用的输入数据

Posted

技术标签:

【中文标题】Angular:生命周期钩子是组件可用的输入数据【英文标题】:Angular: In which lifecycle hook is input data available to the Component 【发布时间】:2016-10-27 12:27:33 【问题描述】:

我有一个组件接收image 对象数组作为Input 数据。

export class ImageGalleryComponent 
  @Input() images: Image[];
  selectedImage: Image;

我想在组件加载时将selectedImage 值设置为images 数组的第一个对象。我曾尝试在 OnInit 生命周期挂钩中这样做:

export class ImageGalleryComponent implements OnInit 
  @Input() images: Image[];
  selectedImage: Image;
  ngOnInit() 
    this.selectedImage = this.images[0];
  

这给了我一个错误Cannot read property '0' of undefined,这意味着在这个阶段没有设置images 值。我也尝试过OnChanges 钩子,但我被卡住了,因为我无法获得有关如何观察数组变化的信息。我怎样才能达到预期的效果?

父组件如下所示:

@Component(
  selector: 'profile-detail',
  templateUrl: '...',
  styleUrls: [...],
  directives: [ImageGalleryComponent]
)

export class ProfileDetailComponent implements OnInit 
  profile: Profile;
  errorMessage: string;
  images: Image[];
  constructor(private profileService: ProfileService, private   routeParams: RouteParams)

  ngOnInit() 
    this.getProfile();
  

  getProfile() 
    let profileId = this.routeParams.get('id');
    this.profileService.getProfile(profileId).subscribe(
    profile => 
     this.profile = profile;
     this.images = profile.images;
     for (var album of profile.albums) 
       this.images = this.images.concat(album.images);
     
    , error => this.errorMessage = <any>error
   );
 

父组件的模板有这个

...
<image-gallery [images]="images"></image-gallery>
...

【问题讨论】:

images 数据如何填充到父组件中?即,是通过 http 请求吗?如果是这样,最好让 ImageGalleryComponent subscribe() 到 http observable。 @MarkRajcok images 只是像 profile: firstName: "abc", lastName: "xyz", images: [ ... ] 这样的父级使用的数据的一部分,这意味着如果我在子级中订阅,我仍然必须订阅父级并且我想避免重复 如果创建子组件时填充父组件中的图像数组,则应在调用 ngOnInit() 之前填充图像输入属性。您需要提供有关如何在父组件中填充图像数组的更多信息,以便任何人进一步帮助您(或创建一个显示问题的最小 Plunker)。 @MarkRajcok 我已经添加了父组件以及如何在其中填充图像。 所以是的,看起来您的父组件正在使用 http(因为它正在使用服务)来填充其 images 属性。由于这是一个异步操作,因此在调用其 ngOnInit() 方法时不会填充子组件的 input 属性。将您的代码从 ngOnInit() 移动到 ngOnChanges(),它应该可以工作。 【参考方案1】:

在调用ngOnInit() 之前填充输入属性。但是,这假定在创建子组件时已经填充了提供输入属性的父属性。

在您的场景中,情况并非如此——图像数据是从服务异步填充的(因此是一个 http 请求)。因此,在调用ngOnInit() 时不会填充输入属性。

为了解决您的问题,当从服务器返回数据时,为父属性分配一个新数组。在孩子中实现ngOnChanges()ngOnChanges() 将在 Angular 更改检测将新数组值向下传播到子节点时调用。

【讨论】:

我同意你的观点,因为我自己也面临同样的问题。即使我在孩子中实现了ngOnchanges(),错误Cannot read property '0' of undefined 也会出现。但是,该应用程序可以毫无问题地继续运行。出现此错误是因为最初尚未填充输入属性。当接收到来自异步调用的数据时,它由第二次调用ngOnChanges() 填充。对于我的情况,除了这个解决方案之外,我还在 html 中添加了 guard against null 运算符。它清楚地解决了错误。 @Mark ..很好地解释并解决了我的问题。【参考方案2】:

您还可以为您的图像添加一个设置器,每当值更改时都会调用该设置器,您可以在设置器本身中设置默认选择的图像:

export class ImageGalleryComponent 
  private _images: Image[];

  @Input()
  set images(value: Image[]) 
      if (value)  //null check
          this._images = value;
          this.selectedImage = value[0]; //setting default selected image
      
  
  get images(): Image[] 
      return this._images;
  

  selectedImage: Image;

【讨论】:

【参考方案3】:

你可以通过简单地改变一些事情来解决它。

  export class ImageGalleryComponent implements OnInit 

  @Input() images: Image[];
  selectedImage: Image;

  ngOnChanges() 
      if(this.images) 
        this.selectedImage = this.images[0];
      
  

 

【讨论】:

注意,您需要添加implements OnChanges才能使用ngOnChanges()方法。【参考方案4】:

作为另一种解决方案,您可以简单地*ngIf所有模板内容,直到您从网络中获得所需内容:

...
<image-gallery *ngIf="imagesLoaded" [images]="images"></image-gallery>
...

并在您的获取方法中切换标志值:

  getProfile() 
    let profileId = this.routeParams.get('id');
    this.profileService.getProfile(profileId).subscribe(
    profile => 
     this.profile = profile;
     this.images = profile.images;
     for (var album of profile.albums) 
       this.images = this.images.concat(album.images);
     
     this.imagesLoaded = true; /* <--- HERE*/
    , error => this.errorMessage = <any>error
   );
 

通过这种方式,只有当父组件在静态内容中拥有子组件所需的所有内容时,您才会渲染子组件。当您有一些表示数据获取状态的加载器/微调器时,它会更加有用:

...
<image-gallery *ngIf="imagesLoaded" [images]="images"></image-gallery>
<loader-spinner-whatever *ngIf="!imagesLoaded" [images]="images"></loader-spinner-whatever>
...

但对您的问题的简短回答:

输入何时可用? 在OnInit钩子里 为什么对您的子组件不可用? 它们是,但在这个特定时间点它们没有加载 我能用这个做什么? 耐心等待渲染子组件utul你以异步方式获取数据或学习子组件处理undefined输入状态

【讨论】:

以上是关于Angular:生命周期钩子是组件可用的输入数据的主要内容,如果未能解决你的问题,请参考以下文章

Angular 2 动态组件加载 ngOnChanges 生命周期钩子调用

Vue——生命周期和钩子函数的一些理解

Angular 生命周期钩子,让你掌控每个关键时刻!

Angular 生命周期钩子和渲染组件

处理 Angular 4 生命周期钩子

初始化所有孩子后的Angular 2生命周期钩子?