Kątowy: w którym hak cyklu życia jest danymi wejściowymi dostępnymi dla komponentu

85

Mam komponent, który otrzymuje tablicę imageobiektów jako Inputdane.

export class ImageGalleryComponent {
  @Input() images: Image[];
  selectedImage: Image;
}

Chciałbym, gdy składnik ładuje selectedImagewartość ustawioną na pierwszy obiekt imagestablicy. Próbowałem to zrobić w OnInithaku cyklu życia w następujący sposób:

export class ImageGalleryComponent implements OnInit {
  @Input() images: Image[];
  selectedImage: Image;
  ngOnInit() {
    this.selectedImage = this.images[0];
  }
}

to daje mi błąd, Cannot read property '0' of undefinedco oznacza, że imageswartość nie jest ustawiona na tym etapie. Próbowałem też OnChangeshaka, ale utknąłem, ponieważ nie mogę uzyskać informacji o tym, jak obserwować zmiany w tablicy. Jak mogę osiągnąć oczekiwany efekt?

Komponent nadrzędny wygląda następująco:

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

Szablon komponentu nadrzędnego ma to

...
<image-gallery [images]="images"></image-gallery>
...
Optimus Pette
źródło
1
W jaki sposób imagesdane są wypełniane w komponencie nadrzędnym? To znaczy, czy odbywa się to za pośrednictwem żądań http? Jeśli tak, może lepiej byłoby, gdyby składnik ImageGalleryComponent subskrybował () obserwowalny http.
Mark Rajcok
@MarkRajcok imagesto tylko część danych, które są używane przez rodzica w ten {profile: {firstName: "abc", lastName: "xyz", images: [ ... ]}}sposób, co oznacza, że ​​jeśli zapiszę się w dziecku, nadal będę musiał zapisać się dla rodzica i chciałbym uniknąć powtórzenia
Optimus Pette
Jeśli tablica images w komponencie nadrzędnym jest zapełniana podczas tworzenia komponentu podrzędnego, wówczas właściwość input images powinna zostać wypełniona przed wywołaniem funkcji ngOnInit (). Będziesz musiał podać więcej informacji o tym, jak tablica obrazów jest zapełniana w komponencie nadrzędnym, aby ktokolwiek mógł ci dalej pomóc (lub stworzyć minimalny Plunker pokazujący problem).
Mark Rajcok
@MarkRajcok Dodałem komponent nadrzędny i sposób umieszczania w nim obrazów.
Optimus Pette
2
Więc tak, wygląda na to, że komponent nadrzędny używa protokołu http (ponieważ korzysta z usługi) do wypełniania właściwości images. Ponieważ jest to operacja asynchroniczna, właściwość input komponentu potomnego nie zostanie zapełniona do czasu wywołania jego metody ngOnInit (). Przenieś swój kod z ngOnInit () do ngOnChanges () i powinno działać.
Mark Rajcok,

Odpowiedzi:

88

Właściwości wejściowe są wypełniane przed ngOnInit()wywołaniem. Zakłada się jednak, że właściwość nadrzędna, która dostarcza właściwości wejściowe, jest już wypełniona podczas tworzenia komponentu podrzędnego.

W Twoim scenariuszu tak nie jest - dane obrazów są wypełniane asynchronicznie z usługi (stąd żądanie http). W związku z tym właściwość input nie zostanie wypełniona po ngOnInit()wywołaniu.

Aby rozwiązać problem, po zwróceniu danych z serwera przypisz nową tablicę do właściwości nadrzędnej. Zaimplementuj ngOnChanges()w dziecku. ngOnChanges()zostanie wywołana, gdy wykrywanie zmian kątowych propaguje nową wartość tablicy do elementu podrzędnego.

Mark Rajcok
źródło
1
Zgadzam się z tobą, ponieważ ja sam stanąłem przed tym samym problemem. Nawet po zaimplementowaniu ngOnchanges()w dziecku błąd Cannot read property '0' of undefinedpojawia się. Jednak aplikacja może działać bez problemu. Ten błąd występuje, ponieważ początkowo właściwość wejściowa nie została wypełniona. Jest zapełniany przy drugim wywołaniu w ngOnChanges()momencie odebrania danych z wywołania asynchronicznego. W moim przypadku dodatkowo do tego rozwiązania dodałem ochronę przed operatorem null w html. To wyraźnie rozwiązało błąd.
Thilina Samiddhi
7

Możesz także dodać metodę ustawiającą dla swoich obrazów, która będzie wywoływana za każdym razem, gdy zmieni się wartość, i możesz ustawić domyślny wybrany obraz w samym programie ustawiającym:

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;
}
Sachin Parashar
źródło
1

Możesz go rozwiązać, po prostu zmieniając kilka rzeczy.

  export class ImageGalleryComponent implements OnInit {

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

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

 }
raj patel
źródło