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 [formGroup]="userInputForm" (ngSubmit)="onSendMessage()">
<div >
<label for="message">Send message</label>
<input id="message" type="text" name="message" #text_input formControlName="message" (keyup.enter)="onSendMessage()">
<button 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>
CodePudding user response:
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')
@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 >
<label for="message">Send message</label>
<input [specialfocus] 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 >
<label for="message">Send message</label>
<input type="text" />
</div>
You has the three approach (your's and this about directives) in this stackblitz
CodePudding user response:
Easiest solution would be to not use JS for this at all. This is standard CSS stuff that has fairly good support in modern browsers:
Use focus-within to determine if there is a focus active in a container.
Second way to do this with CSS is to use the :has() css pesude-class, but this has poorer css support. If you only want to activate this rule if the input is selected, you can modify the HTML markup so the focus-within only triggers if the input is in focus.
In the example below I color the text pink and fill the SVG with blue if focus is inside the .user-input-box
element
.user-input-box:focus-within {
color: pink
}
.user-input-box:focus-within svg {
fill: blue
}
<div >
<label for="message">Send message</label>
<input id="message" type="text" name="message" #text_input formControlName="message">
<button type="submit" value="">
<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>