Angular: у якому гачок життєвого циклу - це вхідні дані, доступні для компонента


87

У мене є компонент, який отримує масив 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>
...

1
Як imagesзаповнюються дані в батьківському компоненті? Тобто це через запит (и) http? Якщо так, вам може бути краще, якщо ImageGalleryComponent підписаться () на http спостережуваний.
Марк Райчок

@MarkRajcok - imagesце лише частина даних, яку використовує батько, наприклад, це {profile: {firstName: "abc", lastName: "xyz", images: [ ... ]}}означає, що якщо я підпишуся на дитину, мені все одно доведеться підписатись на батьків, і я хотів би уникнути повторення
Optimus Pette

Якщо масив images у батьківському компоненті заповнюється, коли створюється дочірній компонент, тоді властивість вводу images повинна бути заповнена до виклику ngOnInit (). Вам потрібно буде надати більше інформації про те, як масив зображень заповнюється в батьківському компоненті, щоб хтось допоміг вам далі (або створити мінімальний Plunker, що відображає проблему).
Mark Rajcok

@MarkRajcok Я додав батьківський компонент та спосіб заповнення зображень у ньому.
Optimus Pette

2
Так так, схоже, ваш батьківський компонент використовує http (оскільки він використовує службу) для заповнення властивості images. Оскільки це асинхронна операція, властивість введення дочірнього компонента не буде заповнена до моменту виклику його методу ngOnInit (). Перемістіть свій код з ngOnInit () в ngOnChanges (), і він повинен працювати.
Mark Rajcok

Відповіді:


90

Властивості вводу заповнюються перед ngOnInit()викликом. Однак це передбачає, що батьківська властивість, яка подає властивість введення, вже заповнена, коли створюється дочірній компонент.

У вашому сценарії це не так - дані зображень заповнюються асинхронно від служби (отже, запит http). Отже, властивість input не буде заповнено при ngOnInit()виклику.

Щоб вирішити вашу проблему, коли дані повертаються із сервера, призначте новий масив батьківській властивості. Впровадити ngOnChanges()в дитину. ngOnChanges()буде викликано, коли Angular виявлення змін поширює нове значення масиву до нащадка.


1
Я погоджуюсь з вами, бо я сам стикався з цією ж проблемою. Навіть після того, як я впровадив ngOnchanges()у дитину, помилка Cannot read property '0' of undefinedприходить. Але додаток може працювати без проблем. Ця помилка виникає через те, що спочатку властивість input не була заповнена. Він заповнюється другим викликом часу, ngOnChanges()коли надходять дані з асинхронного виклику. Для мого випадку, додатково до цього рішення, я додав захист від нульового оператора в html. Це чітко вирішило помилку.
Тіліна Саміддхі

7

Ви також можете додати сеттер для ваших зображень, який буде викликатися, коли значення змінюється, і ви можете встановити вибране за замовчуванням зображення в самому сеттері:

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

1

Ви можете вирішити це, просто змінивши кілька речей.

  export class ImageGalleryComponent implements OnInit {

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

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

 }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.