Я делаю динамическую форму. Field имеет список значений. Каждое значение представлено строкой.

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

У меня есть функция в моем компоненте, которая добавляет новое значение в поле.

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

Значения и кнопка отображаются так в моем HTML.

 {amp}lt;div id="dropdown-values" *ngFor="let value of field.values; let j=index"{amp}gt; {amp}lt;input type="text" class="form-control" [(ngModel)]="field.values[j]" [name]="'value'   j   '.'   i"/{amp}gt;{amp}lt;br/{amp}gt; {amp}lt;/div{amp}gt; {amp}lt;div class="text-center"{amp}gt; {amp}lt;a href="javascript:void(0);" (click)="addValue(field)"{amp}gt;{amp}lt;i class="fa fa-plus-circle" aria-hidden="true"{amp}gt;{amp}lt;/i{amp}gt;{amp}lt;/a{amp}gt; {amp}lt;/div{amp}gt; 

Как только я записываю некоторый текст во ввод значения, ввод теряет фокус. Если я добавляю много значений в поле и записываю символ в одно из введенных значений, ввод теряет фокус, и символ записывается в каждый ввод.

Это происходит, когда массив является примитивным типом, в вашем случае это массив String . Это может быть решено с помощью TrackBy . Поэтому измените ваш шаблон, чтобы он соответствовал следующему:

 {amp}lt;div *ngFor="let value of field.values; let i=index; trackBy:trackByFn"{amp}gt; {amp}lt;input type="text" [(ngModel)]="field.values[i]" /{amp}gt;{amp}lt;br/{amp}gt; {amp}lt;/div{amp}gt; {amp}lt;div{amp}gt; {amp}lt;button (click)="addValue(field)"{amp}gt;Click{amp}lt;/button{amp}gt; {amp}lt;/div{amp}gt; 

и в файле ts добавьте функцию trackByFn , которая возвращает (уникальный) index значения:

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

Это ссылка на ту же проблему, за исключением проблемы для AngularJS, но проблема соответствует вашей. Самый важный отрывок из этой страницы:

Вы повторяете массив и изменяете элементы массива (обратите внимание, что ваши элементы являются строками, которые являются примитивами в JS и, таким образом, сравниваются «по значению»). Поскольку новые элементы обнаруживаются, старые элементы удаляются из DOM и создаются новые (которые, очевидно, не получают фокус).

С TrackBy Angular может отслеживать, какие элементы были добавлены (или удалены) в соответствии с уникальным идентификатором, и создавать или уничтожать только те вещи, которые изменились, что означает, что вы не теряете фокус на поле ввода :)

Как видно из ссылки, вы также можете изменить свой массив, чтобы он содержал уникальные объекты и использовал, например, [(ngModel)]="value.id" , но, возможно, это не то, что вам нужно.

эта библиотека решает такие проблемы:
https://www.npmjs.com/package/keyboard-navigator
сохранение фокуса с помощью трекбай работает только для итеративных элементов, а также, если этот элемент действительно изменился (изменение уникального идентификатора), ваш фокус теряется. в немногих приложениях, использующих хранилище (ngrx / redux), может существовать корневой объект, а рассматриваемый список может быть свойством sub-sub корневого объекта, в большинстве случаев редукторы создают новую копию корневого объекта (старые ссылки теряются ) запуск обновлений DOM на желаемых узлах DOM вместе с другими нежелательными узлами тоже. альтернатива, которую я нашел, состояла в том, чтобы поддерживать фокус с помощью ссылки Xpath.

рассмотрим функцию retainFocus, которая при вызове сохраняет Xpath текущего активного элемента и запускает асинхронную функцию, которая фокусирует элемент на DOM, совпадая с сохраненным выше Xpath.

теперь вызов этой функции внутри привязок store-update-subscription / template-bindings, которые запускают обновления DOM, гарантирует, что элемент в той же позиции будет сфокусирован (это может быть тот же элемент или какой-то другой, но будет сохранена относительная позиция фокуса).

Это происходило со мной, когда я перебирал ключи и значения объекта с помощью вспомогательной функции:

 {amp}lt;div *ngFor="let thing of getThings()" [attr.thingname]="thing.key"{amp}gt; ... {{ applyThing(thing.value) }} {amp}lt;/div{amp}gt; 

В моем компоненте я возвращал массив объектов, содержащих пары ключ / значение:

 export ThingComponent { ... //this.things = { a: { ... }, b: { ... }, c: { ... } } public getThings() { return Object.keys(this.things).map((key) ={amp}gt; { return {key: key, value: this.things[key] } }) } } 

Ответ, данный @ AJT_82, определенно работает точно так, как рекламируется. Однако в моем случае особая проблема заключалась в том, что вспомогательная функция getThings() каждый раз возвращала новый список объектов. Несмотря на то, что их содержание было одинаковым, сами объекты восстанавливались при каждом вызове функции (что происходило во время обнаружения изменений) и, следовательно, у детектора изменений они имели разные идентификаторы, и форма изменялась при каждом изменении модели.

Простым решением в моем случае было кешировать результат getThings() и использовать его в качестве итератора:

 {amp}lt;div *ngFor="let thing of cachedThings" [attr.thingname]="thing.key"{amp}gt; ... {{ applyThing(thing.value) }} {amp}lt;/div{amp}gt; 

 export ThingComponent { public cachedThings = getThings() ... //this.things = { a: { ... }, b: { ... }, c: { ... } } private getThings() { return Object.keys(this.things).map((key) ={amp}gt; { return {key: key, value: this.things[key] } }) } } 

В случаях, когда cachedThings может потребоваться изменить, его нужно будет обновить вручную, чтобы детектор изменений запустил повторный рендеринг.