Angular nativeElement offsetTop alway 0 - javascript

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

Related

Applying a class to a array item which is not in the original array using angular

I have an array which displays data as mat-select dropdown. The user can select any tags he wants. Also we have some free form text field inside the dropdown of tags from which the user can create his own custom text apart from tags array. See below:
display.component.html
<mat-form-field class="full-width">
<mat-label class="full-width">Select Tag</mat-label>
<mat-select formControlName="tagsControl" [(ngModel)]="project.tags" multiple>
<mat-select-trigger>
<mat-chip-list>
<mat-chip *ngFor="let tagging of firstFormGroup.value.tagsControl" [removable]="true"
(removed)="onTaggingRemoved(tagging)">
{{ tagging }}
<mat-icon matChipRemove>cancel</mat-icon>
</mat-chip>
</mat-chip-list>
</mat-select-trigger>
<mat-option *ngFor="let tagging of tagsList"
[value]="tagging">{{tagging}}</mat-option>
<mat-form-field style="margin-left: 17px; margin-top: 8px;">
<input matInput placeholder="Add New Tag" #input>
</mat-form-field>
<button mat-stroked-button color="primary" style="margin-left: 4px;" class="common-button"
(click)="addNewTags(input.value)">
Add</button>
</mat-select>
</mat-form-field>
display.component.ts
onTaggingRemoved(tagging: string){
const taggings = this.firstFormGroup.value.tagsControl as string[];
this.removeFirst(taggings, tagging);
this.firstFormGroup.controls['tagsControl'].patchValue(taggings);
}
removeFirst<T>(array: T[], toRemove: T): void {
const index = array.indexOf(toRemove);
if (index !== -1) {
array.splice(index, 1);
}
}
addNewTags(value: string) {
this.tagsList.push(value);
}
table.component.ts
tags: string[] = ["Prod", "Stg", "Dev", "QA"];
table.component.html
<ng-container matColumnDef="tags">
<mat-header-cell *matHeaderCellDef>
Tags </mat-header-cell>
<mat-cell *matCellDef="let element; let i = index;">
<span *ngIf="element.tags && element.tags.length < 35" matTooltipPosition="above"
matTooltip="{{element.tags}}">
<span *ngIf="element.tags == 'Prod'">
<span class="badge badge-pill bg-info font-color-tags">{{ element.tags.join(', ') }}</span>
</span>
<span *ngIf="element.tags == 'Stg'">
<span class="badge badge-pill bg-primary font-color-tags">{{ element.tags.join(', ')}}</span>
</span>
<span *ngIf="element.tags == 'Dev'">
<span class="badge badge-pill bg-secondary font-color-tags">{{ element.tags.join(', ')}}</span>
</span>
<span *ngIf="element.tags == 'QA'">
<span class="badge badge-pill bg-success font-color-tags">{{ element.tags.join(', ')}}</span>
</span>
</span>
<span *ngIf="element.tags.length == 0">
-
</span>
</mat-cell>
</ng-container>
My question is i am adding the badges the color codes to different array items based in its value. I mean if the tags array item value is "QA", i am adding different color, similarly for "Prod" different color. But i need to add some custom badge color to the custom tag which user created on his own other than tags array. I am planning to add a unique badge to all custom color badge then how to achieve this?
Please help me.
Not sur I got your question but if I did, you want to apply to any new tag in the four you have the correct badge.
In this way, your html code have to be dynamic with [ngClass] like :
<span [ngClass]="method_to_return_badge(element.tags)">{{ element.tags.join(', ')}}</span>
You then have to define the method_to_return_badge in your ts and with a switch on the four possibilities return the right badge.
This will let you write less code in your html and make it more readable.
Hope it helps you

How to use ngb-accordion in a ngFor loop?

I'm using the ngb-accordion in a for loop. It's working for a part, but it seems that it is not possible to open the active element in a for loop.
I want make it possible that when one of the accordion elements includes the property 'OpenAccordion' the panel of that element opens. So I need to define the activeIds and the id for each element inside the array to make it possible.
When I define the *ngFor outside the <ngb-accordion></ngb-accordion> it works because I can link the index to the activeIds and id. But his time it's not possible to open one panel at a time because the panels are seperate.
[closeOthers] is working on this one but open one panel at a time is not working:
<ngb-accordion
class="accordion-item"
[closeOthers]="true"
activeIds="true-{{i}}">
<div *ngFor="let child of items?.children | async; let i = index">
<ngb-panel id="{{(child?.value?.properties | async)?.property.includes('OpenAccordion')}}-{{i}}">
<ng-template ngbPanelHeader let-opened="opened">
<div class="d-flex align-items-center justify-content-between">
<button ngbPanelToggle class="btn btn-link container-fluid text-left">
<h5>
{{ (child?.value?.properties | async)?.title }}
</h5>
</button>
</div>
</ng-template>
<ng-template ngbPanelContent>
<h4>test</h4>
</ng-template>
</ngb-panel>
</div>
</ngb-accordion>
When I define the *ngFor inside the <ngb-accordion></ngb-accordion> the [closeOthers] functionality works, but this time it's not possible to open the active accordion panel. Because the index of the activeId is unknown and does not match the id of the active element.
Open one panel at a time is working but [closeOthers] is not working on this one:
<ng-template ngFor let-child [ngForOf]="items?.children | async" let-i="index">
<ngb-accordion
class="accordion-item"
[closeOthers]="true"
activeIds="true-{{i}}">
<ngb-panel id="{{(child?.value?.properties | async)?.property.includes('OpenAccordion')}}-{{i}}">
<ng-template ngbPanelHeader let-opened="opened">
<div class="d-flex align-items-center justify-content-between">
<button ngbPanelToggle class="btn btn-link container-fluid text-left">
<h5>
{{ (child?.value?.properties | async)?.title }}
</h5>
</button>
</div>
</ng-template>
<ng-template ngbPanelContent>
<h4>test</h4>
</ng-template>
</ngb-panel>
</ngb-accordion>
</ng-template>
How can I make it possible to let both functions [closeOthers] and open one panel at a time works in a *ngFor loop?
In you first solution, there is a mistake, you used the variable i before it is declared:
<ngb-accordion
class="accordion-item"
[closeOthers]="true"
activeIds="true-{{i}}"> // i used before declaration?
<div *ngFor="let child of items?.children | async; let i = index">
I just created a simple sample to use ngFor with ngb-accordion, it works good.
You should try to ngFor inside the <ngb-accordion>:
TS:
import { Component } from '#angular/core';
#Component({
selector: 'ngbd-accordion-static',
templateUrl: './accordion-static.html'
})
export class NgbdAccordionStatic {
accordionSamples = [
{
id: 1,
title: 'Simple',
content: `Simple content sample`
},
{
id: 2,
title: 'Fancy',
content: `Fancy content interesting`
}
]
}
HTML:
<ngb-accordion [closeOthers]="true" activeIds="acc-2">
<ngb-panel *ngFor="let acc of accordionSamples" id="acc-{{acc.id}}" title="{{acc.title}}">
<ng-template ngbPanelContent>
{{acc.content}}
</ng-template>
</ngb-panel>
</ngb-accordion>
https://stackblitz.com/edit/angular-mdbedd?file=src%2Fapp%2Faccordion-static.html

Add Item after NTH element with ion-item

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>

Angular 5 is not rendering the component until click or hover anywhere

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.

How to limit repeated number of items in ng-repeat. angularjs?

I'm working in contact card. I need to add Name and contact number in the list. But the condition is Name and contact must be added only 2 times. for ex.
Contact Card-1
Name-A
Name-B
Contact-1
Contact-2
Whenever I click on the button my name and contact get added in the list but with certain condition.
My code is
<md-list-item ng-show="showContactList" class="md-2-line" ng-repeat="numbers in contactList track by $index" >
<i ng-show="numbers.type == 'test'" class="material-icons md-avatar-icon">textsms</i>
<i ng-show="numbers.type == 'CELL' || numbers.type == 'EXT'" class="material-icons md-avatar-icon">phone</i>
<div class="md-list-item-text" ng-class="{'md-offset': phone.options.offset }">
<h3> {{ numbers.type }} </h3>
<p> {{ numbers.value }} </p>
</div>
<i class="material-icons md-avatar-icon add-rm-icon margin-right" ng-click="arrayText.push(numbers);">add</i>
</md-list-item>
You can use:
<div ng-repeat="item in items | filter:{visible: true} | limitTo: 50">
<p>{{item.id}}</p>
</div>
{visible- true} will return a list of all visible items
You can take a look at the angularjs docu for more information on the filter filter. http://docs.angularjs.org/api/ng.filter:filter

Categories

Resources