Arm144
Arm144

Reputation: 773

Create table with rows from object

I am facing with a problem creating a table with an array of nested objects.

I have an array with this schema:

array = [
    {
        id: 'Column1',
        rows: {
            row1: 'A',
            row2: 'B',
            row3: 'C',
            row4: 'D'
        }
    },
    {
        id: 'Column2',
        rows: {
            row1: 'E',
            row2: 'F',
            row3: 'G',
            row4: 'H'
        }
    },
    {
        id: 'Column3',
        rows: {
            row1: 'I',
            row2: 'J',
            row3: 'K',
            row4: 'L'
        }
    }
];

And I am trying to create a table that looks like this: enter image description here

I was able to create the columns heads but I dont know how to do the body of it.

Any clue about how to work with it?

<table class="table table-users table-fixed table-striped">
    <caption></caption>
    <thead>
      <tr>
        <th scope="col" class="col-xs-2">
        </th>
        <th scope="col" class="col-xs-2" *ngFor="let col of array">{{col.id}}</th>
      </tr>
    </thead>
    <tbody class="table-data">
    </tbody>
</table>

Thank you for your time.

Upvotes: 1

Views: 229

Answers (3)

Marcel Hoekstra
Marcel Hoekstra

Reputation: 1488

Personally I would make your template as simple as possible and convert the data just after it's retrieved. Something like this:

public table = this.convertToTable(this.array);

private convertToTable(array: { id: string, rows: any }[]) {
    // get the property name and value of the columns rows.
    // sort the array by length so the first and largest array can be used to get the properties names.
    const columnRows = array.map(a => ((Object as any).entries(a.rows)).
        map(([rowHeader, rowValue]) => ({ rowHeader, rowValue })))
        .sort((a, b) => b.length - a.length);
    // get the headers
    const headers = array.map(a => a.id);
    // create rows by looping through the largest array with rowValues
    const rows = columnRows.length > 0 ?
        columnRows[0].map((v, rowIndex) => {
            const rowValues = headers.map((_, columnIndex) => {
                const row = columnRows.length > columnIndex ? columnRows[columnIndex] : [];
                const rowValue = row.length > rowIndex ? row[rowIndex].rowValue : null;
                return rowValue;
            });
            return {
                rowHeader: v.rowHeader,
                rowValues
            };
        }) : [];
    return { headers, rows };
}

And the Html:

<table border='1' class="table table-users table-fixed table-striped">
    <caption></caption>
    <thead>
      <tr>
        <th scope="col" class="col-xs-2">
        </th>
        <th scope="col" class="col-xs-2" *ngFor="let header of table.headers">
          {{header}}</th>
      </tr>
    </thead>
    <tr *ngFor="let row of table.rows">
      <td>{{row.rowHeader }}</td>
      <td *ngFor="let value of row.rowValues">
        {{value}}
        </td>
    </tr>
</table>

Upvotes: 1

Arm144
Arm144

Reputation: 773

So finally I found a solution spliting the original array in 2, the original used for the columns and the second to show the rows.

After that created a method to only obtain the values for each row.

Here is the solution: https://stackblitz.com/edit/angular-bn2mgw

Upvotes: 0

Pardeep Jain
Pardeep Jain

Reputation: 86730

You need to use nested *ngFor like this -

<table border='1' class="table table-users table-fixed table-striped">
    <caption></caption>
    <thead>
      <tr>
        <th scope="col" class="col-xs-2">
        </th>
        <th scope="col" class="col-xs-2" *ngFor="let col of array">{{col.id}}</th>
      </tr>
    </thead>
    <tr *ngFor="let col of array">
      <th *ngFor="let item of col.rows | keyvalue; let i = index">
        <span *ngIf='!i'>{{item.key}} </span>
        <span *ngIf='i'>{{item.value}}</span>
        </th>
    </tr>
</table>

Working Example

Upvotes: 3

Related Questions