How to test Input and mat option inside Mat select in Angular - css

I have the following below code in my Html file :
Product.html
<mat-select id="selectProduct"
placeholder="Product"
multiple
[(ngModel)]="request.selectedProductIds"
#productModel="ngModel"
disableOptionCentering
(openedChange)="openedChange($event,'productFilterText')"
panelClass="contnetPanel"
#demo>
<input id="txtFilterProduct"
[(ngModel)]="productFilterText"
class="mat-select-filterbox"
placeholder="Search Product"
autocomplete="off"
(keyup)="filterProducts($event.target.value)" />
<div class="matOptionList">
<mat-option #matOption
*ngFor="let pfoduct of masterData.product"
[value]="product.id"
(click)="onproductClick(matOption,product)">{{product.name}}</mat-option>
</div>
</mat-select>
I want to write Unit test case in Angular for input element inside mat-select element in .ts file like below:
it('open the product dropDown', async(() => {
componentFixture.detectChanges();
spyOn(searchComponent, 'filterProducts');
let input = componentFixture.debugElement.query(By.css('#txtFilterProduct'));
input.triggerEventHandler('keyup', null);
componentFixture.detectChanges();
expect(searchComponent.filterProducts).toHaveBeenCalled();
expect(searchComponent.filterProducts).toHaveBeenCalledTimes(1);
}));
I am getting the error: calling triggerEventHandler on undefined.
Similarly it is giving the same error for mat-option.
Please suggest or tell how we can do unit test for input and mat-option inside mat-select for dropdown

Related

Conditionally applying css to mat form field

I am working on an angular application and I am using mat form fields in it. For changing color of botttom border of mat form field I am using mat-form-field-ripple css which is inbuilt for mat form field. CSS is as follows.
.mat-form-field-ripple {
background-color: #f9c940!important;
}
When I use this CSS, it automatically gets applied to all form fields. I have to apply #f9c940 color in mat-form-filed-ripple in when one condition is true and a different color when another condition is true. My mat form field code is as follows:
<mat-form-field appearance="fill">
<mat-label [ngClass]="{}">Name</mat-label>
<input formControlName="Name" readonly>
</mat-form-field>
I was trying to do it using ngClass as shown above but not getting it. How can I do that?
ngClass doesn't work with <mat-form-field>. Use this syntax-
<mat-form-field [class.mat-form-field-invalid]="booleanVariable">
and in the .ts file, you can conditionally update the booleanVariable
You can apply classes conditionally like this:
<mat-label [ngClass]="{'your-class': foo=='foo', 'your-class-another':bar=='bar' }">
Name
</mat-label>
Just create your own CSS class:
.mat-form-field-ripple {
//your standard styling,
}
.myBackgroundColor {
background-color: #f9c940!important;
// add this selector below the .mat-form-field-ripple selector so that it will override..
}
Then apply it conditionally to your label:
<mat-form-field appearance="fill">
<mat-label [ngClass]="{'myBackgroundColor': mycheck}">Name</mat-label>
<input formControlName="Name" readonly>
</mat-form-field>
In your component you have the expression it is based on:
mycheck = true; // you will modify this true to any expression that is transformable to a boolean. If true, your class is applied, if false then not..

How adjust the position of mat-option

I am running angular app, I have autocomplete field ,I want adjust position of this . I referred official document enter link description here under method ,
updatePosition -
Updates the position of the autocomplete suggestion panel to ensure that it fits all options within the viewport.
I am not sure how to use
This is my template.html
<mat-form-field [style.cursor]="pointer" [style.cursor]="pointer" [style.width.px]=300 >
<input class="selectCustomer" class="selectCustomerData" id="inputCustomer" matInput [matAutocomplete]="auto" [formControl]="customerFilterControl" [(ngModel)]="customerName">
<mat-icon matSuffix>keyboard_arrow_down</mat-icon>
<p id="spandiv">{{customerName}}</p>
<mat-autocomplete dropdown-arrow="true" panelWidth ="450px" #auto="matAutocomplete" [displayWith] = "displayFn">
<mat-option class="CustomerDropDown" *ngFor="let customer of filteredOptions | async" [value] ="customer.AccountID +' '+'('+ customer.AccountName + ')'" (onSelectionChange)="onCustomerChange(customer)">
{{customer.AccountID}} ({{customer.AccountName}}) <p id="spandiv1">{{customer.AccountID}} ({{customer.AccountName}})</p>
</mat-option>
</mat-autocomplete>
</mat-form-field>
As shown the pic below I want the mat-option to move little towards left
As shown in this pic, class highlighted always gets style applied to left:779px but I want to remain atv 775px
I was able to style this using
.mat-autocomplete-panel {
position:relative;
right:3px;
}

How to hover over the angular autocomplete options

I am running angular app, I have autocomplete when I hover over the autocomplete mat-options, I want to see entire customer number and name. I tried to do this
<mat-form-field [style.cursor]="pointer" [style.cursor]="pointer" [style.width.px]=300 >
<input class="selectCustomer" class="selectCustomerData" id="inputCustomer" matInput [matAutocomplete]="auto" [formControl]="customerFilterControl" [(ngModel)]="customerName">
<mat-icon matSuffix>keyboard_arrow_down</mat-icon>
<p id="spandiv">{{customerName}}</p>
<mat-autocomplete dropdown-arrow="true" panelWidth ="450px" #auto="matAutocomplete" [displayWith] = "displayFn">
<mat-option class="CustomerDropDown" *ngFor="let customer of filteredOptions | async" [value] ="customer.AccountID +' '+'('+ customer.AccountName + ')'" (onSelectionChange)="onCustomerChange(customer)">
{{customer.AccountID}} ({{customer.AccountName}}) <p id="spandiv1">{{customer.AccountID}} ({{customer.AccountName}})</p>
</mat-option>
</mat-autocomplete>
</mat-form-field>
Basically I want to see value when I hover on the mat-option
One easy way to do this is to put the option text inside a div element with a small max-width. Then style that div element so overflows are normally hidden, but not when the option text is being hovered.
Example: https://stackblitz.com/edit/angular-vzfpjy
One problem with this solution is that the user has to hover over the text of the option to show the full value.

method gets called multiple times in angular material autocomplete

We have created a component using Angular material's autocomplete. To display the options, we are traversing through an array of 51 objects. I am applying a CSS class to the already selected option. The isAccountingTypeSelected method determines whether the option was selected or not.
The method gets called 51*28 = 1428 times. I don't seem to understand the reason? It should only be called 51 times, shouldn't it?
<mat-form-field class="full-width">
<input type="text" matInput #autoCompleteInput [formControl]="autocompleteForm" [matAutocomplete]="auto" placeholder="Choose Accounting Type" aria-label="Number">
<span matSuffix class="close-icon hover" *ngIf="autoCompleteInput.value" (click)="clearAll($event)"></span>
<span matSuffix class="arrow-drop-down-icon hover" (click)="openPanel()"></span>
<mat-autocomplete #auto="matAutocomplete" (optionSelected)="accountingTypeSelected($event)">
<mat-option *ngFor="let accountingType of filteredAccountingTypes | async" [value]="accountingType.code">
<span class="accounting-type-options" [class.selected]="isAccountingTypeSelected(accountingType.code)">
{{ accountingType.name + ' (' + accountingType.code + ')' }}
</span>
</mat-option>
</mat-autocomplete>
</mat-form-field>
isAccountingTypeSelected(code: string): boolean {
console.log('I was called');
if (this.selectedAccountingTypes.find((accountingType: AccountingType) => accountingType.code === code)) {
return true;
}
return false;
}
Angular uses changedetection lifecycle multiple times to check if the function has changed for [class.selected] or ngClass. If you use function, it will call multiple times. For this reason the use of function is not advised when you bind, instead you should calculate the values in your component.ts file and just bind the values to ngClass or [class].
Example: Stackblitz
N.B: We know when we change selected value it triggers a event change, we can calculate it and attach the calculation result to the [class.my-class] or ngClass.
Angular is gonna evaluate that expression every time it checks for changes, which in your case might be the css being added to your span elements.
Calling methods from the template in a for loop is not the best approach because they are called very often. You should instead store the result in a property and bind to this property instead.
It is a bind problem. Angular checks more times the result value. You can try with ChangeDetectionStrategy.CheckOnce
Your ngFor loop needs track a specific id so it won't re-render for nothing. Try this:
<mat-option *ngFor="let accountingType of filteredAccountingTypes | async; trackBy: trackByCode"[value]="accountingType.code">
</mat-option>
Then you add this function:
trackByCode(index: number, accountingType: yourType): string {
return accountingType.code;
}

How to get a typed value from an Input?

I would like to get a typed value from a ReactStrap Input component, via the onChange function.
The aim is to use a text input only but be able to get a specific typed value (String or Number in my example below).
<Input valueAs="String" name="input1" type="text" onChange={this.onChange}></Input>
<Input valueAs="Number" name="input2" type="text" onChange={this.onChange}></Input>
You can see the expected type is defined by the valueAs attribute.
I expect to get an event in this.onChange with:
a value as a String for Input 1
a value as a Number for Input 2
What is the best way to do this with React / Reactstrap?
Input component of reactstrap does not have a property called valueAs. To get value in a format you need, you can do:
<Input name="input1" type="text" onChange={(e) => this.onChange(`${e.target.value}`)}/>
{/* to make it integer you can add unary plus sign like so: */}
{/* this.onChange(+e.target.value) */}

Resources