9

I am following the templating option given in primeng docs to create a link with column data alongside in a primeng datatable column, but I am not able to show nested object using {{data[col.field]}}.

<p-column [field]="col.field" [header]="col.header" [sortable]="col.sortable" [filter]="col.filter" [editable]="col.editable" [filterPlaceholder]="col.filterPlaceholder" styleClass="{{col.class}}">
                <ng-template let-col let-data="rowData" let-ri="rowIndex" pTemplate="body">
                    <span *ngIf="!col.field.includes('.')" >{{data[col.field]}}</span>
                    <!-- <span *ngIf="col.field.includes('.')">{{data[col.field.split('.')[0]][col.field.split('.')[1]]}}</span> this does not work because field is like x.y-->
                    <!-- I have some other buttons here as well --> 
                </ng-template>
        </p-column>

How can I acheive this?

Sharing entire code -->

<p-dataTable [globalFilter]="gb" [filterDelay]=1000 [value]="tableData" [alwaysShowPaginator]="true" [rowStyleClass]="setStyle" [rows]="rows" [paginator]="paginate" [alwaysShowPaginator]="false" [resizableColumns]="true" tableStyleClass="table-wrap {{rowClass}}"
    [rowsPerPageOptions]="[5,10,20]" expandableRows="{{setExpander}}" [editable]="setEditable" (onRowClick)="handleRowSelect($event)" [lazy]="pagination" [totalRecords]="totalRecords" (onLazyLoad)="loadLazy($event)" [ngClass]="{'paginator-table': pagination}">
    <div *ngFor="let col of tableOptions.columns, let index=index, let odd=odd, let even=even">
        <p-column *ngIf="col.field" [field]="col.field" [header]="col.header" [sortable]="col.sortable" [filter]="col.filter" [editable]="col.editable" [filterPlaceholder]="col.filterPlaceholder" styleClass="{{col.class}}">
            <ng-template let-col let-data="rowData" let-ri="rowIndex" pTemplate="body">
                    <span *ngIf="!col.field.includes('.')" >{{data[col.field]}}</span>
                    <!-- <span *ngIf="col.field.includes('.')">{{data[col.field.split('.')[0]][col.field.split('.')[1]]}}</span> this does not work because field is like x.y-->
                <a *ngIf="col.field === 'ticket'" target="_blank" href={{link}}{{data[col.field]}}><i class="fa fa-external-link" aria-hidden="true"></i></a>
            </ng-template>
        </p-column>
    </div>
</p-dataTable>
AndrasCsanyi
  • 3,111
  • 7
  • 37
  • 65
RemyaJ
  • 4,368
  • 3
  • 16
  • 37
  • Can you share the entire `p-datatable` html code and the typescript which goes with it please ? Or even better, create a Plunker ? Also, you should remove the square brackets of the `field` property. – Antikhippe Jan 08 '18 at 07:24
  • If I make it a flat object everything works fine but how can I make it work for nested object. – RemyaJ Jan 08 '18 at 08:16
  • Can you share the relevant typescript code please ? – Antikhippe Jan 08 '18 at 08:29
  • I am following same code like example here but the data I get from backend is a nested obj for some fields. For nested obj, we can write car.color etc right? – RemyaJ Jan 08 '18 at 09:00

6 Answers6

19

PrimeNG DataTable is deprecated, use Table (AKA TurboTable) instead. https://www.primefaces.org/primeng-5-2-0-rc1-released-turbotable/

Anyways, you could access nested object inside Data-Table as follows:

<p-table [columns]="cols" [value]="data" ... >
  ...
  // Definition of table body
  <ng-template pTemplate="body" let-rowData let-columns="columns">
    <tr [pSelectableRow]="rowData">
      <td *ngFor="let col of columns">
         <div *ngIf="col.subfield;then nested_object_content else normal_content"></div>
         <ng-template #nested_object_content>
           {{rowData[col.field][col.subfield]}}
         </ng-template>
         <ng-template #normal_content>
           {{rowData[col.field]}}
         </ng-template>
      </td>
    </tr>
  </ngTemplate>
  ...
</p-table>

and in your component:

public data = [
{
  field1: {
    subfield1: 'test'
  },
  field2: 'test',
  field3: 'test',
  field4: {
    subfield4: 'test'
  }
}]

this.cols = [
  { field: 'field1', subfield: 'subfield1'},
  { field: 'field2'},
  { field: 'field3'},
  { field: 'field4', subfield: 'subfield4'},
];

I hope this helps you. :)

Bandeeta
  • 191
  • 1
  • 5
7

As a follow-up to what Bandeeta said about TurboTable: this solution can handle multiple nested properties instead of just one subfield:

<tr [pSelectableRow]="row">
  <td *ngFor="let col of columns">
    <span>{{ getCellData(row, col) }}</span>
  </td>
</tr>

And in your component:

getCellData(row: any, col: any): any {
   const nestedProperties: string[] = col.field.split('.');
   let value: any = row;
   for (const prop of nestedProperties) {
     value = value[prop];
   }

   return value;
 }
Stephanie
  • 478
  • 4
  • 11
2

You can do this using Angular Custom Pipe. Sample here.

app.component.html

<p-table [columns]="cols" [value]="cars">
    <ng-template pTemplate="header" let-columns>
        <tr>
            <th *ngFor="let col of columns">
                {{col.header}}
            </th>
        </tr>
    </ng-template>
    <ng-template pTemplate="body" let-rowData let-columns="columns">
        <tr>
            <td *ngFor="let col of columns">
                    {{rowData|field:col}}
            </td>
        </tr>
    </ng-template>
</p-table>

app.component.ts

import { Component } from "@angular/core";

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  cars = [
    {
      year: 2019,
      brand: {
        name: "Toyota"
      },
      color: "White",
      passengers: [
        {
          name: "John"
        }
      ]
    },
    {
      year: 2018,
      brand: {
        name: "Toyota"
      },
      color: "White",
      passengers: [
        {
          name: "Suzanne"
        }
      ]
    },
    {
      year: 2017,
      brand: {
        name: "Toyota"
      },
      color: "White",
      passengers: [
        {
          name: "Gökhan"
        }
      ]
    }
  ];
  cols: any[];
  constructor() {}

  ngOnInit() {
    this.cols = [
      { field: "year", header: "Year" },
      { field: "brand.name", header: "Brand" },
      { field: "color", header: "Color" },
      { field: "passengers.0.name", header: "Passengers" }
    ];
  }
}

Running example is here.

https://stackblitz.com/edit/angular6-primeng-qhxptl

gkhnclk
  • 114
  • 1
  • 5
  • Worked like a charm, the stackblitz demo was exactly what I was looking for. Thanks @gkhnclk for a simple solution – Anmol Jain Apr 07 '20 at 21:06
1

There is a great article on how to do this in a type safe way here but if safety isn't an issue, you can use Lodash' get function which saves having to deviate from the PrimeNG basic table use with crazy subfield stuff.

// in .ts file
import {get} from 'lodash';

// give it a different name if you like
_ = get;

// columns
this.columns = [
    {field: 'user.address.postcode', header: 'Post Code'}
]

Then in the html

<p-table [columns]="columns" [value]="users$ | async" styleClass="p- 
datatable-sm">
<ng-template pTemplate="header" let-columns>
<tr>
  <th *ngFor="let col of columns">
    {{col.header}}
  </th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-rowData let-columns="columns">
<tr>
  <td *ngFor="let col of columns">
    {{_(rowData, col.field)}}
  </td>
</tr>
</ng-template>
</p-table>
MFB
  • 16,000
  • 24
  • 66
  • 111
0

This might be a little bit late, but I ended up with a bit different solution. I have my own table component based on p-table and I bind the columns, rows, etc.

I created a component specifically for this, then I bind the current row and column

<ng-template pTemplate="body" let-rowData let-columns="columns">
    <tr [pSelectableRow]="rowData">
        <td *ngFor="let col of columns">
          <app-table-column [column]="col" [row]="rowData"></app-table-column>
        </td>
    </tr>
</ng-template>

This is my table-column component, I'm sharing a very basic stuff but you can improve it as you wish/need.

I'm using lodash to get the row's value based on the field (column), it works for dotted (nested properties) or for flat properties.

    import { Component, Input, OnInit } from '@angular/core';
import * as moment from 'moment';
import * as _ from 'lodash';

@Component({
  template: `
    <span>
      {{ value }}
    </span>
  `,
  selector: 'app-table-column',
})
export class TableColumnComponent implements OnInit{

  @Input() column;
  @Input() row;

  value: any;

  constructor() {}

  ngOnInit(): void {
    this.parseValue(_.get(this.row, this.column.field));
  }

  parseValue(value) {
    switch (this.column.type) {
      case 'date':
        this.value = moment(value);
        break;

      default:
        this.value = value;
      break;
    }
  }

}
jalex19
  • 146
  • 7
0

this.cols = [
      { field: 'value1', header: 'Value 1' },
      { field: 'value2', header: 'Value 2', element: true },
    ];
<td class="" *ngFor="let col of columns">
  <span class="ui-column-title">{{ col.header }} : </span>
  {{col.element ? rowData[col.field].label : rowData[col.field]}}
</td>
Arif Cemre
  • 21
  • 2