I have a list of ElementRef for all my inputs but when I try to add listeners to them it makes like textInputs
is empty but it's not.
@ViewChildren('text_input') textInputs!: QueryList<ElementRef>;
ngAfterViewInit(): void {
this.initTextInputsListeners();
}
private initTextInputsListeners() {
this.textInputs.forEach(input => {
const inputElement = input.nativeElement;
const parentNode = inputElement.parentNode;
inputElement.addEventListener('focus', () => {
parentNode.classList.add('user-input-active')
});
inputElement.addEventListener('focusout', () => {
if (inputElement.value === '') parentNode.classList.remove('user-input-active');
});
});
}
<div class="user-input send-message" [formGroup]="userInputForm" (ngSubmit)="onSendMessage()">
<div class="user-input-box">
<label for="message">Send message</label>
<input id="message" class="form-control" type="text" name="message" #text_input formControlName="message" (keyup.enter)="onSendMessage()">
<button class="submit-btn" type="submit" value="" (click)="onSendMessage()">
<svg width="32" height="32" viewBox="0 0 24 24">
<path
d="m3.4 20.4l17.45-7.48a1 1 0 0 0 0-1.84L3.4 3.6a.993.993 0 0 0-1.39.91L2 9.12c0 .5.37.93.87.99L17 12L2.87 13.88c-.5.07-.87.5-.87 1l.01 4.61c0 .71.73 1.2 1.39.91z"/>
</svg>
</button>
</div>
</div>
your code looks like work. So the problem is that when you execute the initTextInputsListeners
textInputs have not all the inputs
So To be sure, you can subscribe to this.textInputs.changes
ngAfterViewInit(): void {
this.textInputs.changes.pipe(startWith(null)).subscribe((_) => {
this.initTextInputsListeners();
});
}
There're another approach to achieve you want that it's use directives
You can use a directive applied to your inputs like
@Directive({
selector: '[specialfocus]'
})
export class AddClassParentDirective {
@Input('specialfocus') class="user-input-active"
@HostListener('focus') addClass(){
this.el.nativeElement.parentNode.classList.add(this.class)
}
@HostListener('blur') removeClass(){
this.el.nativeElement.parentNode.classList.remove(this.class)
}
constructor(private el:ElementRef) { }
}
And use as
<div class="user-input-box">
<label for="message">Send message</label>
<input [specialfocus] class="form-control" type="text" />
</div>
Or use a directive applied to the div (see that in this case I use as selector '.user-input-box2'
, so each div that has a class "user-input-box2" is really a UserInputBoxDirective
@Directive({
selector: '.user-input-box2'
})
export class UserInputBoxDirective implements AfterViewInit,OnDestroy {
focus:boolean=false;
subscription:any=null
@HostBinding('class.user-input-active') get _(){
return this.focus?true:null
}
@ContentChild(HTMLInputElement) input:HTMLInputElement
constructor(private el:ElementRef) { }
ngAfterViewInit()
{
const inputs=this.el.nativeElement.getElementsByTagName('input')
if (inputs && inputs.length)
this.subscription=merge(fromEvent(inputs[0],'focus').pipe(map(_=>true)),
fromEvent(inputs[0],'blur').pipe(map(_=>false)))
.subscribe(res=>{
this.focus=res
})
}
ngOnDestroy()
{
this.subscription && this.subscription.unsubscribe
}
}
You use like
<div class="user-input-box2">
<label for="message">Send message</label>
<input class="form-control" type="text" />
</div>
You has the three approach (your's and this about directives) in this stackblitz