Estoy pasando datos asíncronos de un componente primario a un componente secundario. Y el componente hijo necesita saber la longitud de los datos para hacer algo.

El problema es que el componente secundario no puede usar el gancho 'Oninit' para trabajar porque los datos no están disponibles en este momento. Entonces, ¿cómo hago esto?

El código del componente principal se ve así:

@Component({
    moduleId: module.id,
    selector: 'parent',
    template: `<div>
                    <child [items]="items | async">
                </div>`
})

export class Parent implements OnInit {

    items: Items[];

    constructor(
        private itemService: ItemService,
        private router: Router
    ) { }    

    ngOnInit() {
        this.itemService.getItemss()
            .subscribe(
                items => this.items = items,
                error => this.errorMessage = <any>error
            );
    }

}

Y el componente hijo se ve así:

@Component({
    moduleId: module.id,
    selector: 'child',    
    template: `<div>
                    <div *ngFor="let itemChunk of itemChunks"></div>
                    content here
                </div>`
})
export class child implements OnInit{
    @Input() items: Items[];
    itemChunks: Items[][];

    ngOnInit() {
        this.itemChunks = this.chunk(this.Items);
    }

    chunk(items: Items[]) {
        let result = [];
        for (var i = 0, len = items.length; i < len; i += 6) { // this line causes the problem since 'items' is undefined
            result.push(items.slice(i, i + 6));
        }
        return result;
    }
}

¿Cuál es la mejor práctica para manejar esto?

38
Allen Zhang 30 dic. 2016 a las 03:38

3 respuestas

La mejor respuesta

Hay tres maneras de hacer esto:

  1. Pon un *ngIf en padre. Solo renderizar hijo cuando el items padre está listo.
<div *ngIf="items">
   <child [items]="items | async">
</div>
  1. Separe su entrada getter setter en niño. Luego, actúe siempre que se establezca el valor, también puede usar RxJS BehaviorSubject.
private _items = new BehaviorSubject<Items[]>([]);

@Input() set items(value: Items[]) { 
    this._items.next(value); 
}

get items() {
   return this._items.getValue();
}

ngOnInit() {
    this._items.subscribe(x => {
       this.chunk(x);
    })
}
  1. Hazlo durante el ngOnChanges del niño. Consulte aquí por ejemplo. https://angular.io/docs/ts/latest /guide/lifecycle-hooks.html#!#onchanges
73
Collin M. Barrett 21 ene. 2020 a las 21:31

Puedes usar una setter:

export class child implements OnInit{
    itemChunks: Items[][];

    private _items ;

    //bellow  will get called when ever the items are passed in by the parent component
    @Input( 'items' ) set items ( items: Items[] ) {
       this._items = items;

       this.itemChunks = this.chunk(this._items);
    }




    chunk(items: Items[]) {
        let result = [];
        for (var i = 0, len = items.length; i < len; i += 6) { // this line causes the problem since 'items' is undefined
            result.push(items.slice(i, i + 6));
        }
        return result;
    }
}



Por cierto, siento que su componente principal no es correcto también, debería ser:

@Component({
    moduleId: module.id,
    selector: 'parent',
    template: `<div>
                    <child [items]="items | async">
                </div>`
})

export class Parent implements OnInit {

    items: Items[];

    constructor(
        private itemService: ItemService,
        private router: Router
    ) { 
         this.items = this.itemService.getItemss(); // if getItemss is returning an observable, which I think it does
     }    

}
2
Milad 9 ene. 2017 a las 04:36

Solución más simple:

ngOnChanges(changes: SimpleChanges) {
    if (changes['items'].currentValue) {
        this.items = items
    }
}
6
DanielWaw 7 feb. 2019 a las 09:11