How do I make multiple rows out of my ngFor Data? - html

In looping through my data, I'd like to have the "app-system" components displayed in even rows across the screen. When I try to put it in a table and then in a row, of course it only creates one row rather than multiple rows with the data.
Here's an image of what its doing currently https://imgur.com/a/O0MjBES
I have to scroll down to see all 190 items in the row.
I've thought about creating another ngFor loop of rows to apply to the data, but I can't quite figure out how. I'm unable to apply the answers I've tried to research to my project due to my lack of experience.
Here's my template that grabs my "app-system" component and loops through all of the data. It currently shows in one LONG row down the page:
Here is the template for the app-system component that is being copied over, the buttons need to be split into seperate rows. The popup box opens when you click one of the buttons.
<div class="section-container">
<div class="section-container">
<table>
<div *ngFor="let system of systemList | paginate: { itemsPerPage: 190,
currentPage: p }; let i = index">
<app-system [systemInput]="system"></app-system>
</div>
</table>
<div class="pagination section-container card-deck">
<pagination-controls (pageChange)="p = $event"></pagination-controls>
</div>
</div>
```<div class='card mb-1 shadow button-background'>
<div class= "popup-box">
<popup #popup>
<p *ngIf="systemInput.AMStatus; else showAMInvalid"> Anti-Malware Version: Current</p>
<ng-template #showAMInvalid>
<p style="background-color:yellow; ">Anti-Malware Version: Needs Update</p>
</ng-template>
<p *ngIf="systemInput.BLStatus; else showBLInvalid">Bitlocker Status: Active</p>
<ng-template #showBLInvalid>
<p style="background-color:yellow; ">Bitlocker Status: Inactive</p>
</ng-template>
<p *ngIf="systemInput.FirewallRullStatus; else showFirewallRuleInvalid"> Firewall Rules: Applied</p>
<ng-template #showFirewallRuleInvalid>
<p style="background-color:yellow; ">Firewall Rules: Not Applied</p>
</ng-template>
<p *ngIf="systemInput.FirewallContentStatus; else showFirewallContentInvalid"> Firewall: Enabled </p>
<ng-template #showFirewallContentInvalid>
<p style="background-color:yellow; ">Firewall Status: Disabled</p>
</ng-template>
<p *ngIf="systemInput.SCCMStatus; else showSCCMStatusInvalid"> SCCM Status: Running</p>
<ng-template #showSCCMStatusInvalid>
<p style="background-color:yellow; ">SCCM Status: Not Running</p>
</ng-template>
<p *ngIf="systemInput.MSBaselineStatus; else showMSBaselineInvalid"> Microsoft Standards: Met</p>
<ng-template #showMSBaselineInvalid>
<p style="background-color:yellow; ">Microsoft Standards: Not Met</p>
</ng-template>
<p *ngIf="dateCheck() == true; else showDateInvalid"> Last Update: {{systemInput.UpdatedDate | date}}</p>
<ng-template #showDateInvalid>
<p style="background-color:yellow; ">Last Update: {{systemInput.UpdatedDate | date}}</p>
</ng-template>
<p *ngIf="systemInput.USBStatus; else showUSBInvalid">Removable Storage: Disabled</p>
<ng-template #showUSBInvalid>
<p style="background-color:yellow; ">Removable Storage: Enabled</p>
</ng-template>
</popup>
</div>
<button (click)="ClickButton()" [ngClass]="{
'btn buttonGood':isValid(),
'btn buttonDateAlert':isValid()=='datewarning',
'btn buttonAlert':isValid()==false
}">{{systemInput.hostname}}</button>
</div>
export interface Ipcidata {
id: Int16Array;
hostname: string;
AMStatus: boolean;
BLStatus: boolean;
FirewallRullStatus: boolean;
FirewallContentStatus: boolean;
SCCMStatus: boolean;
MSBaselineStatus: boolean;
UpdatedDate: Date;
USBStatus: boolean;
}

You're saying row, but you're thinking of column. You should use flex/grid for that.
You should do what you're trying with css.
On container element do:
.container {
display: flex;
flex-wrap: wrap;
flex-direction: column;
width: 500px; // Or whatever
height: 500px; // Or whatever
}

Try changing the <div> to a <tr> element
<div class="section-container">
<table>
<tr *ngFor="let system of systemList | paginate: { itemsPerPage: 190,
currentPage: p }; let i = index">
<app-system [systemInput]="system"></app-system>
</tr>
</table>
<div class="pagination section-container card-deck">
<pagination-controls (pageChange)="p = $event"></pagination-controls>
</div>

All you should need is ngIf
<div class="section-container">
<table>
<div *ngFor="let system of systemList | paginate: { itemsPerPage: 190,
currentPage: p }; let i = index">
<app-system *ngIf="i % 2 == 0" [systemInput]="system"></app-system>
</div>
</table>
<div class="pagination section-container card-deck">
<pagination-controls (pageChange)="p = $event"></pagination-controls>
</div>
</div>
All we do is use a modulus expression to only show the even rows (The inverse would be i % 2 != 0)
app-system is a child element however, so there will be empty parent divs...

Related

How to set an ngb-accordion to expand in Angular

I currently have ngb-accordions set to a number of panels. By default the panels are closed and I am using a custom function to expand them when clicked. I would like to know how to set an accordion to be expanded by default if the category name is select.
<div class="col-md-12">
<ngb-accordion #filterAccordion [animation]="false" (click)="heightChanged()"#acc="ngbAccordion" [activeIds]="activeIds">
<ngb-panel *ngFor="let filterCategory of filterCategories | filterCategory | keyvalue : returnZero ;let i= index" id="panel-{{i}}">
<ng-template ngbPanelTitle>
<button class="header-btn" (click)="heightChanged()">
<span>{{filterCategory.value.translationId | translate}}</span>
</button>
</ng-template>
<ng-template ngbPanelContent *ngIf="filterCategory.value.filterParameters.length > 0">
<div class="row">
<div class=" col-md-2" *ngFor="let filter of filterCategory.value.filterParameters | keyvalue">
<app-filter-parameters *ngIf="filterCategory.key.toString() !== 'select'"
[(filterParameterValue)]="filter.value.value"
[filterParameterName]="filter.value.key"
[dsaComponent]="'dashboardFilter'"
(applyFilters)="apply()">
</app-filter-parameters>
</div>
</div>
<div class="row">
<div class="form-group col-md-2" *ngIf="filterCategory.key.toString() === 'select'">
<app-date-picker
[filterParameters]="filterCategory.value.filterParameters"
[dsaComponent]="'dashboardFilter'"
(updateTimeframe)="this.updateFilter($event)">
</app-date-picker>
</div>
</div>
</ng-template>
</ngb-panel>
</ngb-accordion>
</div>
Set same ID for all ngb-panels:
<ngb-accordion [activeIds]="activeIds"></ngb-accordion>
// For example
this.activeIds = ['UNIQ_ID','UNIQ_ID','UNIQ_ID'];
You should use activeIds to open panels initially.
You added id="panel-{{i}}" to each panel. So for example, if first panel should be opened by default, you should initialize activeIds="panel-0":
<ngb-accordion #filterAccordion [animation]="false" (click)="heightChanged()"#acc="ngbAccordion" activeIds="panel-0">

moving ngfor data outside of it's tag

it's my first time posting so critic for etiquette.
been using *ngFor="let item of items" in a ionic project but I ran into a problem, that being that once *ngFor is used in the a tag, I don't know who I can transfare the data for a selected item into a tag that is outside of the original tag containing it.
code:
<ion-list>
<div class="wrapper">
<ion-item-group class="day" *ngFor="let day of Excursion_Schedule" >
<ion-item-divider class="dayCard" (click)="day.displayItineraryFlag= !day.displayItineraryFlag"><ion-icon name="ios-arrow-forward" item-right></ion-icon>{{day.ExcSch_date | date:'dd'}}</ion-item-divider>
<!-- CODE I WANT TO MOVE --> <ng-container *ngIf="day.displayItineraryFlag">
<div ion-item class="cardtitle" *ngFor="let place of day.Excursion_CustomRoadMap">
<p>check</p>
<ion-item-divider class="cardtitle" *ngIf="place.Exc_CustomRoadMap_Name" (click)="place.displayStationDetailsFlag= !place.displayStationDetailsFlag">
<div class="textW">{{place.Description}} - {{place.Exc_CustomRoadMap_StartTime}}</div>
<br>
<div *ngIf="place.displayStationDetailsFlag">
<div class="mapNpic">
<img class="pics" src="{{place.Photo}}" alt="">
<img class="pics" src="https://media-cdn.tripadvisor.com/media/photo-s/14/5f/96/77/nice-view-of-the-sunset.jpg" alt="">
</div>
<div class="dits">
<br> <br>
<h3>Adress</h3>
<h3>Phone Number</h3>
<h3>Email</h3>
<h3>Website</h3>
</div>
</div>
</ion-item-divider>
</div>
</ng-container>
</ion-item-group>
</div>
<p>
TESTING 123
<!-- PLACE WHERE I WANT TO MOVE ng-container TO -->
</p>
Just use interpolation. To achieve this you have to write the selected item into a variable and then interpolate it into the outer area. Here is an example:
TS
selectedItem: any;
Excursion_Schedule: Array<any> = [];
HTML
<div><span>{{selectedItem}}</span>
<div *ngFor="let day of Excursion_Schedule">
<!-- write the selected day into the local variable here -->
</div>
</div>

How to make background color change when displaying, depending on object property in Angular

I have processed my BE objects with an Angular interface called Orderline and put them into an array called 'orderlines'. I want to display 'orderline.ct_no' on a green background if the 'change_color' property is true (it's a boolean field).
Component:
public displayOrderlines() {
this.service.getOrderLines().subscribe((response : Orderline[]) => {
this.orderlines = response;
})
}
In .html I've tried this but it throws a big error, any ideas?
<ng-container *ngFor="let orderline of orderlines">
<div class="row" >
<div [style.backgroundColor]="{{ orderline.color_change }} ? 'green' :
'white' "
class="container">
<div class="column">
<p class="value" >{{ orderline.ct_no }}</p>
</div>
</div>
</ng-container>
<div [style.backgroundColor]="orderline.color_change ? 'green' : 'white'" class="container">

highlight word by start and end matches using angular 7

I have a collection of array which is having datas like
[0]: username {kavya} secret {password}
[1]: lorem ipsem text data value
[2]: lorem {{lrm}} datas {{pp}}
I am using foreach to show this data in frontend with
<div *ngFor="let data of output;let i=index">
<div *ngIf="data.includes('{') || data.includes('{{');else isNotEdited;" >
<div class="variable-textarea" contenteditable="false" >
<span>
{{data | slice:' ' }}
</span>
</div>
</div>
<ng-template #isNotEdited>
<ngx-md>{{data}}</ngx-md>
</ng-template>
</div>
Here I achieved like 0,2 row of div will be editable and in case of 1st array is non-editable.
But I want to do like specific matches which word starts with { or {{ and that particular word needs to be highlight and editable.
Is there any option to do in this way
Thanks in advance.
You could split the data into words:
<div *ngFor="let data of arr">
<span *ngFor="let word of data.split(' ')">
<span *ngIf="word.indexOf('{') > -1;else isNotEdited;">
<span class="variable-textarea-2" contenteditable="true">
{{word | slice:' ' }}
</span>
</span>
<ng-template #isNotEdited>
<span class="variable-text-2" contenteditable="false">
{{word}}
</span>
</ng-template>
</span>
</div>
Check this Stackblitz example I made based on your code: https://stackblitz.com/edit/angular-pkg6i9
this is a performance nightmare, you don't want to be running this many functions in template, and your format isn't helping you. map your data ahead of time into a friendlier view model:
this.mappedOutput = this.output.map(data => {
const editable = data.includes('{'); // checking for doubles is redundant
return {
editable,
data: !editable
? data
: data.split(' ')
.map(word => ({
word,
editable: word.trim().startsWith('{') && word.trim().endsWith('}')
}))
};
})
run this whenever your output changes, then use it in template:
<div *ngFor="let data of mappedOutput;let i=index">
<div *ngIf="data.editable;else isNotEdited;" >
<div class="variable-text">
<ng-container *ngFor="let word of data.data">
<div *ngIf="word.editable; else wordTmp" class="variable-textarea inline" contenteditable="true" >
<span>{{word.word}}</span>
</div>
<ng-template #wordTmp>
{{word.word}}
</ng-template>
</ng-container>
</div>
</div>
<ng-template #isNotEdited>
<ngx-md>{{data.data}}</ngx-md>
</ng-template>
</div>
and adjust the styles by adding this to your css:
.variable-textarea.inline {
display: inline-block;
width: fit-content;
margin: 0;
}
here's an edited blitz: https://stackblitz.com/edit/angular-arayve?file=src/app/app.component.html

Grab values from dynamic form content angular

I am working with a back-end API that I query for flight data, which I then loop over and display the data. My template code looks like this...
<form novalidate #form="ngForm">
<div class="flight-table">
<header fxLayout="row" fxLayoutAlign="space-between">
<div fxFlex="25">Flights</div>
<div fxFlex="17">Class</div>
<div fxFlex="18">Price (AED)</div>
<div fxFlex="15">PAX</div>
<div fxFlex="25">Total</div>
</header>
<main>
<div class="flights-body--row" fxLayout="row" fxLayoutAlign="space-between" *ngFor="let flight of data.flights; let i = index">
<p fxFlex="25">{{flight['flightName']}}</p>
<p fxFlex="17">{{flight['flightClass']}}</p>
<p fxFlex="18">{{flight['flightPrice'] | number}}</p>
<p fxFlex="15" fxLayout="column">
<mat-select placeholder="Ticket(s)">
<mat-option *ngFor="let pax of counter" [value]="pax">{{ pax }} </mat-option>
</mat-select>
</p>
<p fxFlex="25">AED 0</p>
</div>
<div class="flights-body--row" fxLayout="row" fxLayoutAlign="space-between">
<p class="capitalize" fxFlex="75"><strong>Total transport</strong></p>
<p class="center-text" fxFlex="25"><strong>AED 0</strong></p>
</div>
</main>
</div>
</form>
When a user selects a number of tickets from the PAX dropdown, I'd like to update both the row total and the total transport. I'd need to be able to grab the value of the PAX select box and multiply it by flight['flightPrice'] but I can't quite figure out how to, because I can't know how many items are in the flights array. What you see is just one row but there could be more.
Also, I'd like to capture the value for each row. I've thought about adding a hidden form field for each row or sth like that. I'm not quite sure how to deal with this.
Is there some better way?
Update
I'm struggling with how to capture the selected rows. I've tried adding a hidden input field in each row like so..
<input
matInput
[name]="flight['flightName']-flight['flightClass']-pax-flight['flightPrice']"
[value]="total"
ngModel>
That doesn't seem to work. The goal is to have each selected row stored in an object like so...
The dashes separate the flightName, flightClass, pax and flightPrice with the value being the total (pax * flightPrice) and pax the number of passengers.
flights: {
lufthansa-business-2-60: 120,
flyEmirates-first-3-50: 150
}
How should I go about it?
I would recommend that you create a dedicated component for your basket items.
You then have an isolated scope you can work with and i.e. use [(ngModel)] to set the selected number of pax on your component (for which you have to import the FormsModule in your app/feature module).
You can then reuse that value to compute the total price for a flight.
To get the total for the entire transport (sum over all basket items), you'd also need to pass data between components. A BasketService that keeps track of your basket items can help with that. I created a little stackblitz to demonstrate how to do this: https://stackblitz.com/edit/angular-tpenou
The relevant parts are:
export class BasketItemComponent implements OnDestroy {
#Input() flight: Flight;
paxOptions = [0, 1, 2, 3, 4];
pax = 0;
constructor(private basketService: BasketService) {
this.basketService.registerBasketItemComponent(this);
}
ngOnDestroy(): void {
this.basketService.removeBasketItemComponent(this);
}
get total() {
return this.flight.flightPrice * this.pax;
}
}
And the template:
<div class="flights-body--row" fxLayout="row" fxLayoutAlign="space-between" >
<p fxFlex="25">{{flight['flightName']}}</p>
<p fxFlex="17">{{flight['flightClass']}}</p>
<p fxFlex="18">{{flight['flightPrice'] | number}}</p>
<p fxFlex="15" fxLayout="column">
<mat-select placeholder="Ticket(s)" [(ngModel)]="pax">
<mat-option *ngFor="let pax of paxOptions" [value]="pax">{{ pax }} </mat-option>
</mat-select>
</p>
<p fxFlex="25">AED {{ total }}</p>
</div>
And the BasketService:
export class BasketService {
basketItems: BasketItemComponent[] = [];
constructor() { }
get total() {
return this.basketItems.reduce( (a, b) => a + b.total, 0);
}
registerBasketItemComponent(c: BasketItemComponent) {
this.basketItems.push(c);
}
removeBasketItemComponent(c: BasketItemComponent) {
this.basketItems.splice(this.basketItems.indexOf(c), 1);
}
}
You would also have to alter your for loop to make use of your newly created custom component:
<form novalidate #form="ngForm">
<div class="flight-table">
<header fxLayout="row" fxLayoutAlign="space-between">
<div fxFlex="25">Flights</div>
<div fxFlex="17">Class</div>
<div fxFlex="18">Price (AED)</div>
<div fxFlex="15">PAX</div>
<div fxFlex="25">Total</div>
</header>
<main>
<app-basket-item [flight]="flight" *ngFor="let flight of data.flights; let i = index">
</app-basket-item>
<div class="flights-body--row" fxLayout="row" fxLayoutAlign="space-between">
<p class="capitalize" fxFlex="75"><strong>Total transport</strong></p>
<p class="center-text" fxFlex="25"><strong>AED {{transportTotal}}</strong></p>
</div>
</main>
</div>
</form>