2016-07-11 20 views

risposta

9

Un semplice 'focus' direttiva

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}); 
    } 
} 
+1

Un piccolo heads-up. Non utilizzare direttamente 'nativeElement'. Questo non è risparmiato con i webworker. – PierreDuc

+1

La proprietà 'direttivo' è stata rimossa in RC 6. È necessario spostare' FocusDirective' nella proprietà dichiarazioni del decoratore NgModule. – cnotethegr8

+0

Ho lo stesso requisito ma, voglio spostare il setTimeout all'interno della direttiva, si può ottenere la stessa funzionalità usando setTimeout all'interno della direttiva? –

2

Il miglior modo di messa a fuoco su un elemento con angular2 è quello di utilizzare il Renderer e invocare un metodo sull'elemento. Non c'è modo di farlo senza elementRef.

Questo si traduce in qualcosa di simile:

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

Dove renderer ottiene iniettato nel costruttore utilizzando protected renderer : Renderer

+0

Penso che un approccio migliore sia scrivere la mia direttiva ngFocus che manipola l'elemento e non lo fa nella classe componente. – almog

+0

Questo è stato gentile implicito .. e il codice rimane un po 'lo stesso – PierreDuc

+0

Ma perché il team Angular2 non l'ha creato? – almog

1

Molto più modo più semplice:

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'); 
    } 

} 
+0

Non funziona per me perché il focus ha chiamato prima che la vista sia effettivamente pronta – Dimanoid

+0

Quindi devi aggiungerla in ngOnInit() [Angular LifeCycle Hooks] o devi iscriverti a qualsiasi evento tu abbia in modo che "ti focalizzi ogni volta che quell'evento è successo". – ShellZero

8

Ho provato entrambe le varianti, ma nessuna è adatta per un uso semplice. 1st (by @AngJobs) ha bisogno di ulteriore lavoro nel componente in cui usi direttiva (per impostare focus = true), 2nd (by @ShellZero) non funziona affatto perché il focus chiama prima che la vista sia effettivamente pronta. Così ho spostato la chiamata di messa a fuoco su ngAfterViewInit. Ora puoi solo aggiungere <input focus... e dimenticarlo. L'elemento otterrà la messa a fuoco dopo l'avvio della visualizzazione automaticamente.

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'); 
    } 
} 
+0

Grazie - questo è fantastico. Anche se il nome della tua classe non ha necessariamente bisogno di prefisso (come hai fatto con 'Dm'). Il bit chiave per applicare un prefisso è il valore del nome del selettore. cioè selettore: '[appFocus]' '. Immagino che questo riduca la probabilità di collisioni nelle successive versioni angolari, ecc. – ne1410s

+1

Avviso: 'Renderer' è deprecato, e il team Angular non prevede di portare' invokeElementMethod() 'forward in' Renderer2'. https://github.com/angular/angular/issues/13818 – stealththeninja

1

Funziona per me ma ha errori nella console. Dopo il debug e la ricerca trovato questo articolo: https://www.picnet.com.au/blogs/guido/post/2016/09/20/angular2-ng2-focus-directive/

Basta copia-incolla. Ha funzionato perfettamente per 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

servizio renderer è ora deprecated (come di 4.x angolare) Il nuovo servizio Renderer2 non ha l'invokeElementMethod. Cosa si può fare è quello di ottenere un riferimento all'elemento in questo modo:

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

E poi si può usare quello per concentrarsi su questo elemento in questo modo:

element.focus(); 

Di più su come selectRootElement funziona here :

EDIT:

Se l'elemento non viene messo a fuoco il problema comune è che l'elemento non è pronto. (es .: disabilitato, nascosto ecc.). Si può fare questo:

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

Questo creerà un macrotask che verrà eseguito nel prossimo VM turno, quindi se è stata attivata l'elemento il fuoco verrà eseguita correttamente.

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

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

    constructor(private _elementRef: ElementRef) { } 

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

} 
Problemi correlati