Angular: en el que el gancho del ciclo de vida son datos de entrada disponibles para el componente

82

Tengo un componente que recibe una serie de imageobjetos como Inputdatos.

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

Me gustaría que cuando el componente cargue el selectedImagevalor se establezca en el primer objeto de la imagesmatriz. He intentado hacer esto en el OnInitgancho del ciclo de vida de esta manera:

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

esto me da un error Cannot read property '0' of undefinedque significa que el imagesvalor no está establecido en esta etapa. También probé el OnChangesgancho, pero estoy atascado porque no puedo obtener información sobre cómo observar los cambios de una matriz. ¿Cómo puedo lograr el resultado esperado?

El componente principal se ve así:

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

La plantilla del componente principal tiene esto

...
<image-gallery [images]="images"></image-gallery>
...
Optimus Pette
fuente
1
¿Cómo se imagesrellenan los datos en el componente principal? Es decir, ¿es a través de una solicitud http? Si es así, es mejor que tenga el ImageGalleryComponent subscribe () al http observable.
Mark Rajcok
@MarkRajcok imagesson solo una parte de los datos que utiliza el padre de esta manera, lo {profile: {firstName: "abc", lastName: "xyz", images: [ ... ]}}que significa que si me suscribo al niño, aún tendré que suscribirme al padre y me gustaría evitar la repetición
Optimus Pette
Si la matriz de imágenes en el componente principal se completa cuando se crea el componente secundario, entonces la propiedad de entrada de imágenes debe completarse antes de llamar a ngOnInit (). Debería proporcionar más información sobre cómo se completa la matriz de imágenes en el componente principal para que cualquiera pueda ayudarlo más (o crear un Plunker mínimo que muestre el problema).
Mark Rajcok
@MarkRajcok He agregado el componente principal y cómo se rellenan las imágenes.
Optimus Pette
2
Entonces, sí, parece que su componente principal está usando http (ya que está usando un servicio) para completar su propiedad de imágenes. Dado que se trata de una operación asincrónica, la propiedad de entrada del componente secundario no se completará cuando se llame a su método ngOnInit (). Mueva su código de ngOnInit () a ngOnChanges () y debería funcionar.
Mark Rajcok

Respuestas:

84

Las propiedades de entrada se rellenan antes de que ngOnInit()se llame. Sin embargo, esto supone que la propiedad principal que alimenta la propiedad de entrada ya está poblada cuando se crea el componente secundario.

En su escenario, este no es el caso: los datos de las imágenes se completan de forma asincrónica desde un servicio (de ahí una solicitud http). Por lo tanto, la propiedad de entrada no se completará cuando ngOnInit()se llame.

Para resolver su problema, cuando los datos se devuelvan desde el servidor, asigne una nueva matriz a la propiedad principal. Implementar ngOnChanges()en el niño. ngOnChanges()se llamará cuando la detección de cambio angular propague el nuevo valor de la matriz hasta el niño.

Mark Rajcok
fuente
1
Estoy de acuerdo con usted porque yo mismo me enfrenté a este mismo problema. Incluso después de implementar ngOnchanges()en el niño, Cannot read property '0' of undefinedaparece el error . Pero la aplicación puede continuar sin problemas. Este error se debe a que inicialmente no se ha completado la propiedad de entrada. Se completa con la segunda llamada ngOnChanges()cuando se reciben los datos de la llamada asíncrona. Para mi caso, además de esta solución, agregué la protección contra el operador nulo en el html. Claramente resolvió el error.
Thilina Samiddhi
5

También puede agregar un configurador para sus imágenes que se llamará cada vez que cambie el valor y puede establecer su imagen seleccionada por defecto en el propio configurador:

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
fuente