web-dev-qa-db-de.com

Angular2 Dynamisches Eingabefeld verliert den Fokus, wenn sich die Eingabe ändert

Ich mache eine dynamische Form. Ein Field verfügt über eine Liste von Werten. Jeder Wert wird durch eine Zeichenfolge dargestellt.

export class Field{
    name: string;
    values: string[] = [];
    fieldType: string;
    constructor(fieldType: string) {this.fieldType = fieldType;}
}

Ich habe eine Funktion in meiner Komponente, die dem Feld einen neuen Wert hinzufügt.

addValue(field){
    field.values.Push("");
}

Die Werte und die Schaltfläche werden in meinem HTML-Code so angezeigt.

<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>

Sobald ich etwas Text in eine Eingabe eines Wertes schreibe, verliert die Eingabe den Fokus . Wenn ich einem Feld viele Werte hinzufüge und ein Zeichen in eine der Werteingaben schreibe, verliert die Eingabe den Fokus und das Zeichen wird geschrieben in jeder Eingabe. 

28
Servietsky

Dies geschieht, wenn das Array ein primitiver Typ ist, in Ihrem Fall ein String-Array. Dies kann durch Verwendung von TrackBy gelöst werden. Ändern Sie also Ihre Vorlage, um Folgendes zu erreichen:

<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>

und fügen Sie in der Datei ts die Funktion trackByFn hinzu, die die (eindeutige) index des Werts zurückgibt:

trackByFn(index: any, item: any) {
   return index;
}

Dies ist ein link über dasselbe Problem, außer dass das Problem für AngularJS ist, aber das Problem entspricht Ihrem. Wichtigster Auszug aus dieser Seite:

Sie wiederholen sich über ein Array und ändern die Elemente des Arrays (beachten Sie, dass Ihre Elemente Strings sind, die in JS Grundelemente sind und daher "nach Wert" verglichen werden). Da neue Elemente erkannt werden, werden alte Elemente aus dem DOM entfernt und neue erstellt (die offensichtlich keinen Fokus erhalten).

Mit TrackBy kann Angular nachverfolgen, welche Elemente gemäß der eindeutigen Kennung hinzugefügt (oder entfernt) wurden, und nur die Dinge erstellen oder löschen, die sich geändert haben. Dadurch verlieren Sie den Fokus auf Ihr Eingabefeld.

Wie aus dem Link ersichtlich, können Sie Ihr Array auch so ändern, dass es Objekte enthält, die eindeutig sind und beispielsweise [(ngModel)]="value.id" verwenden. Dies ist jedoch möglicherweise nicht das, was Sie brauchen.

75
AJT_82

Dies geschah bei mir, als ich mit Hilfe einer Hilfsfunktion über die Schlüssel und Werte eines Objekts iterierte:

<div *ngFor="let thing of getThings()" [attr.thingname]="thing.key">
  ... {{ applyThing(thing.value) }}
</div>

In meiner Komponente habe ich ein Array von Objekten zurückgegeben, die Schlüssel/Wert-Paare enthalten:

export ThingComponent {
  ...

  //this.things = { a: { ... }, b: { ... }, c: { ... } }

  public getThings() {
    return Object.keys(this.things).map((key) => {
      return {key: key, value: this.things[key] }
    })
  }
}

Die Antwort von @ AJT_82 funktioniert auf jeden Fall genau wie angekündigt. In meinem Fall bestand das spezielle Problem jedoch darin, dass die Hilfsfunktion getThings() jedes Mal eine neue Liste von Objekten zurückgab. Obwohl ihr Inhalt derselbe war, wurden die Objekte selbst bei jedem Aufruf der Funktion (der während der Änderungserkennung stattfand) neu generiert. Daher hatten sie für den Änderungsdetektor unterschiedliche Identitäten und die Form wurde bei jeder Modelländerung neu generiert.

Die einfache Lösung in meinem Fall war, das Ergebnis von getThings() zwischenzuspeichern und als Iterator zu verwenden:

<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] }
    })
  }
}

In Fällen, in denen cachedThings möglicherweise variieren muss, muss es manuell aktualisiert werden, damit der Änderungsdetektor ein erneutes Rendern auslöst.

0
t.888