PrimeNG TurboTable: Does dataKey need to be a column? - primeng

Does the dataKey property on p-table need to be defined as a column or does it simply need to be a property of the objects in the [value] array? If it needs to be a column, does this column need to be visible?

No, the dataKey does not need to be a column.
The dataKey should be a property of the record, but it doesn't need to be displayed in order to be used by the table.
HTML:
<p-table [columns]="cols" [value]="cars" [(selection)]="selectedCars" dataKey="vin">
<ng-template pTemplate="header" let-columns>
<th *ngFor="let col of columns">
{{col.header}}
</th>
</ng-template>
<ng-template pTemplate="body" let-car>
<tr>
<td>{{car.year}}</td>
</tr>
</ng-template>
</p-table>
Typescript:
export class TableDemo implements OnInit {
cars: Car[];
cols: any[];
constructor() { }
ngOnInit() {
this.cars = [
{ vin: '123ABC', year: 1994 },
{ vin: '234BCD', year: 1978 },
{ vin: '345CDE', year: 2015 },
];
this.cols = [
{ field: 'year', header: 'Year' }
];
}
}
PrimeNG Table Documentation

Related

How to disable sorting for selected columns in PrimeNG table?

I'm trying to disable sorting of selected columns based on Boolean value in PrimeNG table.
Below is my columns array
cols = [
{ field: 'name', header: 'Name', sort: true },
{ field: 'age', header: 'Age', sort: true },
{ field: 'gender', header: 'Gender', sort: false },
{ field: 'status', header: 'Status', sort: false }
];
Some of the columns having sort value as false and I need to disable those columns from sorting.
<p-table [columns]="cols" [value]="persons" [resizableColumns]="true" sortField="name" sortMode="single">
<ng-template pTemplate="header" let-columns>
<tr>
<th pResizableColumn *ngFor="let col of columns" [pSortableColumn]="col.field" [pSortableColumnDisabled]="col.field === 'name' || col.field === 'age'">
<div class="tb-heading">{{col.header}}</div>
<div class="sort-icons-container">
</div>
</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-rowData let-columns="columns">
<tr [pSelectableRow]="rowData">
<td *ngFor="let col of columns">
{{rowData[col.field]}}
</td>
</tr>
</ng-template>
</p-table>
I can use above code for disabling columns i.e., name and age by using pSortableColumnDisabled but I don't want to hardcode the column names like above as these columns will be passed dynamically by parent component.
Is there any way to disable the columns based on respective sort Boolean value?
After spending sometime on internet found the solution for above problem https://embed.plnkr.co/egBhS1DJhY2Ud1ByfGBp/
Changed the hardcoded condition from
[pSortableColumn]="col.field" [pSortableColumnDisabled]="col.field === 'name' || col.field === 'age'"
to
[pSortableColumnDisabled]="!col.sort ? true : false"

Item Array as Datasource for NgFor

Just want to ask if is it possible to search an item in an array of objects and use it as datasource for my angular table? Can you share the syntax please?
Basically, i have an array of objects consisting of Id and its json file. Populate its data. Look for an item that matches the it, and use it as datasource in a ngfor grid.
I have the following code, but it returns compilation error.
Thanks
ts file
jsonData: Array<{ id: string, json: object }> = [];
ngOnInit(): void {
let json= [
{
"Id": "1",
"Col1": "val11",
"Col2": "val12
},
{
"Id": "2",
"Col1": "val21",
"Col2": "val22
}
]
this.jsonData.push({ id: "100", json: json });
}
html file
<ng-container *ngFor="let row of this.jsonData.find(e => e.id === "101").json;let i=row.Id">
row.Col1 //etc..
</ng-container>
I Have used a different approach check if this helps you out . I am filtering data from the constructer itself (ts file)
here is the code link https://stackblitz.com/edit/angular-wn7vwb?file=app%2Ftable-basic-example.ts
HTML
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<!-- Position Column -->
<ng-container matColumnDef="position">
<th mat-header-cell *matHeaderCellDef> No. </th>
<td mat-cell *matCellDef="let element"> {{element.position}} </td>
</ng-container>
<!-- Name Column -->
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef> Name </th>
<td mat-cell *matCellDef="let element"> {{element.name}} </td>
</ng-container>
<!-- Weight Column -->
<ng-container matColumnDef="weight">
<th mat-header-cell *matHeaderCellDef> Weight </th>
<td mat-cell *matCellDef="let element"> {{element.weight}} </td>
</ng-container>
<!-- Symbol Column -->
<ng-container matColumnDef="symbol">
<th mat-header-cell *matHeaderCellDef> Symbol </th>
<td mat-cell *matCellDef="let element"> {{element.symbol}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
ts
import { Component } from '#angular/core';
const ELEMENT_DATA: PeriodicElement[] = [
{ position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H' },
{ position: 2, name: 'Helium', weight: 4.0026, symbol: 'He' },
{ position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li' },
{ position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be' },
{ position: 5, name: 'Boron', weight: 10.811, symbol: 'B' },
{ position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C' },
{ position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N' },
{ position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O' },
{ position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F' },
{ position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne' },
];
/**
* #title Basic use of `<table mat-table>`
*/
#Component({
selector: 'table-basic-example',
styleUrls: ['table-basic-example.css'],
templateUrl: 'table-basic-example.html',
})
export class TableBasicExample {
displayedColumns = ['position', 'name', 'weight', 'symbol'];
dataSource = ELEMENT_DATA;
constructor() {
this.dataSource = this.dataSource.filter((res) => res.name == 'Helium');
}
}
export interface PeriodicElement {
name: string;
position: number;
weight: number;
symbol: string;
}

Vue v-for nested array

I've got a nested array that I would like to display in a table. However, I can't get my nested array to show correctly.
My data set looks like this:
[
{
"dd":"February",
"md":[
{ "dag":"2020-02-01" },
{ "dag":"2020-02-02" },
{ "dag":"2020-02-03" }
]
},
{
"dd":"March",
"md":[
{ "dag":"2020-03-01" },
{ "dag":"2020-03-02" },
{ "dag":"2020-03-03" }
]
}
]
I would like a table which look like this.
| February | March |
| 2020-02-01 | 2020-03-01 |
| 2020-02-02 | 2020-03-02 |
| 2020-02-03 | 2020-03-03 |
I got this working, but it gives me 2 tables instead of one.
<template v-for="(md2, index) in md2s">
<table :key=index >
<thead >
<tr align="center">
<th style="width: 80px">{{md2}}</th>
</tr>
</thead>
<tr v-for="(date, index) in md2.md" :key=index>
<td align="center" >{{date.dag }}</td>
</tr>
</table>
</template>
All help is appreciated.
br. Erik
You could use a different way to create the loop (one table, multiple columns)
In this case, to populate each header with 'dd' and each column with md elements.
var data=[
{
"dd":"February",
"md":[
{
"dag":"2020-02-01"
},
{
"dag":"2020-02-02"
},
{
"dag":"2020-02-03"
}
]
},
{
"dd":"March",
"md":[
{
"dag":"2020-03-01"
},
{
"dag":"2020-03-02"
},
{
"dag":"2020-03-03"
}
]
}
];
new Vue({
el:'#app',
data:{
md2s: data
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.8/vue.js"></script>
<div id=app >
<table >
<thead >
<tr align="center">
<th v-for="(md2, index) in md2s" :key=index style="width: 80px">{{md2.dd}}</th>
</tr>
</thead>
<tbody>
<tr align="center">
<td v-for="(md2, index) in md2s" :key=index style="width: 80px">
<div v-for="(mdcol, col) in md2.md" :key=col>
{{mdcol.dag}}
</div>
</td>
</tr>
</tbody>
</table>
</div>
https://jsfiddle.net/bn5g1v09/1/
What you need is two diferent iterations. One for the header and another for the table body. For the header, all you need is to add the month name on order. The snippet shows with the computed property months how to do it. This completes the header iteration and the first.
The second one is a little more complex. You need to know beforehand how many lines there will be, for that I made a computed property maxLength that searches over each md and gives the greater one. Then for each row iterate over each month and then verify if the month has enough dates with v-if and if it does look up the desired date from the index and the nested data sctructure. That resumes the second iteration.
The below snippet is a working example with a more complex data showing what could happen with different md sizes and automatic month ordering.
var app = new Vue({
el: '#app',
data: () => ({
nested: [
{ "dd": "February",
"md": [{ "dag": "2020-02-01" },{ "dag": "2020-02-02" },{ "dag": "2020-02-03" },{ "dag": "2020-03-04" }]
},
{ "dd": "March",
"md": [{ "dag": "2020-03-01" },{ "dag": "2020-03-02" },{ "dag": "2020-03-03" }]
},
{ "dd": "January",
"md": [{ "dag": "2020-01-01" }]
}
]
}),
computed: {
staticMonths() {
return Array.from(Array(12),(e,i)=>new Date(25e8*++i).toLocaleString('en-US',{month: 'long'}));
},
months() {
return this.nested.map(item => item.dd).sort((a, b) => {
const A = this.staticMonths.indexOf(a);
const B = this.staticMonths.indexOf(b);
return A-B;
});
},
maxLength() {
return this.nested.reduce((accum, curr) => accum > curr.md.length ? accum : curr.md.length, 0);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table>
<thead>
<tr>
<th v-for="(item, index) in months">{{ item }}</th>
</tr>
</thead>
<tbody>
<tr v-for="index in maxLength">
<td v-for="item in months">
<span v-if="nested.find(nest => nest.dd === item).md.length > index-1">
{{nested.find(nest=>nest.dd===item).md[index-1].dag}}
</span>
</td>
</tr>
</tbody>
</table>
</div>

How to create a table in Angular based on a string value?

I'm using Angular 4/5 and I have to create several tables in the Angular based on a string value. Here is the model which I've made to create a table.
class NameValuePair {
Name: string;
Value: string;
}
export class Family {
Properties: Array<NameValuePair>;
PropertyType: string;
}
Given below is the hard-coded values which the table will contain.
export const list1: Family[] =
[
{
Properties: [
{
Name: "A",
Value: "1"
},
{
Name: "B",
Value: "2"
},
{
Name: "C",
Value: "3"
}
],
PropertyType: "Type 1"
},
{
Properties: [
{
Name: "A",
Value: "10"
},
{
Name: "B",
Value: "20"
},
{
Name: "C",
Value: "30"
}
],
PropertyType: "Type 1"
},
{
Properties: [
{
Name: "A",
Value: "100"
},
{
Name: "B",
Value: "200"
},
{
Name: "C",
Value: "300"
}
],
PropertyType: "Type 2"
}
]
Now, the main thing to note here is that the tables will be created based on the PropertyType. As in the above structure, the PropertyType of the first two elements of the array is same i.e. Type 1 so 2 tables will be created. One with the caption/heading: Type 1 and other with the caption: Type 2.
The properties[] array of the second array element will become the second row of the first table. I'm not able to find the logic on how do I create the tables based on this PropertyType string value. However, that's what I wrote in the component.html file but this logic is incorrect.
<div class="container pt-4" *ngFor="let element of list;let i = index">
<ng-container *ngIf="list[i].PropertyType == list[i+1].PropertyType">
<div style="padding-left:250px;font-size: 20px" class="pb-2">{{element.PropertyType}}</div>
<table id="{{element.PropertyType}}" class="table table-striped table-bordered table-responsive pb-3 mx-auto">
<thead style="height:40px">
<tr align="center">
<th *ngFor="let property of element.Properties" style="font-size: 20px">{{property.Name}}</th>
</tr>
</thead>
<ng-container *ngFor="let element1 of list">
<tr align="center" *ngIf="element.PropertyType == element1.PropertyType">
<td *ngFor="let property of element1.Properties; let propertyIndex = index" style="width: 200px">
<ng-container [ngSwitch]="propertyIndex">
<div *ngSwitchDefault style="font-size: 20px">{{property.Value}}</div>
</ng-container>
</td>
</tr>
</ng-container>
</table>
</ng-container>
</div>
list here refers to the list1 const array as mentioned above. Please help.
This is complete logic, I did not add css, since that is not asked. Use this it is working https://stackblitz.com/edit/angular-hd9jey
import {
Component
} from '#angular/core';
import {
list1
} from './list1';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
})
export class AppComponent {
tableArray = [];
constructor() {
let tableObject = {};
list1.forEach(i => {
if (tableObject[i.PropertyType]) {
tableObject[i.PropertyType].push(i);
} else {
tableObject[i.PropertyType] = [i];
}
this.tableArray = Object.entries(tableObject);
});
}
}
<div class="container pt-4" *ngFor="let table of tableArray;index as i">
<div style="padding-left:250px;font-size: 20px" class="pb-2">{{table[0]}}</div>
<table id="{{table[0]}}" class="table table-striped table-bordered table-responsive pb-3 mx-auto">
<thead style="height:40px">
<tr align="center">
<th *ngFor="let property of table[1][0].Properties" style="font-size: 20px">
{{property.Name}}</th>
</tr>
</thead>
<tr *ngFor="let property of table[1];" align="center">
<td *ngFor="let va of property.Properties">
{{va.Value}}
</td>
</tr>
</table>
</div>
You want to manipulate your list1 data into a nicer format first:
e.g.
export interface CustomTable {
header: string,
rows: Properties[]
}
const tables: CustomTable[] [];
list1.forEach(family => {
// Check if this type is already in the table.
const myTable: CustomTable = tables.find(customTable => customTable.header === family.propertyType);
if (myTable) {
// If it is, then add a new row
myTable.rows.push(family.Properties);
} else {
// If not, create a new table
myTable.rows.push({
header: family.propertyType,
rows: [family.Properties]
});
}
});
Then change your html accordingly. You probably want
<ng-container ngFor="let table of tables">
and within that somewhere a:
<tr *ngFor=let row of tables.rows>

Angular ng-options object linked to ng-repeat object

I have a problem with angular, using ng-options on a select tag, where the default selected element should be determined by a property in "company" from the ng-repeat element.
Look at this code:
<tr data-ng-repeat="company in companies">
<td><input data-ng-model="company.Name" value="{{company.Name}}" /></td>
<td>
<select data-ng-model="selectedSeller" data-ng-options="seller.Login for seller in sellers">
</select>
</td>
</tr>
I can set a default element on all elements by $scope.selectedSeller = $scope.companies[0] in the controller scope, but what I really want, is to set selected to whoever is responsible for the company by linking the seller object to the company object.
I need someway to implement "isSelected" from the code below.
<tr data-ng-repeat="company in companies">
<td><input data-ng-model="company.Name" value="{{company.Name}}" /></td>
<td>
<select data-ng-model="selectedSeller" isSelected="company.responsible == seller.id" data-ng-options="seller.Login for seller in sellers">
</select>
</td>
</tr>
Anyone know how to solve this?
Angular is data driven, so you specify just model and ng-model connects it to the inputs.
So you provide just ng-model, no value and no "isSelected"
Look at this example: http://jsbin.com/kiqazaxahe/edit?html,js,output
<table>
<tr ng-repeat="company in vm.companies">
<td><input data-ng-model="company.
name"/></td>
<td>
<select data-ng-model="company.responsible"
data-ng-options="seller.id as seller.login for seller in vm.sellers">
</select>
</td>
</tr>
</table>
<h2>Json vm.companies</h2>
<pre>{{vm.companies | json}}</pre>
And your data probably look like this:
var StackController = function() {
this.companies = [
{
name: 'company 1',
responsible : 1
},
{
name: 'company 2',
responsible : 2
},
{
name: 'company 3',
}
];
this.sellers = [
{
login : 'pavel',
id : 1,
},
{
login : 'petr',
id : 2,
},
{
login : 'igor',
id : 3,
}
];
};
angular.module('stackApp', [])
.controller('StackController', StackController);
Model of select is company.seller which is connected to seller.id, syntax of ng-options can determine what is used as identifier and what is label.
seller.id as seller.login for seller in vm.sellers
One more thing to add, identifier could be not just id but whole object.
Use the filter as:
<select data-ng-model="selectedSeller" data-ng-options="seller.Login for seller in sellers" ng-init="selectedSeller = $filter('filter')($scope.company, {responsible: seller.id})[0]">
</select>
var app = angular.module('stackApp', []);
app.controller('StackController', function($scope) {
$scope.companies = [
{
name: 'company 1',
responsible : 1
},
{
name: 'company 2',
responsible : 2
},
{
name: 'company 3',
}
];
$scope.sellers = [
{
login : 'pavel',
id : 1,
},
{
login : 'petr',
id : 2,
},
{
login : 'igor',
id : 3,
}
];
}).filter("filterSeller", function() {
return function(sellers, company) {
var seller = {};
angular.forEach(sellers, function(item, j) {
if(company.responsible == item.id){
seller = item;
return false;
}
});
return seller.id;
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<table ng-app="stackApp" ng-controller="StackController">
<tr ng-repeat="company in companies">
<td><input ng-model="company.name"/></td>
<td>
<select ng-model="company.responsibleSelected" ng-options="seller.id as seller.login for seller in sellers"
ng-init="company.responsibleSelected = (sellers | filterSeller:company)">
</select>
</td>
<td>{{company.responsibleSelected}}</td>
</tr>
</table>

Resources