Угловой: в котором ловушка жизненного цикла является входными данными, доступными Компоненту.

86

У меня есть компонент, который получает массив 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 subscribe () к наблюдаемому http.
Mark Rajcok
@MarkRajcok images- это всего лишь часть данных, которые используются таким родителем, {profile: {firstName: "abc", lastName: "xyz", images: [ ... ]}}что означает, что если я подпишусь в дочернем элементе , мне все равно придется подписываться на родительский элемент, и я бы хотел избежать повторения
Оптимус Петт
Если массив изображений в родительском компоненте заполняется при создании дочернего компонента, то свойство ввода изображений должно быть заполнено до вызова ngOnInit (). Вам нужно будет предоставить больше информации о том, как массив изображений заполняется в родительском компоненте, чтобы кто-нибудь мог вам помочь (или создать минимальный Plunker, показывающий проблему).
Mark Rajcok
@MarkRajcok Я добавил родительский компонент и то, как в нем заполняются изображения.
Optimus Pette,
2
Итак, да, похоже, что ваш родительский компонент использует http (поскольку он использует службу) для заполнения своего свойства images. Поскольку это асинхронная операция, свойство input дочернего компонента не будет заполнено к моменту вызова его метода ngOnInit (). Переместите свой код из ngOnInit () в ngOnChanges (), и он должен работать.
Mark Rajcok

Ответы:

89

Свойства ввода заполняются до ngOnInit()вызова. Однако при этом предполагается, что родительское свойство, которое передает свойство input, уже заполнено при создании дочернего компонента.

В вашем сценарии это не так - данные изображений заполняются асинхронно из службы (следовательно, HTTP-запрос). Следовательно, свойство input не будет заполнено при ngOnInit()вызове.

Чтобы решить вашу проблему, когда данные возвращаются с сервера, назначьте новый массив родительскому свойству. Реализуйте ngOnChanges()в детстве. ngOnChanges()будет вызываться, когда обнаружение изменения Angular распространяет новое значение массива на дочерний элемент.

Марк Райкок
источник
1
Я согласен с вами, потому что сам столкнулся с такой же проблемой. Даже после того, как внедрил ngOnchanges()в ребенка, ошибка Cannot read property '0' of undefinedвылетает. Но приложение может работать без проблем. Эта ошибка возникает из-за того, что изначально свойство input не было заполнено. Он заполняется при втором вызове ngOnChanges()при получении данных из асинхронного вызова. В моем случае, в дополнение к этому решению, я добавил защиту от нулевого оператора в html. Это явно решило ошибку.
Thilina Samiddhi
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];
      }
  }

 }
радж пател
источник