15

So I have a child component that goes something like this

export class ChildComponent implements OnInit {

  @Input('parentForm')
  public parentForm: FormGroup;

  constructor(private fb: FormBuilder, private cd: ChangeDetectorRef) { }

  ngOnInit() {
    this.parentForm.addControl('newControl', <Some Control>);
  }
}

Next I have a barebones unit testing file that goes like this

describe('ChildComponent', () => {
  let component: ChildComponent;
  let fixture: ComponentFixture<ChildComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [ReactiveFormsModule, FormsModule],
      declarations: [ ChildComponent ],
      providers: [ FormBuilder, FormGroup ]
    })
    .compileComponents();
  }));

  beforeEach(inject([FormBuilder], (fb: FormBuilder) => {
    fixture = TestBed.createComponent(ChildComponent);
    component = fixture.componentInstance;
    component.parentForm = fb.group({});
    component.ngOnInit();
    fixture.detectChanges();
  }));

  fit('should be created', () => {
    expect(component).toBeTruthy();
  });
});

Previously I had an issue where parentForm was undefined so I tried to build it myself by doing injecting FormBuilder in the beforeEach by doing this component.parentForm = fb.group({});. However now the issue is that karma/jasmine cannot find FormBuilder

Cannot find name 'FormBuilder'.

All I am trying to do is try to get or mock the parentForm for when I create an instance of the component during my unit testing, and I need it because I am calling ngOnInit during the for each as it as a new control.

Any ideas. Thank you

anonuser1234
  • 511
  • 2
  • 11
  • 24
  • Are you able to resolve this problem? My situation is also same. But I am getting "Can't resolve all parameters for FormGroup: (?, ?, ?)" error. – rajk Apr 04 '18 at 02:37
  • 1
    I noticed a typo in your code: `inject([FormBuidler], (fb: FormBuilder)` should be: `inject([FormBuilder], (fb: FormBuilder)` – Pixxl Jul 18 '18 at 02:22

1 Answers1

23

I was able to setup a successful Karma spec test for a Reactive Form Parent <-> Child component. Hopefully the example below will help guide your setup. I've simplified as much code from my codebase to focus on the core question you're trying to resolve.

Parent Component

parent.component.html

...
<div [stepControl]="childFormGroup">
  <child-form-group [formGroup]="childFormGroup"></child-form-group>
</div>
...

parent.component.ts

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';

@Component({
  selector: 'form-parent',
  templateUrl: './parent.component.html',
  styleUrls: ['./parent.component.scss']
})
export class FormParentComponent implements OnInit {
  // childFormGroup will be available on the parent DOM
  // so we can inject it / pass it to the ChildFormComponent
  public childFormGroup : FormGroup;

  constructor(private _formBuilder: FormBuilder) {
    this.createForm();
  }

  private createForm() : void {
    this.childFormGroup = this._formBuilder.group({
      name:  ['Sample Name', Validators.required],
      email: ['', Validators.required]
    });
  }
}

Child Component

child.component.html

...
<form [formGroup]="formGroup">
  <p>This is the childFormGroup</p>
  <br>

  <div>
    <input  placeholder="Name"
            formControlName="name"
            autocomplete="off">
  </div>

  <div>
    <input  placeholder="Email"
            formControlName="email"
            autocomplete="off">
  </div>
</form>
...

child.component.ts

import { Component, Input, Output, EventEmitter } from '@angular/core';
import { FormGroup } from '@angular/forms';

@Component({
  selector: 'child-form-group',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.scss'],
})
export class ChildFormComponent {
  // This declares an inherited model available to this component
  @Input() formGroup : FormGroup;

  constructor() { }

  /* There is no need to create the formGroup here
     hence no constructor method call or ngOnInit() hook...
     It will simply inherit the formGroup by passing it as an
     attribute on the DOM from parent.component.html
  */
}

child.component.spec.ts

import { async, ComponentFixture, TestBed, inject } from '@angular/core/testing';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { FormsModule, ReactiveFormsModule, FormGroup, FormBuilder, Validators } from '@angular/forms';

import { ChildFormComponent } from './child.component';

describe('ChildFormComponent', () => {
  let component: ChildFormComponent;
  let fixture: ComponentFixture<ChildFormComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      schemas: [
        CUSTOM_ELEMENTS_SCHEMA
      ],
      imports: [
        FormsModule,
        ReactiveFormsModule
      ],
      declarations: [
        ChildFormComponent
      ]
    })
    .compileComponents();
   }));

  beforeEach(inject([FormBuilder], (fb: FormBuilder) => {
    fixture = TestBed.createComponent(Step2Component);
    component = fixture.componentInstance;

    /* This is where we can simulate / test our component
       and pass in a value for formGroup where it would've otherwise
       required it from the parent
    */
    component.formGroup = fb.group({
      name:  ['Other Name', Validators.required],
      email: ['', Validators.required]
    });
    fixture.detectChanges();
  }));

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});
Pixxl
  • 945
  • 10
  • 18
  • 1
    This is awesome. Thanks! Fyi missing a second closing parenthesis at the end of the 2nd beforeEach... – James Young Oct 17 '18 at 19:29
  • @JamesYoung Thanks for letting me know, and glad this helped you! :) – Pixxl Nov 07 '18 at 19:10
  • Although I am no longer doing this type of coding I appreciated your answer! Wish I found this back when I had the problem haha – anonuser1234 Mar 04 '19 at 23:54
  • Ah well it's unfortunate I came upon this question late! Hopefully it helps someone else in the future though. I was running into the same issues so this let me work through it and I learned how to fix it myself. – Pixxl Mar 06 '19 at 01:23
  • @Pixxl it just helped me. Thanks a lot and greetings from the future! – Till Kuhn Feb 23 '21 at 21:33