Búsqueda de sitios web

typescript - El componente hijo de Angular 2 no se actualiza cuando cambia la fuente de datos


He creado un componente de tabla que genera una tabla basada en una configuración que paso. Soy consciente de que probablemente hay mejores maneras de escribir un componente reutilizable, pero por ahora esto es lo que tengo.

Básicamente, el componente toma una configuración con un objeto de columnas y un objeto de fuente de datos, luego mapea las columnas a las propiedades en la fuente de datos.

Mi problema es que en algún momento hago cambios en una de las fuentes de datos de la tabla, primero vacío el arreglo, por ejemplo...

this.members.datasource = []

entonces empujo los nuevos datos al origen de datos así...

for (let member in members) {
            this.members.datasource.push(members[member]);
}

Mi problema es que cuando hago esto, la tabla no se actualiza con los nuevos datos en la fuente de datos.

He utilizado la función ngDoCheck en mi componente y efectivamente se activa cuando cambio la fuente de datos, pero no sucede nada más.

Aquí está todo el código y marcado relativo...

componente ip-datatable

import { Component, Input, Output, OnInit, EventEmitter, ElementRef, DoCheck } from "@angular/core";

import { ITable } from "./interfaces/index";

@Component({
    selector: "ip-datatable",
    templateUrl: "./ip-dataTable.component.html"
})
export class IpDataTableComponent implements OnInit, DoCheck {
    @Input()
    public config: ITable;
    public tableData: any;

    @Output()
    private onRowClick = new EventEmitter();

    constructor(private elem: ElementRef) {

    }

    public ngOnInit() {
        // TODO: Decide whether or not to make this more dynamic (ie: excepts functions returning things other than a promise)
        if (typeof (this.config.datasource) == "function") {
            this.config.datasource()
                .then((data: any) => {
                    this.tableData = data;
                });
        } else {
            this.tableData = this.config.datasource;
        }
    }

    public rowClicked(e:Event) {
        this.onRowClick.emit(e);
    }

    public ngDoCheck() {

    }
}

El HTML de la plantilla del componente ip-datatable

<table class="table table-bordered table-hover">
    <thead>
    <tr>
        <td *ngFor="let column of config.columns">{{column.header}}</td>
    </tr>
    </thead>
    <tbody>
    <tr 
        *ngFor="let item of tableData" 
        [class]="(config.rowConfig !== undefined) ? config.rowConfig.classes : ''" 
        (click)="rowClicked($event)" [id]="(item.id !== undefined) ? item.id : ''">
        <td *ngFor="let column of config.columns">{{item[column.mapKey]}}</td>
    </tr>
    </tbody>
</table>

Componente que utiliza ip-datatable

import { Component, OnInit, AfterViewInit, ApplicationRef } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { EngagementService } from "../../services/engagement.service";

import { EngagementEditModel } from "../../models/index";
import { ITable } from "../../core/components/ip-datatable/interfaces/index";

@Component({
    templateUrl: "engagement-context.component.html"
})

export class EngagementContextComponent {
    public id: string;
    public engagementDetails: EngagementEditModel;

    // Temporarily hardcode groups as there is only two available.
    public groups: ITable = {
        columns: [
            {
                header: "Name",
                mapKey: "name"
            }
        ],
        rowConfig: {
            classes: "clickable override"
        },
        datasource: [
        {
            name: "Owners"
        },
        {
            name: "Users"
        }]
    };

    public members: ITable = {
        columns: [
            {
                header: "Name",
                mapKey: "name"
            }
        ],
        datasource: []
    }

    constructor(private engagementService: EngagementService, private route: ActivatedRoute, private appRef: ApplicationRef) {
        this.id = route.snapshot.params["id"];
            this.engagementService.getEngagementEditDetailsById(parseInt(this.id))
            .then((data: EngagementEditModel) => {
                this.engagementDetails = data;
                console.log(this.engagementDetails); // tslint:disable-line
            });
    }

    public groupClicked(e: Event) {
        // Get the element of the event
        let elem: IEventElement = e.currentTarget;
        // Get the name of the selected node
        let groupName = elem.textContent.trim().toLowerCase();
        let members = (<any>this.engagementDetails)[groupName];

        interface IEventElement extends EventTarget {
            textContent?: string;
        }

        this.members.datasource = [];

        for (let member in members) {
            this.members.datasource.push(members[member]);
        }
    }
}

Uso de HTML del ip-datatable

<div class="row">
        <div class="col-md-12">
            <section class="section">
                <div class="section-body">
                    <h4 class="section-title">Groups</h4>
                    <ip-datatable
                        [config]="groups"
                        (onRowClick)="groupClicked($event)">
                    </ip-datatable>
                </div>
            </section>
        </div>
        <div class="col-md-12">
            <section class="section">
                <div class="section-body">
                    <h4 class="section-title">Members</h4>
                    <ip-datatable
                        [config]="members"
                    ></ip-datatable>
                </div>
            </section>
        </div>
    </div>

Respuesta #1

Está bien, entonces mi problema era que en ngOnInit asigno this.config.datasource a una variable llamada tableData. Bueno, cuando ocurre el cambio, nunca se reasigna a esa variable, así que al llamar a la función ngDoCheck y luego asignar this.config.datasource a tableData, obtengo el cambio reflejado como quería.

Dado que he oído hablar del perjuicio en el rendimiento de ngDoCheck, podría hacer otra cosa si empiezo a notar desventajas en el rendimiento. Si alguien pudiera dar alguna idea al respecto en los comentarios, lo agradecería.

Respuesta #2

Puedes usar ChangedetectionStartegy.onPush(), ya que esto actualiza los datos automáticamente y detecta los cambios cada vez que cambia la fuente de datos y notifica a la interfaz de usuario de la misma manera, también considera declarar tu fuente de datos como un arreglo.

Artículos relacionados