change - page title angular 4
Centrarse en el elemento de entrada reciƩn agregado (5)
Tengo una nueva aplicación Angular 2 con una lista de cuadros de
input
.
Cuando el usuario presiona la tecla de retorno, agrego un nuevo cuadro de
input
inmediatamente después del que está editando actualmente.
O, mejor dicho, agrego (asincrónicamente) una nueva entrada a la matriz en el modelo, lo que hace que Angular 2 genere automáticamente un nuevo cuadro de
input
en un futuro próximo.
¿Cómo puedo hacer que
input
foco de
input
cambie automáticamente al elemento recién agregado?
EDITAR 1:
Alternativamente, obtengo una referencia al objeto modelo que está causando que se genere el DOM.
Desde el código del componente, ¿hay alguna forma de buscar un elemento DOM que represente un objeto modelo en particular?
EDITAR 2:
Aquí está mi código para que esto funcione.
Esperemos que esto sea lo suficientemente ofensivo para algunos desarrolladores de Angular 2 para alentar una respuesta :-)
app.WordComponent = ng.core
.Component({
selector: ''word-editor'',
template:''<input type="text" [value]="word.word" (input)="word.update($event.target.value)" (keydown)="keydown($event)"/>'',
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);
});
}
}
});
Con código angular en línea, enfoque después de la pintura condicional:
<span *ngIf="editId==item.id">
<input #taskEditText type="text" [(ngModel)]="editTask" (keydown.enter)="save()" (keydown.esc)="saveCancel()"/>
<button (click)="save()">Save</button>
<button (click)="saveCancel()">Cancel</button>
{{taskEditText.focus()}}
</span>
Eche un vistazo a ViewChild , aquí hay un example . Esto podría ser lo que estás buscando:
import {Component, ViewChild} from ''angular2/core''
@Component({
selector: ''my-app'',
providers: [],
template: `
<div>
<input #name>
</div>
`,
directives: []
})
export class App {
@ViewChild(''name'') vc: ElementRef;
ngAfterViewInit() {
this.vc.nativeElement.focus();
}
}
Para priorizar qué elemento se enfocará al inicializar varias directivas en el mismo ciclo, use:
Directiva:
@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:
<input type="text" focusOnInit="9">
Puede implementar una directiva de texto de entrada simple, para que cada vez que se cree una nueva entrada, se enfoque automáticamente.
El método
focus()
se llama dentro del gancho del ciclo de vida del componente
ngAfterViewInit()
después de que la vista se haya inicializado completamente.
@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;
}
}
}
Use la directiva
FocusInput
en su componente:
@Component({
selector: ''app'',
directives: [FocusInput],
template: `<input type="text" (keyup.enter)="last ? addNewWord():0"
*ngFor="#word of words; #last = last" [value]="word.word"
#txt (input)="word.word = txt.value" />{{words |json}}`
})
export class AppComponent {
words: Word[] = [];
constructor() {
this.addNewWord();
}
addNewWord() {
this.words.push(new Word());
}
}
Tenga en cuenta lo siguiente:
-
El evento
(keyup.enter)
se usa para detectar cuándo se presiona la tecla <enter> -
ngFor
se usa para repetir el elemento de entrada para cada palabra del conjunto dewords
-
last
es un booleano vinculado a una variable local que es verdadera cuando la entrada es la última -
¿El evento keyup está vinculado a la
last ? addNewWord() : 0
expresiónlast ? addNewWord() : 0
last ? addNewWord() : 0
. Esto garantiza que solo se agregue un nuevo campo de entrada cuando se presiona la tecla <enter> desde la última entrada
Si se me permite hacerlo, tomaré parte de la respuesta de @Sasxa y la modificaré para que se parezca más a lo que estás buscando.
Algunos cambios
-
ngFor
unngFor
para que angular2 agregue la nueva entrada en lugar de hacerlo yo mismo. El objetivo principal es simplemente hacer angular2 para iterar sobre él. -
En lugar de
ViewChild
, voy a usarViewChildren
que devuelve una QueryList que tiene una propiedad dechanges
. Esta propiedad es un Observable y devuelve los elementos una vez que han cambiado.
Como en ES5 no tenemos decoradores, tenemos que usar la propiedad de
queries
para usar
ViewChildren
Componente
Component({
selector: ''cmp'',
template : `
<div>
// We use a variable so we can query the items with it
<input #input (keydown)="add($event)" *ngFor="#input of inputs">
</div>
`,
queries : {
vc : new ng.core.ViewChildren(''input'')
}
})
Centrarse en el último elemento.
ngAfterViewInit: function() {
this.vc.changes.subscribe(elements => {
elements.last.nativeElement.focus();
});
}
Como dije antes, ViewChildren devuelve una QueryList que contiene propiedades de
changes
.
Cuando nos suscribimos cada vez que cambia, devolverá la lista de elementos.
Los
elements
lista contienen una
last
propiedad (entre otras) que en este caso devuelve el último elemento, usamos
nativeElement
en él y finalmente
focus()
Agregar elementos de entrada
Esto es por pura conveniencia, la matriz de entradas no tiene un propósito real más que volver a dibujar el
ngFor
.
add: function(key) {
if(key.which == 13) {
// See plnkr for "this.inputs" usage
this.inputs.push(this.inputs.length+1);
}
}
Empujamos un elemento ficticio en la matriz para que se vuelva a dibujar.
Ejemplo usando ES5: http://plnkr.co/edit/DvtkfiTjmACVhn5tHGex
Ejemplo usando ES6 / TS: http://plnkr.co/edit/93TgbzfPCTxwvgQru2d0?p=preview
Actualización 29/03/2016
El tiempo ha pasado, las cosas se han aclarado y siempre hay mejores prácticas para aprender / enseñar. He simplificado esta respuesta cambiando algunas cosas
-
En lugar de usar
@ViewChildren
y suscribirme, hice una directiva que se instaurará cada vez que se cree una nueva entrada -
Estoy usando
Renderer
para que sea seguro para WebWorker. La respuesta original accede afocus()
directamente ennativeElement
que se desaconseja. -
Ahora escucho
keydown.enter
que simplifica el evento key down, no tengo que verificarwhich
valor.
Al punto. El componente se ve así (código completo simplificado en los plnkrs a continuación)
@Component({
template: `<input (keydown.enter)="add()" *ngFor="#input of inputs">`,
})
add() {
this.inputs.push(this.inputs.length+1);
}
Y la directiva
@Directive({
selector : ''input''
})
class MyInput {
constructor(public renderer: Renderer, public elementRef: ElementRef) {}
ngOnInit() {
this.renderer.invokeElementMethod(
this.elementRef.nativeElement, ''focus'', []);
}
}
Como puede ver, estoy llamando a
invokeElementMethod
para activar el
focus
en el elemento en lugar de acceder a él directamente.
Esta versión es mucho más limpia y segura que la original.
plnkrs actualizados a beta 12
Ejemplo usando ES5: http://plnkr.co/edit/EpoJvff8KtwXRnXZJ4Rr
Ejemplo usando ES6 / TS: http://plnkr.co/edit/em18uMUxD84Z3CK53RRe
Actualización 2018
invokeElementMethod
está en desuso.
Use Renderer2 en lugar de Renderer.
Dele a su elemento una identificación, y puede usar selectRootElement :
this.renderer2.selectRootElement(''#myInput'').focus();