I have a simple subtraction of two values in a table, shown below. In most cases the new value is less than the old value, however in some cases when new stock is added, the new value will be higher than the old. Is there a way to show a '+' or '-' beside the 'Difference' label depending on what the change has been.
Thanks in advance!
<tr *ngFor="let change of stockChanges">
<td scope="row">
{{change.oldValue}}
<div class="stockLabel">
Old
</div>
</td>
<td>
{{change.newValue}}
<div class="stockLabel">
New
</div>
</td>
<td>
({{change.newValue - change.oldValue}})
<div class="stockLabel">
Difference
</div>
</td>
</tr>
<tr *ngFor="let change of documentaryList">
<td scope="row">
{{change.oldValue}}
<div class="stockLabel">
Old
</div>
</td>
<td>
{{change.newValue}}
<div class="stockLabel">
New
</div>
</td>
<td>
({{(change.newValue - change.oldValue) >= 0 ? (change.newValue - change.oldValue) : -(change.newValue - change.oldValue)}}
{{change.newValue > change.oldValue ? '+' : '-'}}
)
<div class="stockLabel">
Difference
</div>
</td>
</tr>
You could implement signedNumber pipe:
#Pipe({
name: 'signedNumber',
})
export class SignedNumberPipe implements PipeTransform {
constructor(private format: NumberFormatService) {}
transform(value: number, digitsInfo?: string): string | null {
if (value === null || value === undefined) {
return null;
} else {
return this.sign(value) + this.format.format(value, digitsInfo);
}
}
sign(v: number) {
return v > 0 ? '+' : '';
}
}
Then use it in template:
<td>
({{(change.newValue - change.oldValue) | signedNumber:'1.0-2'}})
<div class="stockLabel">
Difference
</div>
</td>
The "1.0-2" is the format also used by Angular's standard number pipe.
Related
I have table and I want to use show more/ show less text pos.report. currently when I click show more for one <td> it show more for all <td> in the table.
.Html
<tbody>
<tr *ngFor="let pos of procedureOrderList; let i = index">
<td>{{pos.investigationName}}</td>
<td>{{pos.investigationDate | date: 'dd-MM-yyyy'}}</td>
<td>{{pos.released_DT | date: 'dd-MM-yyyy'}}</td>
<td> <ng-container *ngIf="pos.report">
{{(show) ? pos.report : pos.report | slice:0:50}}
<a *ngIf="pos.report.length > 0;" (click)="show = !show" > ...{{ show ? '[Show less]': '[Show More]' }}</a>
</ng-container>
</td>
</tr>
</tbody>
.ts
show = false;
Working Demo in this Stackblitz Link
Basically you need to assign index i value to your show property and according to show and i value you can display show more or show less only for clicked td element only.
<table>
<tbody>
<tr *ngFor="let pos of procedureOrderList; let i = index">
<td>{{pos.investigationName}}</td>
<td>{{pos.investigationDate | date: 'dd-MM-yyyy'}}</td>
<td>{{pos.released_DT | date: 'dd-MM-yyyy'}}</td>
<td>
<ng-container *ngIf="pos.report">
{{(show) ? pos.report : pos.report | slice:0:50}}
<a *ngIf="pos.report.length > 0;" (click)="((show = i ))">
...{{ (( show === i)) ? '[Show less]': '[Show More]' }}</a>
</ng-container>
</td>
</tr>
</tbody>
</table>
As an alternative to GaurangDhorda's method, you could also extend the model you're using to include 'show' as a property.
So assuming that pos is of type Pos, defined something like this:
interface Pos {
investigationName: string;
investigationDate: date;
release_DT: date;
report: string;
}
... then what you can do is extend this interface (or class) and provide it with the 'show' property like this:
interface ShowablePos extends Pos {
show: boolean;
}
Then the trick is that when you get your Pos instances (for example, from an API), you use a map call to transform them into ShowablePos instances:
posApi.getProcedureOrderList()
.pipe(map( posList => {
// each pos is show=false by default
return posList.map(pos => ({ ...pos, show: false }))
}))
.subscribe(...)
Then the only change you need to make to your HTML is to refer to pos.show instead of a global show.
<td>{{pos.investigationName}}</td>
<td>{{pos.investigationDate | date: 'dd-MM-yyyy'}}</td>
<td>{{pos.released_DT | date: 'dd-MM-yyyy'}}</td>
<td>
<ng-container *ngIf="pos.report">
{{(pos.show) ? pos.report : pos.report | slice:0:50}}
<a *ngIf="pos.report.length > 0;" (click)="pos.show = !pos.show" > ...{{ pos.show ? '[Show less]': '[Show More]' }}</a>
</ng-container>
</td>
I am trying to render a table in html that is the representation of a reactive form array.
I loop over the complex object (FormGroup > FormArray > FormGroup > FormArray) With the final array being the data that we want to represent to the UI in its transformed format. (Happens in the function groupFormGroup())
Everything works fine, the data is correctly transformed and displayed.
However, now I am trying to add inputs (NOT formControlName inputs) to the table and when they load, they load without me being able to click into, or change their value. They are not disabled, just unclickable. No errors come up in the console.
What I have tried:
Modifying the class names so it is no longer class='form-control'
Increasing the z-index of inputs to be on the forefront
Making the inputs position relative
Here is the code:
<div class="row" [formGroup]="address">
<div class="col-12">
<table class="w-100">
<thead>
<th></th>
<th>Order ID</th>
<th>GP Code</th>
<th>Product</th>
<th>Quantity</th>
<th>Preset Suite Number</th>
<th></th>
</thead>
<tbody>
<ng-template formArrayName="addressOrders" ngFor let-addressOrder [ngForOf]="address.get('addressOrders').controls" let-ao="index">
<ng-container [formGroup]="addressOrder">
<ng-template ngFor let-fulfillment [ngForOf]="groupFormGroup(addressOrder.get('fulfillments').getRawValue(),'orderDetailId')" let-f="index">
<tr>
<td class='table-input'>
<input type="checkbox"
(change)="updateFulfillmentFormControl(fulfillment,addressOrder,'selected',$event)"/>
</td>
<td>{{addressOrder.get('orderId').value}}</td>
<td>{{fulfillment['gpItemNumber']}}</td>
<td>{{fulfillment['name']}}</td>
<td>{{fulfillment['quantity']}}</td>
<td>
<ng-container *ngIf="fulfillment['isSuite']">
<!-- <input class="form-control" (keydown)="updateFulfillmentFormControl(fulfillment,addressOrder,'suiteNumber',$event)" /> -->
<input type="text" class="form-control" />
</ng-container>
</td>
<td>Edit Detail</td>
</tr>
</ng-template>
</ng-container>
</ng-template>
</tbody>
</table>
</div>
</div>
And the code generating the Form Group:
draw() {
this.formGroup = this.initFormGroup(this.ordersToDisplay)
}
initFormGroup(ordersToDisplay: Array<BulkFulfillCustomer>):FormGroup {
var queueList = this.queues.map(q => q['queue_id']);
return this.fb.group({
customers: this.fb.array(
ordersToDisplay.map(otd => {
return this.fb.group({
gpCustomerNumber:[otd.gpCustomerNumber],
customerName:[otd.customerName],
customerAddresses: this.initAddressForm(otd.addressOrders, queueList)
})
})
)
})
}
initAddressForm(addressOrders: Array<BulkFulfillAddress>, queues:Array<string>):FormArray {
return this.fb.array(
addressOrders.map(ao => {
return this.fb.group({
addressLine:[ao.addressLine],
bundleShipment:[true],
addressOrders: this.initAddressOrdersForm(ao.orders,queues)
})
})
)
}
initAddressOrdersForm(orders: Array<BulkFulfillOrder>, queues:Array<string>):FormArray {
return this.fb.array(
orders.map(o => {
return this.fb.group({
orderId: [o.order.id],
fulfillments: this.initFulfillmentsForm(o.orderDetailFulfillments,queues)
})
})
)
}
initFulfillmentsForm(fulfillments: Array<FulfillmentLine>, queues:Array<string>) {
return this.fb.array(
fulfillments.map(f => {
return this.fb.group({
selected:[true],
expandDetail:[false],
isSuite:[f.inventory.isSuite],
suitePrefix:[f.inventory.suitePrefix],
gpItemNumber:[f.inventory.gpItemNumber],
name:[f.inventory.name],
queueId:[{value:f.inventory.pulseQueueId,disabled:(!this.isPulseFulfill && f.inventory.fulfillmentMethod != INVENTORY_FULFILLMENT_PULSE)}, Validators.compose([Validators.required, ContainsListValidator(queues,'Queue ID')])],
orderDetailId:[f.orderDetailFulfillment.orderDetailId],
pulseOrderId:[f.orderDetailFulfillment.pulseOrderId],
suiteNumber:[{value:f.orderDetailFulfillment.suiteNumber,disabled:!this.isPulseFulfill || !f.inventory.isSuite}, Validators.required],
quantity:[{value:f.orderDetailFulfillment.quantity,disabled:f.orderDetailFulfillment.fulfillmentMethod != 1},Validators.compose([Validators.required, Validators.min(1),Validators.max(f.orderDetailFulfillment.quantity)])]
})
})
)
}
In Vue, I have created multiple tables in the same component's template. Those tables all have the same html template, except they use different property of data (). Somehow the column width is inconsistent between them.
<template>
<v-container>
<h2>Security</h2>
<hr>
<v-simple-table>
<template v-slot:default>
<tbody>
<tr v-for="(a, index) in user_info.security_titles"
:key="a">
<td><strong>{{ a }}</strong></td>
<td v-show="user_info.security_data">{{user_info.security_data[index]}}</td>
</tr>
</tbody>
</template>
</v-simple-table>
<br>
<h2>Connection</h2>
<hr>
<v-simple-table>
<template v-slot:default>
<tbody>
<tr v-for="(b, index) in user_info.connection_titles"
:key="b">
<td> <strong>{{ b }}</strong> </td>
<td v-show="user_info.connection_data">{{user_info.connection_data[index]}}</td>
</tr>
</tbody>
</template>
</v-simple-table>
<br>
<h2>Language</h2>
<hr>
<v-simple-table>
<template v-slot:default>
<tbody>
<tr v-for="(c, index) in user_info.language_titles"
:key="c">
<td> <strong>{{ c }}</strong> </td>
<td v-show="user_info.language_data">{{user_info.language_data[index]}}</td>
</tr>
</tbody>
</template>
</v-simple-table>
</v-container>
</template>
I have used any style
A table always tries to fit in the content the most nicely, so if your column contains a row with long text, that column will take up more space.
You can use the width property in your headers to have a fixed width as shown in the docs
{
text: string
value: string
align?: 'start' | 'center' | 'end'
sortable?: boolean
filterable?: boolean
divider?: boolean
class?: string | string[]
width?: string | number
filter?: (value: any, search: string, item: any) => boolean
sort?: (a: any, b: any) => number
}
I am trying to change the row of a table to #0093ff; if the Holidays.HolidayDate is the next upcoming holiday.
NEW CODE:
<table ng-if="model.Holidays" class="table">
<tr>
<th>Holiday</th>
<th>Date</th>
<th>Branch</th>
<th>Hours</th>
</tr>
<tr ng-repeat="Holidays in model.Holidays" ng-style="isUpcomingHoliday(Holidays.HolidayDate) ? 'color:#0093ff' : '' ">
<td>{{Holidays.HolidayName}}</td>
<td>{{Holidays.HolidayDate | fulldaydate}}</td>
<td>{{Holidays.Branch ? Holidays.Branch : 'All'}}</td>
<td>{{Holidays.Hours ? Holidays.Hours : 'Closed'}}</td>
</tr>
</table>
JS
.filter('isUpcomingHoliday', ['$filter',
function ($filter) {
return function (input) {
const sortedHolidays = this.model.Holidays.sort((a, b) => {
return moment(b.date).isAfter(moment(a.date))
});
const upcomingHoliday = sortedHolidays[0];
return moment(holidayDate).isSame(upcomingHoliday);
};
}])
You can achieve it by sorting holidays and compare with the first element. I think it can look like this:
isUpcomingHoliday(holidayDate) {
const sortedHolidays = this.model.Holidays.sort((a, b) => {
return new Date(b.HolidayDate) - new Date(a.HolidayDate)
};
return holidayDate.getTime() === sortedHolidays[0].getTime();
}
<section class="module module-divider-bottom" data-app="global">
<div class="container" ng-controller="ContactController">
<div class="container">
<table ng-if="model.Holidays" class="table">
<tr>
<th>Holiday</th>
<th>Date</th>
<th>Branch</th>
<th>Hours</th>
</tr>
<tr ng-repeat="Holidays in model.Holidays" ng-style="isUpcomingHoliday(Holidays.HolidayDate) ? 'background-color:#0093ff' : '' ">
<td>{{Holidays.HolidayName}}</td>
<td>{{Holidays.HolidayDate | fulldaydate}}</td>
<td>{{Holidays.Branch ? Holidays.Branch : 'All'}}</td>
<td>{{Holidays.Hours ? Holidays.Hours : 'Closed'}}</td>
</tr>
</table>
</div>
</div>
</section>
It could be better and more readable with moment.js:
isUpcomingHoliday(holidayDate) {
const sortedHolidays = this.model.Holidays.sort((a, b) => {
return moment(b.date).isAfter(moment(a.date))
};
const upcomingHoliday = sortedHolidays[0];
return moment(holidayDate).isSame(upcomingHoliday);
}
Something like this should do the trick. I didn't know if you already had an api that tells you the next holiday, If you are using moment to check if the date is the same (or if you already have something to compare dates).
I chose the class vs ngStyle because you might want to style other things related to that holiday. Hope that helps.
isNextHoliday (holidayDate): boolean {
// return some function that compares the two dates
}
.some-color {
background: red;
}
<section class="module module-divider-bottom" data-app="global">
<div class="container" ng-controller="ContactController">
<div class="container">
<table ng-if="model.Holidays" class="table">
<tr>
<th>Holiday</th>
<th>Date</th>
<th>Branch</th>
<th>Hours</th>
</tr>
<tr ng-repeat="Holidays in model.Holidays">
<td>{{Holidays.HolidayName}}</td>
<td [class.some-color]="isNextHoliday(Holidays.HolidayDate)">{{Holidays.HolidayDate | fulldaydate}}</td>
<td>{{Holidays.Branch ? Holidays.Branch : 'All'}}</td>
<td>{{Holidays.Hours ? Holidays.Hours : 'Closed'}}</td>
</tr>
</table>
</div>
</div>
</section>
I have a PrimeNg turbotable with row expansion feature.
How can I expand the rows by default.
Here is my Code :
HTML
<p-table [columns]="cols" [value]="cars" dataKey="vin">
<ng-template pTemplate="header" let-columns>
<tr>
<th style="width: 2.25em"></th>
<th *ngFor="let col of columns">
{{col.header}}
</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-rowData let-expanded="expanded" let-columns="columns">
<tr>
<td>
<a href="#" [pRowToggler]="rowData">
<i [ngClass]="expanded ? 'fa fa-fw fa-chevron-circle-down' : 'fa fa-fw fa-chevron-circle-right'"></i>
</a>
</td>
<td *ngFor="let col of columns">
{{rowData[col.field]}}
</td>
</tr>
</ng-template>
<ng-template pTemplate="rowexpansion" let-rowData let-columns="columns">
<tr>
<td [attr.colspan]="columns.length + 1">
<div class="ui-g ui-fluid" style="font-size:16px;padding:20px">
<div class="ui-g-12 ui-md-3" style="text-align:center">
<img [attr.alt]="rowData.brand" src="assets/showcase/images/demo/car/{{rowData.brand}}.png">
</div>
<div class="ui-g-12 ui-md-9">
<div class="ui-g">
<div class="ui-g-12">
<b>Vin:</b> {{rowData.vin}}
</div>
<div class="ui-g-12">
<b>Vin:</b> {{rowData.color}}
</div>
<div class="ui-g-12">
<b>Brand:</b> {{rowData.brand}}
</div>
<div class="ui-g-12">
<b>Color:</b> {{rowData.color}}
</div>
</div>
</div>
</div>
</td>
</tr>
</ng-template>
</p-table>
Ts
export class TableRowExpansionDemo implements OnInit {
cars: Car[];
cols: any[];
constructor(private carService: CarService) { }
ngOnInit() {
this.carService.getCarsSmall().then(cars => this.cars = cars);
this.cols = [
{ field: 'vin', header: 'Vin' },
{ field: 'year', header: 'Year' },
{ field: 'brand', header: 'Brand' },
{ field: 'color', header: 'Color' }
];
}
}
}
I tried using expandedRowKeys attribute, but it is not working for me.
What am I missing here?
Thanks
Update: For Version >7.x
Setting value to 1 won't work on version 7+ use boolean(true/false)
const thisRef = this;
this.cars.forEach(function(car) {
thisRef.expandedRows[car.vin] = true;
});
Working StackBlitz
For Version <7.x
I tried using expandedRowKeys attribute
Yes you're right. So add [expandedRowKeys]="expandedRows"> to p-table element :
<p-table [columns]="cols" [value]="cars" dataKey="vin" [expandedRowKeys]="expandedRows">
and then, you just need to fill expandedRows object with vin values of the rows you want to expand (because dataKey is vin).
Because you want all rows to be expanded, you can fill it like that :
const thisRef = this;
this.cars.forEach(function(car) {
thisRef.expandedRows[car.vin] = 1;
});
in order to have something like
expandedRows = {"dsad231ff": 1, "gwregre345": 1, ...}
See working Plunker
In the accepted answer, the expandedRows mapping to the turbo table expandedRowKeys is one way i.e. it can set the state of rows at the time of loading only.
If someone want to collapse or expand it after the table is loaded, you can directly edit the 'expandedRowKeys' variable by passing the table reference from the html.
Define Your table with a reference variable #dt
<p-table #dt [columns]="cols" [value]="cars" dataKey="vin">
Edit your table body like this to get a on click function trigger
<ng-template pTemplate="body" let-rowData let-expanded="expanded" let-columns="columns">
<tr>
<td>
<a href="#" [pRowToggler]="rowData">
<i [ngClass]="expanded ? 'fa fa-fw fa-chevron-circle-down' : 'fa fa-fw fa-chevron-circle-right'"></i>
</a>
</td>
<td *ngFor="let col of columns">
<a >
<span (click)="onItemClick(rowData, dt)">
{{rowData[col.field]}}
</span>
</a>
</td>
</tr>
</ng-template>
in your TS file add the function as
onItemClick(rowData:any, dt:any) {
if(dt.expandedRowKeys[rowData._id]){
dt.expandedRowKeys[rowData._id] = 0;
}
else {
dt.expandedRowKeys[rowData._id] = 1;
}
}
This approach gives you more freedom in changing state of table on a trigger of event from outside of table and expanding or collapsing multiple rows at once.
After hitting many solution i tried and found easiest way to resolve
<p-table [columns]="cols" [value]="cars" dataKey="vin" [expandedRowKeys]="expandedRows">
Replace datakey value to your unique id key from list of objects fetched from service or http calls.
<p-table [columns]="cols" [value]="repaymentsList" dataKey="id" expandableRows="true" rowExpandMode="single" [responsive]="true">