Concentrati sull’elemento di input appena aggiunto

Ho una nuova app Angular 2 con un elenco di caselle di input. Quando l’utente preme la chiave di ritorno, aggiungo una nuova casella di immissione immediatamente dopo quella che stanno attualmente modificando. O meglio, io (in modo asincrono) aggiungo una nuova voce all’array nel modello, il che fa sì che Angular 2 generi automaticamente una nuova casella di input nel prossimo futuro.

Come posso fare in modo che il focus di input cambi automaticamente nell’elemento appena aggiunto?

[modifica] In alternativa, ottengo un riferimento all’object del modello che sta causando la generazione del DOM. Dal codice componente, c’è un modo per cercare un elemento DOM che rappresenta un particolare object modello?

[modifica] Ecco il mio codice per farlo funzionare. Spero che questo sia abbastanza offensivo per alcuni sviluppatori di Angular 2 per incoraggiare una risposta 🙂

app.WordComponent = ng.core .Component({ selector: 'word-editor', template:'', styles:[ '' ], properties:[ 'list:list', 'word:word' ] }) .Class({ constructor:[ function() { } ], keydown:function(e) { if(e.which == 13) { var ul = e.target.parentNode.parentNode.parentNode; var childCount = ul.childNodes.length; this.list.addWord("").then(function(word) { var interval = setInterval(function() { if(childCount < ul.childNodes.length) { ul.lastChild.querySelector('input').focus(); clearInterval(interval); } }, 1); }); } } }); 

Se sono autorizzato a farlo, prenderò parte della risposta @Sasxa e la modificherò per renderla più simile a ciò che stai cercando.

Alcuni cambiamenti

  • ngFor un ngFor così angular2 aggiunge il nuovo input invece di farlo da solo. Lo scopo principale è solo quello di rendere angular2 per iterare su di esso.
  • Invece di ViewChild userò ViewChildren che restituisce una QueryList che ha una proprietà changes . Questa proprietà è osservabile e restituisce gli elementi dopo che sono cambiati.

Poiché in ES5 non abbiamo decoratori, dobbiamo utilizzare la proprietà delle queries per utilizzare ViewChildren

Componente

 Component({ selector: 'cmp', template : ` 
// We use a variable so we can query the items with it
`, queries : { vc : new ng.core.ViewChildren('input') } })

Concentrati sull’ultimo elemento.

 ngAfterViewInit: function() { this.vc.changes.subscribe(elements => { elements.last.nativeElement.focus(); }); } 

Come ho detto prima, ViewChildren restituisce una QueryList che contiene la proprietà changes . Quando ci iscriviamo ad esso ogni volta che cambia, verrà restituita la lista degli elementi. Gli elements dell’elenco contengono last proprietà (tra le altre) che in questo caso restituisce l’ultimo elemento, usiamo nativeElement su di esso e infine focus()

Aggiungi elementi di input Questo è per pura convenince, l’array di input non ha uno scopo reale più del ridisegno di ngFor .

 add: function(key) { if(key.which == 13) { // See plnkr for "this.inputs" usage this.inputs.push(this.inputs.length+1); } } 

Spingiamo un object fittizio sull’array per ridisegnarlo.

Esempio utilizzando ES5: http://plnkr.co/edit/DvtkfiTjmACVhn5tHGex

Esempio con ES6 / TS: http://plnkr.co/edit/93TgbzfPCTxwvgQru2d0?p=preview

Aggiornamento 29/03/2016

Il tempo è passato, le cose sono state chiarite e ci sono sempre le migliori pratiche per imparare / insegnare. Ho semplificato questa risposta cambiando alcune cose

  • Invece di usare @ViewChildren e sottoscriverlo ho creato una direttiva che verrà instatata ogni volta che viene creato un nuovo input
  • Sto usando Renderer per renderlo sicuro da WebWorker. La risposta originale accede a focus() direttamente su nativeElement che è scoraggiato.
  • Ora ascolto keydown.enter che semplifica l’evento key down, non devo controllare which valore.

Al punto. Il componente sembra (codice semplificato, completo sul plnkrs di seguito)

 @Component({ template: ``, }) add() { this.inputs.push(this.inputs.length+1); } 

E la direttiva

 @Directive({ selector : 'input' }) class MyInput { constructor(public renderer: Renderer, public elementRef: ElementRef) {} ngOnInit() { this.renderer.invokeElementMethod( this.elementRef.nativeElement, 'focus', []); } } 

Come puoi vedere, sto invocando invokeElementMethod per triggersre l’triggerszione dell’elemento anziché accedervi direttamente.

Questa versione è molto più pulita e sicura di quella originale.

plnkrs aggiornati alla versione beta 12

Esempio utilizzando ES5: http://plnkr.co/edit/EpoJvff8KtwXRnXZJ4Rr

Esempio con ES6 / TS: http://plnkr.co/edit/em18uMUxD84Z3CK53RRe

Aggiornamento 2018

invokeElementMethod è deprecato. Usa Renderer2 invece di Renderer.

Assegna un ID al tuo elemento e puoi utilizzare selectRootElement :

 this.renderer2.selectRootElement('#myInput').focus(); 

Dai un’occhiata a ViewChild , ecco un esempio . Questo potrebbe essere quello che stai cercando:

 import {Component, ViewChild} from 'angular2/core' @Component({ selector: 'my-app', providers: [], template: ` 
`, directives: [] }) export class App { @ViewChild('name') vc: ElementRef; ngAfterViewInit() { this.vc.nativeElement.focus(); } }

Con il codice angular in linea, mettere a fuoco dopo la vernice condizionale:

      {{taskEditText.focus()}}  

È ansible implementare una direttiva di testo di input semplice, in modo che ogni volta che viene creato un nuovo input, esso si concentri automaticamente. Il metodo focus() viene chiamato all’interno ngAfterViewInit() ciclo di vita del componente ngAfterViewInit() dopo che la vista è stata completamente inizializzata.

 @Directive({ selector: 'input[type=text]' }) export class FocusInput implements AfterViewInit { private firstTime: bool = true; constructor(public elem: ElementRef) { } ngAfterViewInit() { if (this.firstTime) { this.elem.nativeElement.focus(); this.firstTime = false; } } } 

Usa la direttiva FocusInput nel tuo componente:

 @Component({ selector: 'app', directives: [FocusInput], template: `{{words |json}}` }) export class AppComponent { words: Word[] = []; constructor() { this.addNewWord(); } addNewWord() { this.words.push(new Word()); } } 

Nota quanto segue:

  1. L’evento (keyup.enter) viene utilizzato per rilevare quando viene premuto il tasto < (keyup.enter) >
  2. ngFor viene utilizzato per ripetere l’elemento di input per ogni parola dall’array di words
  3. last è un Boolean associato a una variabile locale che è vera quando l’input è l’ultimo
  4. L’evento keyup è associato all’espressione last ? addNewWord() : 0 last ? addNewWord() : 0 . Ciò garantisce che un nuovo campo di input venga aggiunto solo quando il tasto viene premuto dall’ultimo Input

Demo Plnkr

Per dare la priorità a quale elemento verrà messo a fuoco durante l’inizializzazione di più direttive nello stesso ciclo, utilizzare:

Direttiva:

 @Directive({ selector: '[focusOnInit]' }) export class FocusOnInitDirective implements OnInit, AfterViewInit { @Input('focusOnInit') priority: number = 0; static instances: FocusOnInitDirective[] = []; constructor(public renderer: Renderer, public elementRef: ElementRef) { } ngOnInit(): void { FocusOnInitDirective.instances.push(this) } ngAfterViewInit(): void { setTimeout(() => { FocusOnInitDirective.instances.splice(FocusOnInitDirective.instances.indexOf(this), 1); }); if (FocusOnInitDirective.instances.every((i) => this.priority >= i.priority)) { this.renderer.invokeElementMethod( this.elementRef.nativeElement, 'focus', []); } } } 

Uso:

  

https://plnkr.co/edit/T9VDPIWrVSZ6MpXCdlXF