javascript - example - angularjs queryselector
El campo de entrada dinĂ¡mico Angular2 pierde el foco cuando cambia la entrada (2)
Estoy haciendo una forma dinámica. Un Field
tiene una lista de valores. Cada valor está representado por una cadena.
export class Field{
name: string;
values: string[] = [];
fieldType: string;
constructor(fieldType: string) {this.fieldType = fieldType;}
}
Tengo una función en mi componente que agrega un nuevo valor al campo.
addValue(field){
field.values.push("");
}
Los valores y el botón se muestran así en mi HTML.
<div id="dropdown-values" *ngFor="let value of field.values; let j=index">
<input type="text" class="form-control" [(ngModel)]="field.values[j]" [name]="''value'' + j + ''.'' + i"/><br/>
</div>
<div class="text-center">
<a href="javascript:void(0);" (click)="addValue(field)"><i class="fa fa-plus-circle" aria-hidden="true"></i></a>
</div>
Tan pronto como escribo un texto en una entrada de un valor, la entrada pierde el foco. Si agrego muchos valores a un campo y escribo un carácter en uno de los valores ingresados, la entrada pierde el enfoque y el carácter se escribe en cada entrada.
Esto me estaba sucediendo cuando estaba iterando sobre las claves y valores de un objeto usando una función auxiliar:
<div *ngFor="let thing of getThings()" [attr.thingname]="thing.key">
... {{ applyThing(thing.value) }}
</div>
En mi componente estaba devolviendo una matriz de objetos que contenían pares clave / valor:
export ThingComponent {
...
//this.things = { a: { ... }, b: { ... }, c: { ... } }
public getThings() {
return Object.keys(this.things).map((key) => {
return {key: key, value: this.things[key] }
})
}
}
La respuesta dada por @ AJT_82 definitivamente funciona exactamente como se anuncia. Sin embargo, en mi caso, el problema específico era que la función auxiliar, getThings()
, devolvía una nueva lista de objetos cada vez. A pesar de que su contenido era el mismo, los objetos mismos se regeneraron en cada llamada a la función (lo que estaba ocurriendo durante la detección de cambios) y, por lo tanto, al detector de cambios, tenían identidades diferentes y la forma se regeneraba en cada cambio de modelo.
La solución simple en mi caso fue cachear el resultado de getThings()
y usarlo como iterador:
<div *ngFor="let thing of cachedThings" [attr.thingname]="thing.key">
... {{ applyThing(thing.value) }}
</div>
...
export ThingComponent {
public cachedThings = getThings()
...
//this.things = { a: { ... }, b: { ... }, c: { ... } }
private getThings() {
return Object.keys(this.things).map((key) => {
return {key: key, value: this.things[key] }
})
}
}
En los casos en los que las cachedThings
en la cachedThings
deban variar, deberá actualizarse manualmente para que el detector de cambios active la representación.
Esto sucede cuando la matriz es un tipo primitivo, en su caso una matriz de String
. Esto se puede resolver utilizando TrackBy
. Así que cambia tu plantilla para que coincida con lo siguiente:
<div *ngFor="let value of field.values; let i=index; trackBy:trackByFn">
<input type="text" [(ngModel)]="field.values[i]" /><br/>
</div>
<div>
<button (click)="addValue(field)">Click</button>
</div>
y en el archivo ts agregue la función trackByFn
, que devuelve el index
(exclusivo) del valor:
trackByFn(index: any, item: any) {
return index;
}
Este es un link sobre el mismo problema, excepto que el problema es para AngularJS, pero el problema corresponde al suyo. Extracto más importante de esa página:
Está repitiendo sobre una matriz y está cambiando los elementos de la matriz (tenga en cuenta que sus elementos son cadenas, que son primitivas en JS y, por lo tanto, se comparan "por valor"). Dado que se detectan nuevos elementos, los elementos antiguos se eliminan del DOM y se crean los nuevos (que obviamente no se enfocan).
Con TrackBy
Angular puede rastrear qué elementos se han agregado (o eliminado) de acuerdo con el identificador único y crear o destruir solo las cosas que cambiaron, lo que significa que no pierde el enfoque en su campo de entrada :)
Como se ve en el enlace, también puede modificar su matriz para que contenga objetos que sean únicos y use [(ngModel)]="value.id"
por ejemplo, pero tal vez eso no sea lo que necesita.