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:生命周期钩子是组件可用的输入数据的主要内容,如果未能解决你的问题,请参考以下文章