简体   繁体   English

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

[英]Angular: In which lifecycle hook is input data available to the Component

I have a component which receives an array of image objects as Input data.我有一个组件,它接收一组image对象作为Input数据。

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

I would like when the component loads the selectedImage value be set to the first object of the images array.我想在组件加载selectedImage值时将其设置为images数组的第一个对象。 I have tried to do this in the OnInit lifecycle hook like this:我试图在OnInit生命周期钩子中这样做:

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

this gives me an error Cannot read property '0' of undefined which means the images value isn't set on this stage.这给了我一个错误Cannot read property '0' of undefined这意味着images值未在此阶段设置。 I have also tried the OnChanges hook but I'm stuck because i can't get information on how to observe changes of an array.我也尝试过OnChanges钩子,但我被卡住了,因为我无法获得有关如何观察数组变化的信息。 How can I achieve the expected result?我怎样才能达到预期的结果?

The parent component looks like this:父组件如下所示:

@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
   );
 }
}

The parent component's template has this父组件的模板有这个

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

Input properties are populated before ngOnInit() is called.ngOnInit()之前填充输入属性。 However, this assumes the parent property that feeds the input property is already populated when the child component is created.但是,这假设在创建子组件时已经填充了提供输入属性的父属性。

In your scenario, this is not the case – the images data is being populated asynchronously from a service (hence an http request).在您的场景中,情况并非如此——图像数据是从服务异步填充的(因此是 http 请求)。 Therefore, the input property will not be populated when ngOnInit() is called.因此,调用ngOnInit()时不会填充输入属性。

To solve your problem, when the data is returned from the server, assign a new array to the parent property.为了解决您的问题,当数据从服务器返回时,为父属性分配一个新数组。 Implement ngOnChanges() in the child.在孩子中实现ngOnChanges() ngOnChanges() will be called when Angular change detection propagates the new array value down to the child.当 Angular 更改检测将新数组值向下传播到子级时,将调用ngOnChanges()

You can also add a setter for your images which will be called whenever the value changes and you can set your default selected image in the setter itself:您还可以为您的图像添加一个设置器,每当值更改时都会调用该设置器,您可以在设置器本身中设置默认选择的图像:

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;
}

You can resolve it by simply changing few things.你可以通过简单地改变一些东西来解决它。

  export class ImageGalleryComponent implements OnInit {

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

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

 }

And as another one solution, you can simply *ngIf all template content until you get what you need from network:作为另一种解决方案,您可以简单地*ngIf所有模板内容,直到您从网络中获得所需内容:

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

And switch flag value in your fetching method:并在您的获取方法中切换标志值:

  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
   );
 }

In this way you will renderout child component only when parent will have all what child needs in static content.通过这种方式,只有当父组件在静态内容中拥有子组件所需的所有内容时,您才会渲染子组件。 It's even more useful when you have some loaders/spinners that represent data fetching state:当您有一些表示数据获取状态的加载器/微调器时,它会更有用:

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

But short answer to your questions:但对您的问题的简短回答:

  • When inputs are available?什么时候输入可用?
    • In OnInit hookOnInit钩子中
  • Why are not available to your child component?为什么对您的子组件不可用?
    • They are, but at this particular point in time they were not loaded他们是,但在这个特定的时间点他们没有被加载
  • What can I do with this?我能用这个做什么?
    • Patiently wait to render child component utul you get data in asynchronous manner OR learn child component to deal with undefined input state耐心等待渲染子组件 utul 以异步方式获取数据或学习子组件处理undefined输入状态

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM