我在操作中将表单控件标记为已触摸和脏,并且更改未反映在 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 调用以及多次手动超时。
<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);