I would like to add an image after every 8 items. This image would be the only item in the ion-item element. This image is not part of the items array and come from another array.
I'm using this (simplified) code:
<ion-list>
<ion-item *ngFor="let item of items; let i = index" (click)="goTo()>
<img src="{item.image}}">
<h2>{{ item.name }}</h2>
</ion-item>
</ion-list>
How could I insert an image every 8 items ?
You can use the index of the ngFor and the modulo operator to achieve that. Please take a look at this working StackBlitz project (the demo uses Ionic 3 but the logic is exactly the same for Ionic 4).
In the following code I just created two list to have some items to show in the view:
Component
import { Component } from '#angular/core';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
public items = [];
public otherImages = [];
constructor() {
// Prepare some items
for(let i = 1; i < 30; i++) {
this.items.push({
name: `Item ${i}`,
image: `https://via.placeholder.com/160x160?text=Item+${i}`
});
}
// Prepare some extra images
for(let j = 1; j < 5; j++) {
this.otherImages.push({
image: `https://via.placeholder.com/160x160?text=Another+Image+${i}`
});
}
}
}
Template
<ion-header>
<ion-navbar>
<ion-title>Home</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<h2>Welcome to Ionic!</h2>
<ion-list>
<ng-container *ngFor="let item of items; let i = index">
<ng-container *ngIf="i > 0 && i % 8 === 0; else noImage">
<!-- First show the image -->
<ion-item *ngIf="i >= 8 && otherImages[i / 8 - 1]">
<img [src]="otherImages[i / 8 - 1].image">
</ion-item>
<!-- Then show the item -->
<ion-item>
<img [src]="item.image">
<h2>{{ item.name }} </h2>
</ion-item>
</ng-container>
<ng-template #noImage>
<!-- Only show the item -->
<ion-item>
<img [src]="item.image">
<h2>{{ item.name }}</h2>
</ion-item>
</ng-template>
</ng-container>
</ion-list>
</ion-content>
In the code above, the first *ngFor="let item of items; let i = index" just goes through the list of items in the items array.
Then we can check the index to see if i > 0 && i % 8 === 0 which means that the current index is the 8th, 16th, 24th,... element of the array.
Since arrays are zero based, the index 8 means the 9th element. This means that we need to show first the extra image, and then the 9th element from the items array.
Please notice that in order to get the right image from the otherImages array, we need to get the index doing: otherImages[i / 8 - 1].image.
<ng-container *ngIf="i > 0 && i % 8 === 0; else noImage">
<!-- First show the image -->
<ion-item *ngIf="i >= 8 && otherImages[i / 8 - 1]">
<img [src]="otherImages[i / 8 - 1].image">
</ion-item>
<!-- Then show the item -->
<ion-item>
<img [src]="item.image">
<h2>{{ item.name }} </h2>
</ion-item>
</ng-container>
If the index is different, we just need to show the item:
<ng-template #noImage>
<!-- Only show the item -->
<ion-item>
<img [src]="item.image">
<h2>{{ item.name }}</h2>
</ion-item>
</ng-template>
Related
Im working in a menu where the current section is gonna be select it on scroll, but when i try to get the offsetTop of the elements i get alway 0 for some reason, on the parentElement a get value for offsetTop but in this case does not work for me using the offsetTop of the parentElement because i have many childElements inside with different offsetTop.
Template
<div nxCol="12,12,8" *ngIf="selectedPlan && selectedPlan?.id != quotePlan.id && !loading">
<!-- Limits -->
<h5 class="nx-margin-y-0" translate [id]="LIMITS_AND_PRICING_SECTION_ID" #limitPricing> page.service-detail.available-plan.menu.limit-pricing </h5>
<mat-list class="mat-list-padding-none">
<mat-list-item><strong translate>global.label.limits</strong></mat-list-item>
<mat-divider></mat-divider>
<mp-service-plan-limit-display [limits]="selectedPlan?.limits"></mp-service-plan-limit-display>
</mat-list>
<mat-list class="nx-margin-top-m mat-list-padding-none">
<mat-list-item><strong translate>page.service-detail.pricing</strong></mat-list-item>
<mat-divider></mat-divider>
<mp-service-plan-price-display
[prices]="selectedPlan?.prices"
[planId]="selectedPlan?.id"
[availablePlans]="true"
></mp-service-plan-price-display>
</mat-list>
<!-- /Limits -->
<!-- Documentation -->
<ng-container *ngIf="planSpecDocuments.length > 0">
<h5 class="nx-margin-top-3xl nx-margin-bottom-2xs" translate [id]="DOCUMENTATION_SECTION_ID" #documentation>
page.service-detail.available-plan.menu.documentation
</h5>
<p class="nx-margin-0">Details how to embed the Service into your application.</p>
<mat-list class="mat-list-padding-none mat-file-list">
<ng-container *ngFor="let specDocument of planSpecDocuments">
<mat-list-item>
<ng-container *ngIf="(partnerId$ | async)?.length && !isPreview; else documentSpecDisabledTpl">
<a [href]="specDocument.specDocumentUrl | async" [download]="specDocument.name" class="file-links file-link-active">
<span class="icon-document mpt-icon"></span>{{ specDocument.name }}
</a>
</ng-container>
<ng-template #documentSpecDisabledTpl>
<span class="file-links">
<span class="icon-document mpt-icon"></span>
{{ specDocument.name }}
</span>
</ng-template>
</mat-list-item>
</ng-container>
</mat-list>
</ng-container>
<!-- /Documentation -->
</div>
Component
#ViewChild('limitPricing') limitPricing!: ElementRef;
#ViewChild('documentation') documentation!: ElementRef;
#ViewChild('swaggerSpec') swaggerSpec!: ElementRef;
timer(500).subscribe(() => {
console.dir(this.limitPricing?.nativeElement);
this.limitPricingOffset = this.limitPricing?.nativeElement.offsetTop - 100; // offsetTop here is always zero
this.documentationOffset = this.documentation?.nativeElement.offsetTop - 100;
this.swaggerSpecOffset = this.swaggerSpec?.nativeElement.offsetTop - 100;
});
Declare the element in the constructor, as private _element: ElementRef) { ...}
this._element.nativeElement.getBoundingClientRect().top
I am looping through a list of products and then checking if the product Id is already present in an array of products(objects) , then printing the quantity else if the product is not in the object then trying to print 0. Below is the code I have tried till now.
<ion-item class="added" *ngFor="let item of fetchProducts();let key=index;">
<ng-container *ngFor="let cartitem of cart" >
<span class="count" *ngIf="cartitem.p_id==item.p_id;">
{{cartitem.qty}}
</span>
</ng-container>
</ion-item>
How to print 0 if item is not in the cartitem in same span.
You can simply do this using ternary operator like below.
<ng-container *ngFor="let cartitem of cart" >
<span class="count">
{{cartitem.p_id==item.p_id ? cartitem.qty : 0 }}
</span>
</ng-container>
Can use *ngIf else condition inside your for loop -
<ion-item class="added" *ngFor="let item of fetchProducts();let key=index;">
<ng-container *ngFor="let cartitem of cart">
<span class="count" *ngIf="cartitem.p_id==item.p_id; then content else other_content">
</span>
<ng-template #content>{{cartitem.qty}}</ng-template>
<ng-template #other_content>0</ng-template>
</ng-container>
</ion-item>
Instead of using template logic I would move the logic to the class.
cart and products are obviously available in the class.
Therefore adjust the fetchProducts function in the class to return the products list as needed (with the quantity information) and use a single ngFor loop in the template.
Or add a new function getProductsWithQuantity...
In your class
public getProductsWithQuantity() {
return this.fetchProducts().map(product => {
...product,
quantity: this.getQuantity(product);
});
}
private getQuantity(product) {
const foundCartItem = this.cart.find(cartItem => product.id === cartItem.id);
if (foundCartItem) {
return foundCartItem.qty;
}
return 0;
}
In your template:
<ion-item class="added" *ngFor="let item of getProductsWithQuantity();let key=index;">
<span class="count">
{{item.qty}}
</span>
...
im having this issue which is breaking my head right now and I cant figured out what is the problem.
I have a form called device-form, which is called from the dashboard using routing. The device form has inside 4 tabs (angular material), and the second one must show a Highchart component, when you select that tab, the container appears but not the chart.... if you wait, the chart will never show up, but if you click in any place the chart appears, also if you hover some item with mouse, or if you resize the screen!!
App-chart-box.component.html:
<mat-card fxFlex="100%" fxLayout="row">
<mat-card-title></mat-card-title>
<mat-card-content>
<mat-tab-group class="form-wizard tab-group">
<mat-tab *ngFor="let chart of charts; let i = index">
<ng-template mat-tab-label>
{{ chart.sensorTypeName }}
</ng-template>
<div class="tab-content">
<vr-chart class="card-img-top" [title]="chart.sensorTypeName" [yTitle]="chart.sensorTypeUnit" type="spline" [series]="chart.series"
[period]="period" [minThreshold]="minThreshold" [maxThreshold]="maxThreshold"></vr-chart>
</div>
</mat-tab>
</mat-tab-group>
</mat-card-content>
<mat-card-actions>
<small class="footer">Last updated: {{ dateUpdated }}</small>
</mat-card-actions>
device-form.component.html:
<mat-tab>
<ng-template mat-tab-label>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<span>Statistics</span>
</div>
</ng-template>
<div fxFlex="100%" class="shipping-address" fxLayout="column">
<mat-form-field class="select-period">
<mat-select placeholder="{{ 'DEVICE_FORM.PERIOD' | translate }}" [(ngModel)]="filterPeriodSelected" (change)="loadDeviceChart()">
<mat-option *ngFor="let period of chartPeriods" [value]="period.value">
{{ period.label | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<!-- <div *ngIf="deviceCharts.length == 0" class="alert bg-primary alert-dismissible fade show" role="alert">
<strong class="ng-tns-c6-44"> {{'MESSAGE.NO_DATA_TO_SHOW' | translate}}</strong>
</div> -->
<vr-chart-box *ngIf="deviceCharts" class="fix-width" [charts]="deviceCharts" [period]="filterPeriodSelected"></vr-chart-box>
</div>
</mat-tab>
As you see the app-chart-box component is rendered, actually we can always see the footer loaded, always, but the body of the chart dont appear until click or hover o whatever.
Update:
Actually Im having the very same issue with a table which is filled with data coming from an API when you change a dropdown up there in the main dashboard page, the table component is always listening to .onCompanyChange.subscribe, read all the data, but the table is not showing until you make click or hover or whatever event... this is the code :
Table component:
export class CompanyHistoryComponent implements OnInit {
#ViewChild('tableInput') tableInput: ElementRef;
#Input() companyId = 0;
private rows: Observable<Array<any>>;
// public rows: any[] = [];
resultsLength: number;
dataSource: ListDataSource<any> | null;
database: ListDatabase<any>;
tableHover = true;
tableStriped = true;
tableCondensed = true;
tableBordered = true;
constructor(
public sensorService: SensorService,
public dashboardService: DashboardService,
private cd: ChangeDetectorRef,
) {
// Selected company changed
this.dashboardService.onCompanyChange.subscribe(
(id: number) => {
this.companyId = id;
this.loadSensorHistory();
cd.detectChanges();
});
}
public loadSensorHistory() {
const sDate: Number = moment().subtract(30, 'day').unix()
const eDate: Number = moment().unix();
this.sensorService.getSensorDataHistory(this.companyId, sDate, eDate, null, null, 1, 10).subscribe(response => {
if (response.statusCode !== 200 || !response.data) {
// this.toastrService.error('MESSAGE.NO_DATA', 'SENSOR_FORM_LIST.TITLE', { positionClass: 'toast-top-right', closeButton: true });
} else {
this.rows = response.data.list;
}
});
}
ngOnInit() {
}
As you see this time I added detectChanges() without any results :(
let me know if you have an idea.
This is the HTML of the Table :
<table class="table" [class.table-hover]="tableHover" [class.table-striped]="tableStriped" [class.table-condensed]="tableCondensed"
[class.table-bordered]="tableBordered">
<thead>
<tr>
<th>Date</th>
<th>Device</th>
<th>Sensor</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of rows" [class.row-failed]="row.failed > 0">
<td>{{ row.data_createdDate | date:'MM/dd/yyyy HH:mm:ss a Z' }}</td>
<td>
{{ row.deviceMAC }}
</td>
<td>
{{ row.sensorNumber }} - {{ row.sensorName }}
</td>
<td>{{ row.value }}</td>
</tr>
</tbody>
</table>
You should call ChangeDetectorRef detectChanges()
It might be an incorrect pipe operator.
I had a similar issue with Angular 11 and mat-table. The rows wouldn't show up until I hovered over them. My issue was that I was using an invalid pipe operator on one of the columns. I was using a decimal pipe on a string column. Once I corrected that it works perfectly.
Here is my element
<div *ngFor="let item of items |xxxx ; let l = count "> <div>
<!--use {{l}} in other element-->
How can I get the value of {{ l }} to use it outside this div or to use it in component.ts after filter directly?
Wrap your template in <ng-container>. Like this:
<ng-container *ngFor="let item of items |xxxx ; let l = count ">
<div ><div>
<!--use {{l}} in other element-->
</ng-container>
i am add animation effect using animate.css i need to time delay of the each child element
for the referral add image
How to add time delay in ionic 2 or Angular2 ?
also referred this to implement in ionic2
<ion-item *ngFor="let transLeaf of transLeafTmp" class="animated zoomIn">
<ion-avatar item-left>
<i class="icon" [style.background-color]="transLeaf.bgColor"><img src="{{transLeaf.categoryIcon}}"></i>
</ion-avatar>
<p>{{transLeaf.narration}}</p>
<h3>heading</h3>
<ion-note item-right class="transValue">
<h2 test111</h2>
</ion-note>
</ion-item>
HeyHey!
I didn't try but I think it should work! Good luck! ;)
HTML
<ion-item *ngFor="let transLeaf of transLeafTmp;" [class.animated]="transLeaf.isAnimated" class="zoomIn">
<ion-avatar item-left>
<i class="icon" [style.background-color]="transLeaf.bgColor"><img src="{{transLeaf.categoryIcon}}"></i>
</ion-avatar>
<p>{{transLeaf.narration}}</p>
<h3>heading</h3>
<ion-note item-right class="transValue">
<h2 test111</h2>
</ion-note>
</ion-item>
TypeScript :
private ngAfterViewInit(){
this.animateTransLeaf();
}
private animateTransLeaf(){
let i = -1;
while (++i < this.transLeafTmp.length) {
setTimeout(() => {
this.transLeafTmp[i].isAnimated = true;
}, 50 * i);
}
}