Кендо: вставить FormGroup сверху в FormArray
Постановка проблемы:
В приведенной ниже демонстрации стекаблиц я могу динамически вставить FormGroup
в начале сетки, нажав кнопку Add
и вызвав событие (blur)
из одного элемента управления, чтобы обновить значение другого элемента управления, совместно использующего rowIndex
, Теперь проблема в том, что если я вставил несколько formGroups
и включил событие (blur)
на любом из недавно добавленных FormGroups
, он обновляет значение других элементов управления, а также имеет другие rowIndex
.
Действия по воспроизведению проблемы:
- Откройте демонстрационную ссылку ниже.
- Добавьте несколько строк (2 строки), нажав кнопку Добавить.
- Введите значение в поле имени 2-й вновь добавленной строки и сфокусируйтесь.
- Он обновляет поле возраста в новой добавленной строке, а не только во второй.
Демонстрация Stackblitz: https://stackblitz.com/edit/angular-oitz5j-4uezqr
Требование:
Выделите любое поле имени определенной строки. Поле Age в той же строке должно отображать случайное число, не затрагивая другие строки.
Что я пробовал до сих пор?
Пытался использовать метод Array unshift(), но получал ошибку
Свойство unshift не существует для типа FormArray
Попытался использовать метод FormArray.insert(). Он будет работать, если мы хотим добавить FormGroup
по определенному индексу. Но он не может динамически вставить несколько FormGroups
поверх FormArray
.
Обновление: Вышеупомянутая проблема работает нормально без Kendo Grid. Пожалуйста, проверьте демо ниже.
Демо: https://stackblitz.com/edit/angular-oitz5j-2a31gs
Связанные проблемы в github/stackoverflow:
https://github.com/angular/angular/issues/16322
Angular - форму массива push определенного индекса
Примечание: Если мы собираемся добавить элементы управления в конце сетки, она работает как чемпион. Пожалуйста, ознакомьтесь с приведенной ниже демонстрацией, которая прекрасно работает с последним добавлением элементов управления.
Демо: https://stackblitz.com/edit/angular-oitz5j-aszrjq
Ответы
Ответ 1
Почему мой код не работает?
Это не проблема кендо вообще. Вся ваша форма уже создается каждый раз, когда запускается наблюдаемая view
. Поэтому, когда вы добавляете строку, вы в основном добавляете к items
в createForm
, а также запускаете наблюдаемую view
(вызывая updateGridData
), что, в свою очередь, добавляет к items
в createForm
. Telerik, кажется, делает последнее, как видно из этого примера, поэтому нам вообще не нужно insert
входить в items
в addNewRow
. Вместо этого просто обновите gridData
и затем вызовите updateGridData
.
Решение
В вашем коде всего две проблемы. Первый находится в вашем блоке подписки view
. Код, который вы используете, будет в основном просто помещаться поверх предыдущего items
, а не заменять их. Поэтому нам нужно переинициализировать items
FormArray
здесь.
this.view.subscribe(r => {
this.createForm.setControl('items', new FormArray([]));
r.data.forEach((i) => {
const newRow = new FormGroup({
name: new FormControl(i.name),
age: new FormControl(i.age)
});
(this.createForm.get("items") as FormArray).push(newRow);
});
});
Вы можете переписать это, используя map
, как показано ниже.
this.view.subscribe(r => {
this.createForm.setControl('items', new FormArray(r.data.map(item =>
new FormGroup({
name: new FormControl(item.name),
age: new FormControl(item.age),
})
)));
});
Теперь все, что вам нужно сделать, это назначить gridData
при добавлении новой строки. Нам нужно получить данные, введенные пользователем из createForm
, поскольку gridData
нигде не привязан и не будет содержать данные, введенные пользователем. Здесь не нужно будет ничего делать с формой, поскольку блок подписки view
уже заботится об объявлении createForm
.
addNewRow() {
const blankRow = {
name: "",
age: ""
};
this.gridData = [blankRow, ...this.createForm.get('items').value];
const data: GridDataResult = { data: this.gridData, total: this.gridData.length };
this.editService.updateGridData(data);
}
Сетка теперь должна работать.
Разбитый стек Блиц
Примечание. Причина, по которой пример, в котором вы перемещаете новую строку в конец сетки, работает, заключается в том, что вы объявляете createForm
только один раз в начале в ngOnInit
. Затем вы вручную обновите gridData
и insert
в items
. Здесь нет updateGridData
, который запускает view
для воссоздания createForm
. Если бы было, даже это не сработало бы, как ожидалось.
Ответ 2
Как я начал отлаживать:
Я не говорю, что это не странно, но когда вы вызываете этот метод FormArray.insert
, он вставляет копию существующего массива formArray и сопоставляется с текущим состоянием. Я добавил console.log(formArray.controls)
и проверил, что он растет в геометрической прогрессии, что означает, что моментальный снимок добавлялся при каждой вставке.
Это заставило меня узнать, что вы подписаны на view
Observable, и всякий раз, когда он изменяется, вы перемещаете все элементы в сетке из существующего списка.
Как это исправить:
Шаг 1: в вашем методе addRow удалите создание formGroup и вставку в массив, как это уже обрабатывается в вашем методе view.subscribe
addNewRow() {
...
/*
<--- remove the creation of formgroup and adding to formarray taken care in next step --->
if (this.createForm) {
(this.createForm.get("items") as FormArray).insert(2, newRow);
}
*/
}
Шаг 2: в вашем view.subscribe измените метод с push на insert. Таким образом, начальные элементы будут вставлены, а не вставлены
this.view.subscribe(r => {
r.data.forEach((data, i) => { //<-- Added index of the array
const newRow = new FormGroup({
name: new FormControl(data.name),
age: new FormControl(data.age),
});
if (this.createForm) {
(this.createForm.get("items") as FormArray).insert(i, newRow);//<--changed to insert
}
});
});
Шаг 3 Теперь вы хотите отменить
addNewRow() {
const blankRow = {
name: "",
age: "",
};
this.gridData.unshift(blankRow); //<-- you do it here.
const data: GridDataResult = { data: this.gridData, total: this.gridData.length, }
this.editService.updateGridData(data);
}
Обновлен Stackblitz для вашей справки: https://stackblitz.com/edit/angular-oitz5j-ulhs4k
Ответ 3
проблема в кендо-сетке. вы наступаете на индекс 0 снова и снова его источника данных
и кто знает, что он будет делать внутри, чтобы отобразить данные. но верно то, что он не использует значение массива формы. если вы извлечете из массива кендо сетку, она будет работать нормально или если вы добавите в конце вашего метода onFocusOut консольный журнал console.log(this.createForm.get("items").value);
, вы увидите, что значение formArray в порядке.
Если вам нужно использовать да или да кендо сетки. прочитайте следующий документ:
https://www.telerik.com/kendo-angular-ui/components/grid/editing/editing-reactive-forms/
примеры, которые я вижу, показывают, что вы не можете обращаться с формой извне. вам нужно использовать методы, которые предоставляет сетка кендо. потому что сетка кендо не отображает значение fromArray действительно