【问题标题】:Angular: In which lifecycle hook is input data available to the ComponentAngular:生命周期钩子是组件可用的输入数据
【发布时间】: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(),它应该可以工作。

标签: javascript angular typescript


【解决方案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输入状态

    【讨论】:

      猜你喜欢
      • 2017-11-22
      • 2019-06-26
      • 1970-01-01
      • 2020-03-15
      • 2016-07-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多