I am setting css style using custom code when user opens mat-select. But it returns undefined error.
In console, when I print and check element values, could see panel:undefined.
Can someone please let me know How to get the panel value.
Error: Cannot read property 'nativeElement' of undefined
Html:
<mat-select #select (openedChange)="changeHeight($event,select)">
<mat-option>1</mat-option>
<mat-option>2</mat-option>
</mat-select>
component.ts:
import { Component, OnInit, Inject, Renderer2, ElementRef } from '#angular/core';
constructor(private renderer:Renderer2){}
changeHeight(event:any,element:any)
{
if (event)
{
const height = window.innerHeight||
document.documentElement.clientHeight||
document.body.clientHeight;
this.renderer.setStyle(element.panel.nativeElement,
'max-height',
(height-10-element.panel.nativeElement.getBoundingClientRect().y)+'px')
}
}
Add this
import { ViewChild } from "#angular/core";
import { MatSelect } from "#angular/material/select";
and this to your component:
#ViewChild('select') select: MatSelect;
Afterwards you can, if you need it, pass select as a parameter or just remove the parameter and call it directly.
set elementref for element top of constructor then set the same name in your template #name with your elementref name.
Related
I have a simple Angular component which takes a debounce time as an Input parameter. The parameter type is a number with a default value equals to 0. It is used later on as a debounceTime value.
input.component.ts
import { AfterViewInit, Component, Input } from '#angular/core';
import { debounceTime } from 'rxjs';
#Component({
selector: 'app-input',
templateUrl: './input.component.html',
styleUrls: ['./input.component.scss']
})
export class InputComponent implements AfterViewInit {
#Input() debounce: number = 0;
ngAfterViewInit(): void {
//(...).pipe(debounceTime(this.debounce)).subscribe(...);
}
}
I would like to pass the debounce value from the parent component without binding to the local variable or constant.
app.component.html
<app-input debounce="200"></app-input>
It doesn't work because debounce value comes as a string type from a template. It does work when I use binding to the local constant. A console error confirms the type.
Error: src/app/app.component.html:3:12 - error TS2322: Type 'string' is not assignable to type 'number'.
I can disable strict templates checks as this is a default for Angular 15 but rxjs "debounceTime" function won't accept a string parameter.
How can I pass a number directly from the template without using bindings?
You can just use [] for input to pass a number like this:
<app-input [debounce]="200"></app-input>
you'll now get a number.
Use a setter
debounce: number = 0;
#Input() set _debounce(value:any){
this.debounce=+value;
}
Or use
.pipe(debounceTime(+this.debounce)
NOTE: The + is a confortable way to parse a string to number
I have a parent and child component. In the parent I am searching for a user and give the userId via #Input to my child component. This is working.
After my child component is getting the value I want to call a function so that I am getting the card from the user back. How do I do this? I always getting a card is undefined error.
<p (show)="serachForCard()">
User: {{userId}}
Card: {{card.id}}
</p>
My Idea was to call a function but it is still not working.
Function in my ts file:
serachForCard() {
this.cardService.getCardByUserId(this.userId)
.subscribe(
(data: Card) => this.card= data
)
}
Implement OnChanges interface inside your child component and call your function from ngOnChanges method.
#Component({
...
})
export class MyComponent implements OnChanges {
#Input() someInput: string;
ngOnChanges(): void {
// do something with this.someInput
}
}
Documentation: OnChanges
A lifecycle hook that is called when any data-bound property of a directive changes. Define an ngOnChanges() method to handle the changes.
I need to put a loading in multiple components of my project. So instead of putting the same HTML over and over again across the components, I need to know what's the best way to not repeat code, but I don't know if this is correct thing to do. I have created a component called loading-graphic, which I bind it in every HTML file of the respective components. I read about ngTemplateOutlet and ngContent, but to be honest it doesn't make sense in my head to use it for this case (and I don't get it too... I'm a beginner on it). So, on what should I bet? Thanks.
Base on your question, I think creating Reusable Components with NgTemplateOutlet would be the best solution to avoid repeating HTML in different component Templates. It allows you to pass parameters base on your host component and makes your Angular app easier to test and develop since it sllows easily modified reusable component for various use cases without having to modify individual components itself.
Since you are a begginer I am going to Illustrate simple way of using NgTemplateOutlet, however dive deep later on Templates and Stamps.
Imaging you have a reusable Search component where you want to hide a check box base on the parent component. Your Template will look like below.
we pass data from the parent component to the child/Search component using #Input and property binding, so we define which checkboxes to hide base on Parent component.
here is the code sample for Search Component
search.component.ts
======================
import { Component, OnInit, Output, EventEmitter, Input } from '#angular/core';
#Component({
selector: 'app-search',
templateUrl: './app-search.component.html',
styleUrls: ['./app-search.component.css']
})
export class AppSearchComponent implements OnInit {
accountName: string = '';
#Output() accountSearchChange = new EventEmitter<string>(); //1. Event Binding to pass data from Child to Parent Component ->Int
#Input() searchMode: 'account' | 'holder' | 'distribution' = 'account'; //Use NgTemplateOutlet for reusable componenet
constructor() { }
ngOnInit() {
}
//2. Event Binding to pass data from Child to Parent Component ->Invoke Emit
doSearchFilter(searchText: string) {
console.log('Search Child: doSearchFilter -> ' + searchText);
this.accountSearchChange.emit(searchText);
}
clearFilters() {
console.log('Account Search: Clear Filter is called');
this.accountName = '';
}
}
search.component.html
=====================
<ng-container [ngSwitch]="searchMode">
<div class="input-full-width" *ngSwitchCase="'account'">
<mat-checkbox class="example-container check-full-width">Show active and pending only</mat-checkbox>
</div>
<div class="input-full-width" *ngSwitchCase="'holder'">
<mat-checkbox class="example-container check-full-width">View only holders with missing requirements</mat-checkbox>
</div>
<div class="input-full-width" *ngSwitchCase="'holder'">
<mat-checkbox class="example-container check-full-width">View only active Holders</mat-checkbox>
</div>
</ng-container>
I am using Search component inside Account component and below is the code sample.
in HTML file i am referring app-search css selector and pass the search Mode defined in ts.
import { Component, OnInit, ViewChild, AfterViewInit } from '#angular/core';
import { MatSort, MatTableDataSource, MatPaginator } from '#angular/material';
import { Router } from "#angular/router";
import { Observable } from 'rxjs';
import { AccountService } from 'src/app/core/services/account.service';
import { Deal } from 'src/app/core/models/deal';
#Component({
selector: 'app-account',
templateUrl: './account.component.html',
styleUrls: ['./account.component.css']
})
export class AccountsComponent implements OnInit, AfterViewInit {
displayedColumns: string[] = ['accountId', 'accountShortName', 'accountType'];
public dealDataSource = new MatTableDataSource<Deal>();
dealsObservable: Observable<Deal[]>;
searchMode = 'distribution';
isLoadingResults = true;
#ViewChild(MatSort) sort: MatSort;
#ViewChild(MatPaginator) paginator: MatPaginator;
constructor(private router: Router, private api: AccountService) { }
......................................................
<app-search (accountSearchChange)='doFilter($event)' [searchMode]="searchMode"></app-search>
Hope this is clear.
i think loading component is not a bad Idee. Use loading component in your app-root component. You can use a loading.service and interceptor to display or hide the component. You will get automatically a Loading Indicator for each API call.
sample: https://medium.com/#zeljkoradic/loader-bar-on-every-http-request-in-angular-6-60d8572a21a9
I am trying to pass data from parent component to child components, but I am getting data is undefined,
In my below code
parent component
here part_data I have declared in service
this._PartService.GetDataValues(this.search.item, this.search.filter_type, release_part).subscribe(
result => {
this.resultData = result;
this._PartService.part_data = this.resultData.data
})
child component
Here I am using observer
this.searchService.observer.subscribe(search => {
this.part = search.item;
this.specification = this._PartService.part_data.partMasterDataCompleteList[0];
})
Here I am getting PartMasterDataCompleteList[0] is undefined
Use this as the child component:-
#Component({
selector: 'app-hero-detail',
templateUrl: './hero-detail.component.html',
styleUrls: ['./hero-detail.component.css']
})
export class HeroDetailComponent implements OnInit {
#Input() hero: Hero;
constructor() {}
ngOnInit() {}
}
When you need to feed data to the component, use the below code:-
<app-hero-detail [hero]="selectedHero"></app-hero-detail>
The hero property is #Input which receives data from the outer environment.
For more information, refer to Angular tutorial:-
https://angular.io/tutorial/toh-pt3
You can pass your data in sub component and get using #Input method.
There is one other way to pass data from one component to other component.
Click here
As i having parent and child component as follows,
Child Component (RWTaxComponent)
import { Component, OnInit, Input, Output, EventEmitter } from '#angular/core';
#Component({
selector: 'rw-tax',
templateUrl: 'rw.tax.component.html'
})
export class RWTaxComponent implements OnInit {
#Input() hsn: string = '';
#Input() srno: string = '';
#Input() taxPercent: number;
#Output() taxPercentChange: any = new EventEmitter();
constructor(
) { }
ngOnInit() {
}
ngOnChanges(event) {
console.log('HSN:: '+this.hsn);
console.log('SRNO:: '+this.srno);
if (this.hsn && this.srno) {
// Doing my logic here to find taxPercent
this.taxPercentChange.emit(this.taxPercent);
}
}}
Child component template (rw.tax.component.html) is,
<p>{{taxPercent | number:'1.2-2'}}</p>
And i invoked the above child component in my parent component as follows,
<rw-tax [(hsn)]="lineItem.hsn" [(srno)]="lineItem.srno" [(taxPercent)]="lineItem.taxPercent"></rw-tax>
I want to change taxpercent whenever hsn/srno is changed, these hsn/srno is not changed in RWTaxComponent itself, it is changed by parent component.
So i used ngOnChanges() event to detect that hsn/srno is changed and it getting invoked when ever i change hsn and srno in my parent component.
But the problem is after doing my logic to find taxpercent am trying to update the value in parent component by this.taxPercentChange.emit(this.taxPercent); and getting error "Expression has changed after it was checked"
Am i wrongly understood the Angular2 lifecycle ?
Please suggest a right way to do it...
This issue occurs when there is a change in input of the component and on that change if we are trying to change or emit any property.
Solution 1:
try promises resolve the changes and in then block emit the output event.
Solution 2 : (may not work)
Try setTimeout method on emitting the value