表单控件被标记为脏和触摸后,Angular 不会更新 UI

问题描述 投票:0回答:1

我在操作中将表单控件标记为已触摸和脏,并且更改未反映在 UI 上。

我正在使用 ngxtension 库中的 signalSlice: https://ngxtension.netlify.app/utilities/signals/signal-slice/

在调用 markAsTouched 之后,我希望 UI 能够反映该更改,但这仅在模糊事件之后发生(单击输入,然后单击输入外部)。如果有更多输入,所有输入都会同时更新。

我想要实现的是能够将我的表单标记为在 signalSlice 操作中触摸。

这是 StackBlitz 上的最小应用程序: https://stackblitz-starters-9bchku.stackblitz.io

这是应用程序的代码:

@Injectable()
export class AppService {
  readonly #initialState = {
    submitting: false,
  };

  readonly state = signalSlice({
    initialState: this.#initialState,
    actionSources: {
      submit: (_state: any, $: Observable<void>) =>
        $.pipe(
          tap(() => console.log('Submit triggered')),
          tap(() => {
            this.formGroup.controls.name.markAsDirty();
            this.formGroup.controls.name.markAsTouched();

            console.log(
              this.formGroup.controls.name.touched,
              this.formGroup.controls.name.dirty,
              this.formGroup
            );

            console.log('Control should be marked as dirty and touched');
          }),
          filter(() => this.formGroup.valid),
          tap(() => console.log('Form valid')),
          switchMap(() =>
            timer(5000).pipe(
              tap(() => console.log('Submitted')),
              map(() => ({ submitting: false })),
              startWith({ submitting: true })
            )
          )
        ),
    },
  });

  readonly formGroup = new FormGroup<{ name: FormControl<string> }>({
    name: new FormControl<string>('', { validators: [Validators.required] }),
  });
}

@Component({
  selector: 'app-form',
  standalone: true,
  providers: [],
  imports: [JsonPipe, ReactiveFormsModule],
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <form [formGroup]="formGroup">
      <input type="text" formControlName="name" placeholder="Name" />

      <p>Touched: {{ formGroup.get('name').touched | json }}</p>
      <p>Dirty: {{ formGroup.get('name').dirty | json }}</p>
    </form>
`,
})
export class MyForm {
  #appService = inject(AppService);

  formGroup = this.#appService.formGroup;
}

@Component({
  selector: 'app-root',
  standalone: true,
  providers: [AppService],
  imports: [MyForm, JsonPipe],
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <h1>Hello!</h1>

    <app-form />

    <button type="button" (click)="submit()">Submit</button>

    <p>Submitting: {{ submitting() | json }}</p>
  `,
})
export class MyApp {
  #appService = inject(AppService);

  submit = this.#appService.state.submit;
  submitting = this.#appService.state.submitting;
}

bootstrapApplication(MyApp);

我尝试了一些方法,例如手动调用更改检测、延迟 markAsTouched 调用以及多次手动超时。

angular signals angular-reactive-forms angular-forms angular-library
1个回答
0
投票

<app-form>
组件没有理由重新渲染(因为
changeDetection: ChangeDetectionStrategy.OnPush
策略)。

你需要以某种方式强迫它。您可以引入一种变化检测标记:

@Injectable()
export class AppService {
  readonly #initialState = {
    submitting: false,
    cdTick: 0,
  };

  readonly state = signalSlice({
    initialState: this.#initialState,
    actionSources: {
      tick: (_state: any, $: Observable<void>) =>
        $.pipe(
          map(() => ({
            cdTick: _state.cdTick() ? 0 : 1,
          }))
        ),
      submit: (_state: any, $: Observable<void>) =>
        $.pipe(
          tap(() => console.log('Submit triggered')),
          tap(() => {
            this.formGroup.controls.name.markAsDirty();
            this.formGroup.controls.name.markAsTouched();

            console.log(
              this.formGroup.controls.name.touched,
              this.formGroup.controls.name.dirty,
              this.formGroup
            );

            this.state.tick();

            console.log('Control should be marked as dirty and touched');
          }),
          filter(() => this.formGroup.valid),
          tap(() => console.log('Form valid')),
          switchMap(() =>
            timer(5000).pipe(
              tap(() => console.log('Submitted')),
              map(() => ({ submitting: false })),
              startWith({ submitting: true })
            )
          )
        ),
    },
  });

  readonly formGroup = new FormGroup<{ name: FormControl<string> }>({
    name: new FormControl<string>('', { validators: [Validators.required] }),
  });
}

@Component({
  selector: 'app-form',
  standalone: true,
  providers: [],
  imports: [JsonPipe, ReactiveFormsModule],
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <form [formGroup]="formGroup">
      <input type="text" formControlName="name" placeholder="Name" />

      <p>Touched: {{ formGroup.get('name').touched | json }}</p>
      <p>Dirty: {{ formGroup.get('name').dirty | json }}</p>
      <p>cdTick: {{ cdTick() | json }}</p>
    </form>
`,
})
export class MyForm {
  #appService = inject(AppService);
  cdTick = this.#appService.state.cdTick;

  formGroup = this.#appService.formGroup;
}

@Component({
  selector: 'app-root',
  standalone: true,
  providers: [AppService],
  imports: [MyForm, JsonPipe],
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <h1>Hello!</h1>

    <app-form />

    <button type="button" (click)="submit()">Submit</button>

    <p>Submitting: {{ submitting() | json }}</p>
  `,
})
export class MyApp {
  #appService = inject(AppService);

  submit = this.#appService.state.submit;
  submitting = this.#appService.state.submitting;
}

bootstrapApplication(MyApp);

堆栈闪电战

© www.soinside.com 2019 - 2024. All rights reserved.