You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular组件模板驱动表单单元测试问题:表单无可用控件

Template-driven Form Subcomponent Controls Not Found in Parent NgForm During Unit Testing

Let’s break down why your test is failing and fix it step by step:

Root Causes

  1. Missing CustomInputComponent in Test Setup
    Your PersonDetailsContactInfoComponent relies on a custom-input component, but your test module doesn’t declare or import it. Without this, Angular can’t render the input elements, so their corresponding form controls never get registered with the parent NgForm.

  2. Mismatched name Attribute on Fax Input
    Looking at your template, the fax input uses name="model.fax" but binds to [(ngModel)]="fax". This mismatch would register the control under the name model.fax (not fax) if it worked, which is almost certainly not what you intend.

  3. Incorrect Timing of fixture.detectChanges()
    You’re setting the model value before triggering initial change detection. Angular needs to run change detection first to initialize the component, render child components, and register form controls with the parent form.

Fixed Solution

Step 1: Update Test Module to Include CustomInputComponent

Add CustomInputComponent to the declarations array (or import its shared module if it’s part of one):

beforeEach(() => {
  TestBed.configureTestingModule({
    imports: [FormsModule, NoopAnimationsModule],
    declarations: [
      TestHostComponent,
      PersonDetailsContactInfoComponent,
      CustomInputComponent // Add this line
    ],
    providers: [ ]
  });
  fixture = TestBed.createComponent(TestHostComponent);
  component = fixture.componentInstance;
});

Step 2: Fix the Fax Input’s name Attribute

In person-details-contact-info.component.html, align the name attribute with your ngModel binding:

<custom-input type="text" [disabled]="permissions.canViewOnly" name="fax" [(ngModel)]="fax" maxlength="16">
</custom-input>

Step 3: Adjust Change Detection Timing in Tests

Run initial change detection first to set up the form, then update the model and re-run detection:

it('has invalid email format', () => {
  // Initialize component and form first
  fixture.detectChanges();

  // Set invalid email value
  component.model.emailAddress = 'an invalid email format.';
  fixture.detectChanges();

  const ctrl = component.ngForm.form.get('emailAddress');
  expect(ctrl).not.toBeNull(); // First verify the control exists
  expect(ctrl.valid).toBe(false);
});

Explanation

  • Declaring CustomInputComponent lets Angular render the input elements and register their controls with the parent NgForm using their name attributes.
  • Fixing the name attribute ensures the control is registered under the correct key, matching your ngModel binding.
  • Running fixture.detectChanges() upfront triggers the component’s initialization lifecycle, which is required for form controls to attach to the parent NgForm.

内容的提问来源于stack exchange,提问作者Jake Shakesworth

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:51:44