I am trying to merge the divs that have the same number in order to see the week and its number.
This is how my html looks like
<div class="wrapper clearfix">
<div class="left">
<label class="kw">WeekNo.</label>
</div>
<div class="right week-number" *ngFor="let day of days">
<label class="number-label"><span>{{day.weekNumber}} </span></label>
</div>
</div>
do I have to use *ngIf or just css?
I have read this How to merge two divs, but I don't know where ti include the if statement
this is the class I am using
export class Day {
number: number;
weekDay: number;
name: string;
weekNumber: number;
constructor(number: number, weekDay: number, name: string, weekNumber: number
) {
this.number = number;
this.weekDay = weekDay;
this.name = name;
this.weekNumber = weekNumber;
}
}
and this is how it should look afet merging
You can do it the following way by creating a groupBy pipe and including Bootstrap 4 in the project. Here is a plain working demo (you will have to do the styling) demo:
groupby.pipe.ts
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({name: 'groupBy'})
export class GroupByPipe implements PipeTransform {
transform(collection: any[], property: string): any[] {
// prevents the application from breaking if the array of objects doesn't exist yet
if(!collection) {
return null;
}
const groupedCollection = collection.reduce((previous, current)=> {
if(!previous[current[property]]) {
previous[current[property]] = [current];
} else {
previous[current[property]].push(current);
}
return previous;
}, {});
// this will return an array of objects, each object containing a group of objects
return Object.keys(groupedCollection).map(key => ({ key, value: groupedCollection[key] }));
}
}
html code
<div class="table table-borderless ">
<div class="col-md-12 text-center border"><span>{{currentMonthName}}</span> <span>{{currentYear}}</span></div>
<div class="d-flex">
<div class=" text-center m-2 border" *ngFor="let day of days | groupBy:'weekNumber'">
{{day.key}}
<table class="table">
<tbody>
<tr>
<th scope="row" *ngFor="let x of day.value ">{{x.number}}</th>
</tr>
<tr>
<th scope="row" *ngFor="let y of day.value ">{{y.name}}</th>
</tr>
</tbody>
</table>
</div>
</div>
</div>
Related
My question is, how do I write a function in my table-viewer.component.ts file to change the value from false to true within the JSON file based on whether the user clicks the cancelled button?
I have a JSON file containing the following details.
db.json
"firstName": "Hamza",
"lastName": "Gallagher",
"carReg": "FG67 POI",
"parkingNumber": "00003",
"date": "2021-01-04",
"cancelled": false
etc ..
And I am displaying them in a table with angular, which shows the following:
Image of table
Table-viewer.component.html
<table class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Car Registration</th>
<th>Parking Number</th>
<th>Date</th>
<th>Status</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let booking of bookings">
<td>{{booking.firstName}}</td>
<td>{{booking.lastName}}</td>
<td>{{booking.carReg}}</td>
<td>{{booking.parkingNumber}}</td>
<td>{{booking.date}}</td>
<td>
<div *ngIf="booking.cancelled" class="red">Cancelled</div>
<div *ngIf="!booking.cancelled" class="green">Booked</div>
</td>
<td>
<button class="btn btn-danger mr-2" (click)="cancelBooking()">Cancel</button>
</td>
</tr>
table-viewer.component.ts
export class TableViewerComponent implements OnInit {
bookings: Booking[] = [];
getBookings(): void {
this.bookingService.getAll().subscribe((book: Booking[]) => {
this.bookings = book;
});
}
constructor(private bookingService: BookingService) {
}
ngOnInit(): void {
this.getBookings();
}
cancelBooking(): void {
// Help
}
}
booking.ts
export interface Booking {
'firstName': string;
'lastName': string;
'carReg': string;
'parkingNumber': string;
'date': string;
'cancelled': boolean;
}
I think it is as simple as:
cancelBooking(booking:Booking){
booking.cancelled = true;
}
And just pass it in the view to the method
Please use this code in your componenet.ts
cancelBooking(index): void {
// Help
this.bookings[index].cancelled = true;
}
Template :
Change the loop code with below code:
<tr *ngFor="let booking of bookings; let i = index">
<td>{{booking.firstName}}</td>
<td>{{booking.lastName}}</td>
<td>{{booking.carReg}}</td>
<td>{{booking.parkingNumber}}</td>
<td>{{booking.date}}</td>
<td>
<div *ngIf="booking.cancelled" class="red">Cancelled</div>
<div *ngIf="!booking.cancelled" class="green">Booked</div>
</td>
<td>
<button class="btn btn-danger mr-2" (click)="cancelBooking(i)">Cancel</button>
</td>
</tr>
Table-viewer.component.html
<table class="table table-striped table-bordered table-hover">
...
<td>
<button class="btn btn-danger mr-2" (click)="cancelBooking(booking: Booking)">Cancel</button>
</td>
</tr>
table-viewer.component.ts
export class TableViewerComponent implements OnInit {
...
cancelBooking(booking): void {
booking.cancelled = !booking.cancelled
// or booking.cancelled = false; or whatever you want to do
}
}
Now, you have the change in the bookings array.
Next, you should implement a service to rewrite a json file.
https://stackoverflow.com/a/52242184/11218448
I'm trying to access certain data from an API and having trouble doing so. Here is the structure of the JSON from the API:
I'm wanting to access Name, StateName & CityName within value to iterate through a table. I'm getting the obvious error "Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays", because ngFor isn't iterating through the array. How can I fix my code to iterate through the data to fill my table?
component.ts:
#Component({
selector: 'app-search',
templateUrl: './search.component.html',
styleUrls: ['./search.component.css']
})
export class SearchComponent implements OnInit {
customer: any;
constructor(private Service: CustomerDataService) { }
ngOnInit(): void {
this.getCustomerData();
}
public getCustomerData() {
this.Service.getCustomers().subscribe((data)=>{
console.log(data);
this.customer = data;
})
}
}
HTML:
<div class="container">
<form class="form-inline">
<div class="input-group mb-2">
<div class="has-clear">
<input type="text" name="search" required class="form-control" placeholder="Name" />
<button class="clear-data" type="reset"><i class="icon-close-bold-sm"></i></button>
</div>
<button type="submit" class="btn btn-primary mb-2 ml-3">Search</button>
</div>
</form>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">City</th>
<th scope="col">State</th>
</tr>
</thead>
<tbody>
<tr *ngFor = "let customers of customer">
<td scope="row">{{customers.value.Name}}</td>
<td scope="row">{{customers.value.StateName}}</td>
<td scope="row">{{customers.value.CityName}}</td>
</tr>
</tbody>
</table>
</div>
</div>
The value is an array and not object, so
public getCustomerData() {
this.Service.getCustomers().subscribe((data)=>{
console.log(data);
this.customer = data.value;
})
}
and in your template
<tr *ngFor = "let customers of customer">
<td scope="row">{{customers.Name}}</td>
<td scope="row">{{customers.StateName}}</td>
<td scope="row">{{customers.CityName}}</td>
</tr>
Or you can just edit your template
<tr *ngFor = "let customers of customer.value">
<td scope="row">{{customers.Name}}</td>
<td scope="row">{{customers.StateName}}</td>
<td scope="row">{{customers.CityName}}</td>
</tr>
Consider using | async to access the observable directly in your html
<tr *ngFor = "let customer of customers$ | async">
<td scope="row">{{customer.Name}}</td>
<td scope="row">{{customer.StateName}}</td>
<td scope="row">{{customer.CityName}}</td>
</tr>
and in your component
customers$ : Observable<Customer> = this.getCustomerData();
You can update your getCustomerData with a map operator to return the list of Customers you have inside value property.
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)])]
})
})
)
}
I have a table containing some rows.The table data comes from loop.Here I need to add new columns based on selection of checkbox.Suppose I checked product,a new table heading will create as product along with blank rows just below the table heading,again If I uncheck created columns will be removed.New columns should create before add button.Here is the code below
https://stackblitz.com/edit/angular-table-focus
app.component.html
<div class="container">
<h2>Basic Table</h2>
<p>The .table class adds basic styling (light padding and only horizontal dividers) to a table:</p>
<div><button (click)="enable()">Edit</button> <input type="checkbox" value="product">Product <input type="checkbox" value="market">market</div>
<table class="table border">
<thead>
<tr>
<th>Items</th>
<th>Business</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of groups;let i = index" (click)="setClickedRow(i)" [class.active]="i == selectedRow">
<td> <input #focus [disabled]='toggleButton' type="text" value="{{row.name}}"> </td>
<td> <input [disabled]='toggleButton' type="text" value="{{row.items}}"> </td>
<td> <button (click)="addRow(i)">Add</button></td>
</tr>
</tbody>
</table>
</div>
app.component.ts
import { Component,ViewChild, ElementRef } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
selectedRow : Number;
#ViewChild('focus') input: ElementRef;
public toggleButton: boolean = true;
ngOnInit() {
}
groups=[
{
"name": "pencils",
"items": "red pencil"
},
{
"name": "rubbers",
"items": "big rubber"
},
{
"name": "rubbers1",
"items": "big rubber1"
},
];
addRow(index): void {
var currentElement = this.groups[index];
this.groups.splice(index, 0, currentElement);
}
enable(){
this.toggleButton = false
setTimeout(()=>{ // this will make the execution after the above boolean has changed
this.input.nativeElement.focus();
this.selectedRow = 0;
},0);
}
setClickedRow(index) {
this.selectedRow = index;
}
}
Check out my StackBlitz for live demo: https://stackblitz.com/edit/angular-table-focus-77xrnn
To add or remove columns dynamically, we have to first fetch the values from the component instead of hard coding them in the template.
columns = ["Items", "Business"];
Component:
And whenever a column checkbox is toggled, we listen to the change event and add or delete the column accordingly.
onColumnStatusChange(column, isChecked) {
if (isChecked) {
// Add column to the table.
this.columns.push(column);
this.groups.forEach(value => {
value[column] = "";
});
} else {
// Delete column from the table.
this.columns.splice(this.columns.indexOf(column), 1);
this.groups.forEach(value => {
delete value[column];
});
}
}
Template:
In the template, columns and also table row items must be fetched dynamically.
<input type="checkbox" value="product" (change)="onColumnStatusChange($event.target.value, $event.target.checked)">Product
<input type="checkbox" value="market" (change)="onColumnStatusChange($event.target.value, $event.target.checked)">market
...
...
<tr>
<ng-container *ngFor="let column of columns; let i = index">
<th>{{ column }}</th>
</ng-container>
</tr>
...
...
<tr *ngFor="let row of groups;let i = index" (click)="setClickedRow(i)" [class.active]="i == selectedRow">
<ng-container *ngFor="let item of objectKeys(row); let j = index">
<td><input #focus [disabled]='toggleButton' type="text" value="{{row[item]}}"></td>
</ng-container>
<td><button (click)="addRow(i)">Add</button></td>
</tr>
As it looks in the photo, that I want is, when I click show more, I want to expand this row, and show me all sensors in there
I'm using materialize, and my code html is:
<table class="bordered table-bordered" [mfData]="homeboxsp | dataFilter : filterQuery" #mf="mfDataTable" [mfRowsOnPage]="rowsOnPage"
[(mfSortBy)]="sortBy" [(mfSortOrder)]="sortOrder">
<thead style="color:black; background:rgb(207, 235, 245);border:1px solid rgb(190, 190, 190);">
<tr>
<th>
<mfDefaultSorter by="client">Client</mfDefaultSorter>
</th>
<th>
<mfDefaultSorter by="description">Homebox</mfDefaultSorter>
</th>
<th>
<mfDefaultSorter by="sensors">Sensors</mfDefaultSorter>
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of homeboxsp">
<td>{{item.client}}</td>
<td>{{item.description}}</td>
<td>
<ul>
<ul *ngFor="let sensor of item.sensors">
<li>{{sensor.sensor_serial}}</li>
</ul>
</ul>
</td>
</tr>
</tbody>
</table>
you can do this in simple way using ngIf
<ul *ngFor="let sensor of item.sensors;let i = index;">
<li *ngIf="i < value">{{sensor.sensor_serial}}</li>
<button (click)="showmore()">showmore</button>
</ul>
ts
value:number = 2;
showmore(){
let value = this.value;
this.value= value+1;
}
you can modify logic as you want
The SlicePipe is perfect for this kind of situations. It does pretty much the same as the accepted answer with fewer *ngIf needed.
#Component({
selector: 'my-app',
template: `
<div>
<div *ngFor="let val of values | slice:0:visible">{{val}}</div>
<div *ngIf="visible==3" (click)="visible=values.length">show more</div>
<div *ngIf="visible==values.length" (click)="visible=3">show less</div>
</div>
`,
})
export class App {
visible= 3;
values = [1, 2,3,4, 5,6,7,8,9]
constructor() {
}
}
You can see a working example here.
The same principle applies if you want to have multiple lists that expand. You just need to control the visibility for each list separately.
Here's an example of that.
#Component({
selector: 'my-app',
template: `
<div>
<div *ngFor="let group of groups">
<div>{{group.name}}</div>
<div *ngFor="let val of group.values | slice:0:(group.expanded? group.values.length :3)">{{val}}</div>
<div *ngIf="!group.expanded" (click)="group.expanded=true">show more</div>
<div *ngIf="group.expanded" (click)="group.expanded=false">show less</div>
</div>
</div>
`,
})
export class App {
groups = [{name: 'a', values: [1, 2,3,4, 5,6,7,8,9]},{name: 'b', values: [1, 2,3,4, 5,6,7,8,9]}]
constructor() {
}
}
And the working plnkr here.
You can build a simple component that can show just a certain number of sensors, or adjust it to show just certain height:
<td><values-display-cmp [values]="row"></values-display-cmp></td>
Then the component:
#Component({
selector: 'values-display-cmp',
template: `
<ul>
<li *ngFor="let val of shownValues">{{val}}</li>
</ul>
More
`
})
export class ValuesDisplayComponent {
#Input() set values(values) {
this._values = values;
this.shownValues = values.slice(0, 2);
};
open = false;
shownValues = [];
_values = [];
more() {
this.open = true;
this.shownValues = this._values;
}
}
Here's a full example.
Separate your array in 2 parts, and after clicking show more concat them. Seems like not a big deal.