How to set focus on element with binding?

2020-02-05 03:42发布

问题:

In Angular2 how can I set binding on element focus. I don't want to set it with elementRef. I think in AngularJS there is ngFocus directive In Angular2 there is no such directive

回答1:

Renderer service is now deprecated (as of Angular 4.x) The new Renderer2 service doesn't have the invokeElementMethod. What you can do is to get a reference to the element like this:

const element = this.renderer.selectRootElement('#elementId');

And then you can use that to focus on that element like so:

element.focus();

More on how selectRootElement works here:

EDIT:

If the element doesn't get focused the common issue is that the element is not ready. (eg.: disabled, hidden etc.). You can do this:

setTimeout(() => element.focus(), 0);

This will create a macrotask that will run in the next VM turn, so if you enabled the element the focus will run properly.



回答2:

A simple 'focus' Directive

import {Directive, Input, ElementRef} from 'angular2/core';
@Directive({
    selector: '[focus]'
})
class FocusDirective {
    @Input()
    focus:boolean;
    constructor(@Inject(ElementRef) private element: ElementRef) {}
    protected ngOnChanges() {
        this.element.nativeElement.focus();
    }
}

// Usage
@Component({
    selector : 'app',
    template : `
        <input [focus]="inputFocused" type="text">
        <button (click)="moveFocus()">Move Focus</button>
    `,
    directives: [FocusDirective]
})
export class App {
    private inputFocused = false;
    moveFocus() {
        this.inputFocused = true;
        // we need this because nothing will 
        // happens on next method call, 
        // ngOnChanges in directive is only called if value is changed,
        // so we have to reset this value in async way,
        // this is ugly but works
        setTimeout(() => {this.inputFocused = false});
    }
}


回答3:

I tried both variants but none is suitable for simple use. 1st (by @AngJobs) needs additional work in component where you use directive (to set focus=true), 2nd (by @ShellZero) not working at all because focus called before the view is actually ready. So I moved focus call to ngAfterViewInit. Now you can just add <input focus... and forget it. Element will get focus after view init automatically.

import { Directive, ElementRef, Renderer, AfterViewInit } from '@angular/core';
@Directive({
    selector: '[focus]'
})

export class DmFocusDirective implements AfterViewInit {
    constructor(private _el: ElementRef, private renderer: Renderer) {
    }

    ngAfterViewInit() {
        this.renderer.invokeElementMethod(this._el.nativeElement, 'focus');
    }
}


回答4:

The best way of setting focus on an element with angular2 is by using the Renderer and invoke an method on the element. There is no way of doing this without elementRef.

This results in something like this:

this.renderer.invokeElementMethod(this.input.nativeElement, 'focus', []);

Where renderer gets injected in the constructor using protected renderer : Renderer



回答5:

Much more simpler way:

import { Directive, ElementRef, Renderer} from "@angular/core";
@Directive({
    selector: "[Focus]"
})

export class myFocus {
    constructor(private _el: ElementRef, private renderer: Renderer) {
        this.renderer.invokeElementMethod(this._el.nativeElement, 'focus');
    }

}


回答6:

It works for me but have errors in console. After debugging and searching found this article: https://www.picnet.com.au/blogs/guido/post/2016/09/20/angular2-ng2-focus-directive/

Just copy-paste. It worked perfectly for me.

import {Directive, AfterViewInit, ElementRef, DoCheck} from '@angular/core';

@Directive({selector: '[focus]'})
export class FocusDirective implements AfterViewInit, DoCheck {
    private lastVisible: boolean = false;
    private initialised: boolean = false;

    constructor(private el: ElementRef) {
    }

    ngAfterViewInit() {
        console.log('inside FocusDirective.ngAfterViewInit()');
        this.initialised = true;
        this.ngDoCheck();
    }

    ngDoCheck() {

        console.log('inside FocusDirective.ngDoCheck()');

        if (!this.initialised) {
            return;
        }
        const visible = !!this.el.nativeElement.offsetParent;
        if (visible && !this.lastVisible) {
            setTimeout(() => {
                this.el.nativeElement.focus();
            }, 1);
        }
        this.lastVisible = visible;
    }
}


回答7:

From @MrBlaise I took the setTimeout snippet which made the following work for me.

<input type="text" #searchInput />



import { ElementRef, ViewChild } from '@angular/core';

...

@ViewChild('searchInput') private searchInput: ElementRef;

...

setTimeout(() => this.searchInput.nativeElement.focus(), 0);


回答8:

import { Directive, ElementRef, AfterViewChecked } from '@angular/core';

@Directive({
 selector: '[autoFocus]',
})
export class FocusDirective implements AfterViewChecked {

  constructor(private _elementRef: ElementRef) { }

  ngAfterViewChecked() {
    this._elementRef.nativeElement.focus()
  }

}


回答9:

The trick is to use both focus and select together:

this.(element).nativeElement.focus();
this.(element).nativeElement.select();


标签: angular