ngx-intl-tel-input can't able to give custom tabindex - html

I have used ngx-intl-tel-input as a separate component and tried to
give custom tabindex but it's not taking to country box field and
input box field actually how can i make it possible?
<ngx-intl-tel-input *ngIf="!tooltip && type == 'phone' && !showPasswordIcon" [inputId]="fcn ? fcn : 'mobileNumber'" cssClass="form-control input-md" class="phone-box" [enablePlaceholder]="enablePlaceholder" [selectedCountryISO]="CountryISO.Australia" [maxLength]="15" [separateDialCode]="true" [formControl]="form.controls[fcn]" [phoneValidation]="true" container="body" (paste)="onlyNumbers($event)" (drop)="onlyNumbers($event)" triggers="manual" #inputContainer customPlaceholder="Mobile number"> </ngx-intl-tel-input>

You may consider creating a directive that can help you to add tabIndex on desired input element.
#Directive({
selector: '[addTabIndex]',
})
export class AddTabIndexDirective {
#Input() addTabIndex;
#Input() targetElement = 'input';
constructor(private el: ElementRef, private render: Renderer2) {}
ngAfterViewInit() {
const input = this.el.nativeElement.querySelector(this.targetElement);
if (input && this.addTabIndex) {
this.render.setAttribute(input, 'tabindex', this.addTabIndex);
}
}
}
Usage
<ngx-intl-tel-input tabIndex="2"
Stackblitz

Related

Making a date field reactive in Angular

I'm trying to make a datepicker component from ngx-bootstrap a custom date-field, so that I can globalize some functionality and configs. But I can't seem to be able to catch the value of the Date object in the date input field.
My date-field.ts (I'm re-using some setup from a text-field. So bear with me if you see some remnants of the text field component. But I'm sure that my main problem is that my component doesn't know it's a date field)
import { Component, OnInit, forwardRef, Input } from '#angular/core';
import { FormControl, ControlValueAccessor, NG_VALUE_ACCESSOR } from '#angular/forms';
#Component({
selector: 'date-field',
templateUrl: './date-field.component.html',
styleUrls: ['./date-field.component.scss'],
providers:[
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: forwardRef(() => DateFieldComponent),
}
]
})
export class DateFieldComponent implements OnInit, ControlValueAccessor {
public dateField = new FormControl("")
private onChange: (name: string) => void;
private onTouched: () => void
#Input() name: string;
#Input() label: string;
#Input() required: boolean;
datepickerConfig = {
dateInputFormat: 'ddd, MMMM Do YYYY',
isAnimated: true,
adaptivePosition: true,
returnFocusToInput: true,
containerClass: 'theme-dark-blue'
}
constructor() { }
ngOnInit() { }
writeValue(obj: any): void {
const date = Date
this.dateField.setValue(new Date());
console.log(date);
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
if (isDisabled) {
this.dateField.disable();
} else {
this.dateField.enable();
}
}
doInput() {
this.onChange(this.dateField.value)
}
doBlur() {
this.onTouched();
}
}
The template HTML:
<label
*ngIf="label"
for="{{name}}"
class="col-auto col-form-label {{required ? 'required' : '' }}">
{{label}}
</label>
<div class="col-expand relative">
<input
type="text"
class="form-control date-field"
#dp="bsDatepicker"
[formControl]="dateField"
(input)="doInput()"
(blur)="doBlur()"
ngModel
bsDatepicker
[bsConfig]="datepickerConfig"
required="{{required}}">
</div>
Using it in parent forms like this:
<date-field
name="dateChartered"
label="Date local union chartered"
formControlName="dateChartered"
required="true">
</date-field>
<p><strong>Date chartered is:</strong> {{dateChartered}}</p>
Assuming in your parent component you're correctly initializing FormGroup in controller and using it correctly in template, you have two main errors in your component.
First, as Belle Zaid says, you should remove ngModel from you custom datepicker's <input>.
Second, you are binding doInput() to (input), but it will fire only if you type in your input field, same for (change). You should bind to (bsValueChange) that's an output event exposed by BsDatepicker and it's safer, unless you plan to update value on user's input.
The resulting template will look like this:
<label *ngIf="label"
for="my-custom-datepicker"
class="col-auto col-form-label {{required ? 'required' : '' }}">
{{label}}
</label>
<div class="col-expand relative">
<input id="my-custom-datepicker"
type="text"
class="form-control date-field"
required="{{required}}"
bsDatepicker
[formControl]="dateField"
[bsConfig]="datepickerConfig"
(blur)="doBlur()"
(bsValueChange)="doInput()">
</div>
Once done the two changes you will notice an error in your console:
ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was
checked. Previous value for 'ng-pristine': 'true'. Current value: 'false'.. Find more
at https://angular.io/errors/NG0100
This is due to the fact that calling this.dateField.setValue(obj) in writeValue will also trigger (bsValueChange) and, thus, doInput(). To overcome this issue, you can edit your code like the following:
private componentInit = false;
// ...
doInput() {
if (!this.componentInit) {
this.componentInit = true;
return;
}
this.onChange(this.dateField.value);
}

How to handle input events on custom input component in Angular?

I'm trying to create a custom Input component using Reative Form in Angular 7 but i'm having trouble passing events to the input field.
My component has a label and an input field and a specific layout. My component html looks like this:
<div id="customInputGroup" [formGroup]="parentFormGroup" [ngClass]="{'input-value': hasValue()}">
<label [for]="inputId">{{label}}</label>
<input [id]="inputId" [name]="inputName" class="form-control" [placeholder]="placeholder" [formControlName]="inputName" [mask]="mask">
</div>
And in my Component:
#Component({
selector: 'app-input-field',
templateUrl: './input-field.component.html',
styleUrls: ['./input-field.component.css']
})
export class InputFieldComponent implements OnInit {
#Input('placeholder') placeholder = '' ;
#Input('label') label = '';
#Input('inputId') inputId = '';
#Input('inputName') inputName = '';
#Input('parentFormGroup') parentFormGroup:FormGroup;
#Input('mask') mask;
constructor() { }
ngOnInit() {
}
hasValue(){
return (this.parentFormGroup.get(this.inputName).value != undefined
&& this.parentFormGroup.get(this.inputName).value != null
&& this.parentFormGroup.get(this.inputName).value != '')
}
Everything was working fine till i had to handle an input onBlur event(or it could be any other input event).
The result i want is to call my component passing any event like this:
<app-input-field (blur)="functionName()"></app-input-field>
or
<app-input-field (keyup)="functionName()"></app-input-field>
And my component will be able to pass these events to my input field 'dynamically'. Is it possible to do it?
Events like blur works on input field , not selectors like <app-input-field>
You can emit event for all events like blur, keyup, mouseover etc..
InputFieldComponent:
HTML:
<input (blur)="functionName('blur')" (keyup)="functionName('keyUp')" [id]="inputId" [name]="inputName" class="form-control" [placeholder]="placeholder" [formControlName]="inputName" [mask]="mask">
TS:
#Output() OnInputEvent= new EventEmitter();
functionName(eventName) {
this.OnInputEvent.emit(eventName);
}
In your component:
<app-input-field (OnInputEvent)="functionName($event)"></app-input-field>
TS:
functionName(event) {
switch (event) {
case "blur":
...
break;
case "keyUp":
...
break;
}
}
Working Demo
for blur event you need to create a eventemitter with the same name and emit this event on input elemnt blur emit
export class InputFieldComponent implements OnInit {
#Output() blur:EventEmitter<any> = new EventEmitter(); // 👈
constructor() { }
ngOnInit() {
}
}
template
<input type="text" (blur)="blur.emit($event)" >
app.template
<app-input-field (blur)="onBlur($event)" (keyup)="onKeyup($event)"></app-input-field>
we did this for the blur event because the event is not bubble where the keyup will work without create any custom event because it bubble.
demo 🔥🔥
you can implement two way data binding like this
#Input() value:EventEmitter<any> = new EventEmitter();
#Output() valueChange:EventEmitter<any> = new EventEmitter();
template
<app-input-field [(value)]="name" ></app-input-field>
demo 🌟🌟
any bubble event like input,keypress,keyup,keydown you can capture on the element itself or you can work around it like what we did on blur event.

how change style value of previous element

i have elements and when click down with the mouse the color of text changing from black to red
my problem is that i want when i click an another element so this elemnt changing to red and the previous element return to be black
#Directive({
selector: '[appSelect]'
})
export class SelectDirective implements OnInit {
constructor(private elRef: ElementRef, private renderer: Renderer2) {
}
ngOnInit() {
}
#HostListener('mousedown') onmousedown() {
if (this.elRef.nativeElement.style.color !== 'red') {
this.renderer.setStyle(this.elRef.nativeElement, 'color', 'red');
} else {
this.renderer.setStyle(this.elRef.nativeElement, 'color', 'black');
}
Using a directive to change style is using a sledgehammer to crack a nut. But you can do adding a Output to your directive.
In general -without directive- we can do some like
<div *ngFor="let item of [0,1,2,3,4];let i=index"
[style.color]="selectedIndex==i?'red':'black'"
(click)="selectedIndex=i" >
click me
</div>
when we has defined a variable like
selectedIndex:number=-1;
If you want use a directive we can do some similar. We defined a variable like
control:any={}
And we write some like
<div *ngFor="let item of [0,1,2,3,4]"
[appSelect]="control"
(onSelect)="control=$event" >
click me
</div>
Where our directive is
export class SelectDirective implements AfterViewChecked {
#Input('appSelect') control;
#Output() onSelect:EventEmitter<any> = new EventEmitter<any>();
constructor(private elRef: ElementRef, private renderer: Renderer2) {
}
ngAfterViewChecked() {
if (this.elRef.nativeElement==this.control)
this.renderer.setStyle(this.elRef.nativeElement, 'color', 'red');
else
this.renderer.setStyle(this.elRef.nativeElement, 'color', 'black');
}
#HostListener('mousedown') onmousedown() {
this.onSelect.emit(this.elRef.nativeElement);
}
}
See that we change the color in the event AfterViewChecked. You can see the result of both solutions in the stackblitz

How to make tab key as enter key in Angular Material?

this is my angular materiel auto complete code
<input type="search" id="setId" name="setId" [attr.list]='collectionType' [(ngModel)]="selValue" class="text-box"
placeholder="--Select--" (focus)="ValidateParent()" (keyup.tab)="test()" (keyup)="EmitValues($event)" [id]="setId"
[matAutocomplete]="auto" [title]="selValue" [placeholder]='WaterMarkText'>
<div [hidden]="IsCascading">
<mat-autocomplete [id]="collectionType" #auto="matAutocomplete" (optionSelected)='onChange($event)'>
<mat-option *ngFor="let items of codeList" [value]="items.text" [attr.data-text]='items.text' [id]="items.value">
{{items.text}}
</mat-option>
</mat-autocomplete>
</div>
Angular material had a problem with tab selection.like the materiel auto complete not able to select the value while click the tab button. but it's working while click the enter button. So manually I need to overwrite the enter key event on tab key event. How could possible?
Improve my comment, and based on the response we can create a directive
import {
Directive,
AfterViewInit,
OnDestroy,
Optional
} from '#angular/core';
import {
MatAutocompleteTrigger
} from '#angular/material';
#Directive({
selector: '[tab-directive]'
})
export class TabDirective implements AfterViewInit, OnDestroy {
observable: any;
constructor(#Optional() private autoTrigger: MatAutocompleteTrigger) {}
ngAfterViewInit() {
this.observable = this.autoTrigger.panelClosingActions.subscribe(x => {
if (this.autoTrigger.activeOption) {
this.autoTrigger.writeValue(this.autoTrigger.activeOption.value)
}
})
}
ngOnDestroy() {
this.observable.unsubscribe();
}
}
You use:
<input tab-directive type="text" matInput [formControl]="myControl"
[matAutocomplete]="auto" >
(see stackblitz)
Update We can control only tab.key, else always you close, you get the selected value, so
#Directive({
selector: '[tab-directive]'
})
export class TabDirective {
observable: any;
constructor(#Optional() private autoTrigger: MatAutocompleteTrigger) {}
#HostListener('keydown.tab', ['$event.target']) onBlur() {
if (this.autoTrigger.activeOption) {
this.autoTrigger.writeValue(this.autoTrigger.activeOption.value)
}
}
}
(see a new stackblitz)
Update 2 I don't believe this answer has so many upvotes because it's wrong. As #Andrew allen comments, the directive not update the control. Well, It's late, but I try to solve. One Option is use
this.autoTrigger._onChange(this.autoTrigger.activeOption.value)
Anohter idea is inject the ngControl, so
constructor(#Optional() private autoTrigger: MatAutocompleteTrigger,
#Optional() private control: NgControl) {}
ngAfterViewInit() {
this.observable = this.autoTrigger.panelClosingActions.subscribe(x => {
if (this.autoTrigger.activeOption) {
const value = this.autoTrigger.activeOption.value;
if (this.control)
this.control.control.setValue(value, {
emit: false
});
this.autoTrigger.writeValue(value);
}
})
}
I'm a little late to the party; so there's one annoying issue with Eliseo's answer: It autocompletes even when the user has already selected a different option from the panel with a mouse click. The workaround I finally came up with was not straight forward at all.
/**
* Selects currently active item on TAB
* #example
* <input vxTabActiveOption [matAutocomplete]="matAutocomplate">
* <mat-autocomplete #matAutocomplate="matAutocomplete" autoActiveFirstOption>
*/
#Directive({ selector: '[vxTabActiveOption]' })
export class TabActiveOptionDirective implements AfterViewInit, OnDestroy {
private readonly destroy$ = new Subject<void>();
/**
* Whether or not the autocomplete panel was open before the event
*/
private panelOpen = false;
constructor(
private autoTrigger: MatAutocompleteTrigger,
private control: NgControl
) { }
ngAfterViewInit() {
const autocomplete = this.autoTrigger.autocomplete;
merge(
autocomplete.opened.pipe(map(() => true)),
autocomplete.closed.pipe(map(() => false))
).pipe(
takeUntil(this.destroy$),
delay(0)
).subscribe(value => this.panelOpen = value);
}
#HostListener('keydown.tab')
onBlur() {
if (this.panelOpen && this.autoTrigger.activeOption) {
const value = this.autoTrigger.activeOption.value;
this.control.control.setValue(value, { emit: false });
this.autoTrigger.writeValue(value);
}
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}
Yes, I know it's an old question, but there're a better aproach that is use a directive like
#Directive({ selector: '[tab-selected]' })
export class TabSelected implements AfterViewInit {
constructor(private auto: MatAutocomplete) {}
ngAfterViewInit() {
this.auto._keyManager.onKeydown = (event: KeyboardEvent) => {
switch (event.keyCode) {
case TAB:
if (this.auto.isOpen) {
const option = this.auto.options.find(x => x.active);
if (option) {
option.select();
event.preventDefault();
return;
}
}
this.auto._keyManager.tabOut.next();
break;
case DOWN_ARROW:
this.auto._keyManager.setNextItemActive();
break;
case UP_ARROW:
this.auto._keyManager.setPreviousItemActive();
break;
}
};
}
}
You use like
<mat-form-field class="example-full-width" appearance="fill">
<mat-label>Number</mat-label>
<input type="text"
placeholder="Pick one"
aria-label="Number"
matInput
[formControl]="myControl"
[matAutocomplete]="auto">
<mat-autocomplete tab-selected #auto="matAutocomplete"
autoActiveFirstOption >
<mat-option *ngFor="let option of filteredOptions | async" [value]="option">
{{option}}
</mat-option>
</mat-autocomplete>
</mat-form-field>
You can be on work in this stackblitz

focus for ng-select not working Angular 5

i'm trying to use focus for ng-select in angular 5 but its not working
i'm working with ng2-select
How to use focus for ng-select in Angular 5 ?
<label class="input">
<input (blur)="goTo()" type="text">
</label>
<label class="select" >
<div *ngIf="items.length > 0" >
<ng-select #select [items]="items">
</ng-select>
</div>
</label>
#ViewChild('select') myElement: ElementRef;
goTo(){
this.myElement.element.nativeElement.focus();
}
this works fine for me,
#ViewChild('ngSelect') ngSelect: NgSelectComponent;
ngAfterViewInit() {
if (this.autofocus) {
setTimeout(() => {
this.ngSelect.focus();
});
}
}
refer https://github.com/ng-select/ng-select/issues/762
Change this
goTo(){
this.myElement.element.nativeElement.focus();
}
to this,
import { ChangeDetectorRef } from '#angular/core';
constructor (private cRef: ChangeDetectorRef) {}
// import 'SelectComponent' from your library for 'ng2-select'
goTo(){
let el = null;
if (this.items.length > 0) {
this.cRef.detectChanges();
el = (this.myElement.nativeElement as SelectComponent).element.nativeElement.querySelector('div.ui-select-container > input');
if (el) { el.focus(); }
}
}
You may have to check if the element is defined or not (or if you need an extra 'nativeElement' in there, but I'm basically reusing the logic in here.
Just a cautionary note, this may not be a stable fix though if the library updates to rename these classes or modify the template.
Hope it helps.
A quick reply, in this solution,each select elements are recorded for future usage (and in this case , blur). This needs being adapted to your situation.
import { Component, OnInit } from '#angular/core';
import { ViewChildren, QueryList } from '#angular/core';
#Component({
selector: 'app-editor',
templateUrl: './editor.component.html',
styleUrls: ['./editor.component.css']
})
export class EditorComponent implements AfterViewInit {
// Viewchildren
// https://netbasal.com/understanding-viewchildren-contentchildren-and-querylist-in-angular-896b0c689f6e
myindex:number;
#ViewChildren("select") inputs: QueryList<any>
constructor() {
this.myindex=0
}
// You will have to setup some input parameters in this function in order to switch to the right "select", (you have control)
private goTo()
{
// focus next input field
if (this.myindex +1 < this.inputs.toArray().length)
{
this.inputs.toArray()[this.myindex +1].nativeElement.focus();
this.myindex = this.myindex + 1;
}
else {
this.inputs.toArray()[0].nativeElement.focus();
this.myindex = 0;
}
}
private processChildren(): void {
console.log('Processing children. Their count:', this.inputs.toArray().length)
}
ngAfterViewInit() {
console.log("AfterViewInit");
console.log(this.inputs);
this.inputs.forEach(input => console.log(input));
// susbcribe to inputs changes, each time an element is added (very optional feature ...)
this.inputs.changes.subscribe(_ =>
this.processChildren() // subsequent calls to processChildren
);
}
}
A Simple way, you can do this also.
<label class="input">
<input (blur)="goTo(select)" type="text">
</label>
<label class="select">
<div *ngIf="items.length > 0">
<ng-select #select [items]="items">
</ng-select>
</div>
</label>
And in Typescript File.
goTo(select){
select.focus();
}