Использование combLatest с valueChanges в ngOnInIt

Я пытаюсь использовать combLatest с методом valueChanges реактивной формы в хуке NgOnInIt. Мой вариант использования ниже.

Я хочу показать имя, полученное от Observable1$ в форме, используя patchValue() из реактивных форм. Это поле имени доступно для редактирования, что означает когда пользователь хочет отредактировать имя, которое уже отображается в форме, я хочу проверить, не совпадает ли имя, и если это не так, я хочу включить кнопку для вызвать метод PUT. Следовательно, я хочу использовать combLatest из RxJS для обработки этого поведения, но я не могу этого сделать. Ниже ошибки, которые я получаю.

Фактическое поведение:

  1. Я получаю сообщение об ошибке, говорящее о превышении максимального размера стека вызовов из-за updateValueAndValidity(), поскольку он переходит в бесконечный цикл.
  2. Я добавил {emitEvent: false} в patchValue(), но когда я попытался отредактировать поле, из-за этого {emitEvent: false} больше ничего не выдается.

Ожидаемое поведение:

Поле формы должно отображать данные, которые я получил от observable1$ (например: testName). Когда я хочу отредактировать это поле (например: testNameE), метод valueChanges должен выдавать значение {description: testNameE}.

Как я пытаюсь этого добиться.

Форма-component.ts

  ngOnInit() {
    combineLatest([
      this.observable1$.pipe(startWith(null)), - provides name
      this.form.valueChanges.pipe(startWith(null)), - wants to get the input from the user when he tried to change the name
    ]).pipe(
      tap(([name, formData]) => {
        if (name) {
          this.patchValue(name);
        }
        if (formData) {
        // check the name is different than received from observable1$,
        // if yes, enable button
        }
      }),
    ).subscribe();
  }
  
  patchValue(name: string) {
    this.form.patchValue({
      name: name,
    });
    this.form.updateValueAndValidity();
  }

Может кто-нибудь, пожалуйста, скажите мне, что я делаю неправильно здесь. Любая помощь приветствуется.

Ниже ссылка на stackblitz. https://stackblitz.com/edit/angular-ivy-zjpwxb?file=src/app/app.component.ts

PatchValue вызовет Observable valueChanges, поэтому возникает бесконечный цикл.

enno.void 18.11.2022 09:20

@ enno.void Я понял это, когда удалил его, но если я не использую patchValue, то как я могу отобразить в форме имя, которое я получил от observable1$?

Radiant 18.11.2022 09:22

Вы можете указать patchValue не генерировать событие или генерировать только для себя, ознакомьтесь с документацией: angular.io/api/forms/FormControl

enno.void 18.11.2022 09:23

@ enno.void Я пробовал это, но когда я это сделал и если хочу отредактировать значение в форме, он ничего не испускает.

Radiant 18.11.2022 09:25

Он ничего не излучает ... пожалуйста, обновите свой вопрос, указав подробности этого. Откуда вы знаете? Что такое фактическое и ожидаемое поведение и минимальный пример, демонстрирующий это

Andrew Allen 18.11.2022 09:31

@AndrewAllen Я добавил фактическое и ожидаемое поведение и попытаюсь создать пример на stackblitz.

Radiant 18.11.2022 09:48

@AndrewAllen вот ссылка на stackblitz stackblitz.com/edit/angular-ivy-zjpwxb?file=src/app/…. Пожалуйста, проверьте.

Radiant 18.11.2022 10:32

@ enno.void Я создал пример. Пожалуйста, проверьте stackblitz.com/edit/angular-ivy-zjpwxb?file=src/app/…

Radiant 18.11.2022 10:33

Извините, я все еще не понимаю, чего вы пытаетесь достичь. Вы исправляете значение, чтобы обновить имя формы, чтобы оно соответствовало имени observable1$, так почему ваш наблюдаемый объект, который проверяет, отличается ли имя формы от имени observable1$, когда-либо возвращает true, чтобы включить кнопку

Andrew Allen 18.11.2022 11:50

@AndrewAllen Я хочу предоставить пользователю возможность отредактировать имя и сохранить его.

Radiant 18.11.2022 12:02
Улучшение генерации файлов Angular
Улучшение генерации файлов Angular
Angular - это фреймворк. Вы можете создать практически любое приложение без использования сторонних библиотек.
Еженедельник новостей для разработчиков - выпуск №10
Еженедельник новостей для разработчиков - выпуск №10
В выпуске этой недели собраны самые интересные статьи и новости, отобранные из всего контента, опубликованного за предыдущую неделю на сайте...
Для развертывания Сайтов с использованием Blazor, Angular и React с репозиторием на GitHub на Cloudflare.
Для развертывания Сайтов с использованием Blazor, Angular и React с репозиторием на GitHub на Cloudflare.
Как развернуть сайты с помощью Blazor, Angular и React с репозиторием на GitHub на Cloudflare.
Шаблоны Angular PrimeNg
Шаблоны Angular PrimeNg
Как привнести проверку типов в наши шаблоны Angular, использующие компоненты библиотеки PrimeNg, и настроить их отображение с помощью встроенной...
Promise и Observables в Angular
Promise и Observables в Angular
Здесь мы рассмотрим основные различия между обещаниями и наблюдаемыми таблицами на примере.
Как использовать d3.js для рисования 2D SVG-элементов в приложении Angular?
Как использовать d3.js для рисования 2D SVG-элементов в приложении Angular?
D3.js - это обширная библиотека, используемая для привязки произвольных данных к объектной модели документа (DOM). Мы разберем основные варианты...
0
10
101
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Чтобы проверить, действительна ли форма на основе значений полей, вам необходимо использовать валидаторы форм.

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

Затем вы можете отключить кнопку отправки, если форма недействительна.

Компонент.ts

import { Component, OnInit } from '@angular/core';
import { AbstractControl, FormBuilder, FormGroup, ValidationErrors, Validators } from '@angular/forms';
import { delay, Observable, of } from 'rxjs';

@Component({
  selector: 'app-temp',
  templateUrl: './temp.component.html',
  styleUrls: ['./temp.component.scss']
})
export class TempComponent implements OnInit {

  nameFromService?: String;
  nameForm?: FormGroup;
  
  constructor(private formBuilder: FormBuilder) { }

  ngOnInit(): void {
    this.nameForm = this.formBuilder.group({
      name: ['', [Validators.required, this.checkNameIsServiceName] ]
    });

    this.setNameFromService();
  }

  checkNameIsServiceName = (control: AbstractControl): ValidationErrors | null => {
    if (this.nameFromService == control.value) return {forbiddenName: {value: control.value}};
    else return null;
  }

  setNameFromService(){
    // TODO GET name from service that returns an observale
    let obsStringName: Observable<String> = of('TEST').pipe(delay(1000));

    obsStringName.subscribe(result => {
      // set variable value and form field value
      this.nameFromService = result;
      this.nameForm?.patchValue({name: result});
    });
  }

  onSubmit(){
    // PUT request
  }
}

Компонент.html

<div *ngIf = "nameForm && nameFromService">
    <form [formGroup] = "nameForm">
        <input type = "text" placeholder = "Enter username" formControlName = "name" />

        <button type = "submit" 
            (click) = "onSubmit()"
            [disabled] = "!nameForm.valid">
            Put User
        </button>
    </form>
</div>
Ответ принят как подходящий

Я думаю, что самое простое решение здесь — отделить логику формы патча, чтобы она зависела только от observable1$, а не от изменения значения.

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


  ngOnInit() {
    // patch name to form
    this.observable1$.pipe(
      filter(v => !!v),
      takeUntil(this.destroyed$)
    ).subscribe(name => this.patchValue(name))

    // for use with a async pipe
    this.showSaveButton$ = combineLatest([this.observable1$, this.form.valueChanges])
      .pipe(
        filter(([name, _]) => !!name)
        map(([name, formData]) => {
          // check different
          return name !== formData.name
        }),
        takeUntil(this.destroyed$),
        startWith(false)
      )
  }
  
  patchValue(name: string) {
    this.form.patchValue({
      name: name,
    });
    this.form.updateValueAndValidity();
  }

Большое спасибо за ответ. Я пытался сделать что-то подобное, что тоже было для меня просто, но потом решил использовать combLatest. Я опубликую это решение в ближайшее время.

Radiant 18.11.2022 14:06

Другие вопросы по теме