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

IT技术 javascript angular typescript
2021-03-08 15:49:18

我有一个组件,它接收一组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>
...
4个回答

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

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

为了解决您的问题,当数据从服务器返回时,为父属性分配一个新数组。ngOnChanges()在孩子身上实施ngOnChanges()当 Angular 变化检测将新数组值向下传播到子级时将被调用。

我同意你的看法,因为我自己也面临着同样的问题。即使在我ngOnchanges()在孩子身上实施之后,错误也Cannot read property '0' of undefined来了。但是,该应用程序可以毫无问题地继续使用。出现此错误是因为最初未填充输入属性。它由ngOnChanges()收到异步调用数据时的第二次调用填充就我而言,除了此解决方案之外,我还在 html 中添加了针对空运算符防护它清楚地解决了错误。
2021-05-06 15:49:18
@Mark ..很好地解释并解决了我的问题。
2021-05-15 15:49:18

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

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

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

  export class ImageGalleryComponent implements OnInit {

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

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

 }
注意,您需要添加implements OnChanges才能使用ngOnChanges()方法。
2021-05-13 15:49:18

作为另一种解决方案,您可以简单地使用*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输入状态