Подтвердить что ты не робот

Кендо: вставить 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

4b9b3361

Ответ 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 действительно