3

I am trying to achieve the following where I can wrap <app-test-field> components inside the <form> tag where I can dynamically bind in formControlName and formGroup to TestField input as follows using Angular with StorybookJS.

However, I am encountering the following errors:

  • core.mjs:11483 ERROR Error: NG01203: No value accessor for form control name: 'name'.
  • core.mjs:11483 ERROR Error: NG01050: formControlName must be used with a parent formGroup directive. You'll want to add a formGroup directive and pass it an existing FormGroup instance (you can create one in your class).

In TestField.stories.js

Here is the template for my Angular component in StorybookJS.

<form [formGroup]="sampleForm">
  <!-- name input -->
  <app-test-field [label]="label" [formControlName]="formControlName"></app-test-field>  
  <!-- email input... -->  
  <!-- more inputs... --> 
</form>

In test-field.component.html

<div class="form-group">
  <label for="{{ formControlName }}">{{ label }}</label>
  <input
    type="text"
    class="form-control"
    [formControlName]="formControlName"
    [id]="formControlName"
  /><br />
</div>

In test-field.component.ts

import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-test-field',
  templateUrl: './test-field.component.html',
  styleUrls: ['./test-field.component.css'],
})
export class TestFieldComponent {
  @Input() label: string = '';
  @Input() formControlName: string = '';
}

I researched most examples online where the input itself is wrapped around with the <form> tag in the component itself as below:

<form [formGroup]="registerForm">
  <div class="form-group">
    <label>Name</label>
    <input type="text" class="form-control" [ngClass]="inputDanger" formControlName="name"><br/>
    </span>
    <br/>
  </div>
</form>

But I want to remove form tag such that the end goal would be the following where input is a component by itself wrapped inside a <form> tag as follows:

<form [formGroup]="sampleForm">
  <!-- name input -->
  <app-test-field [label]="label" [formControlName]="formControlName"></app-test-field>  
  <!-- email input... -->  
  <!-- more inputs... --> 
</form>

Can someone help me with this? Is anything missing?

7
  • levelup.gitconnected.com/… I think it will be helpful for you. Commented Oct 18, 2023 at 6:29
  • 1
    Thanks for the link! Will give this a read. Its an article with membership payment required to access this content. Is it the same case for you?
    – Jinglemahn
    Commented Oct 18, 2023 at 6:42
  • Or make a Custom form Control or use ViewProvider like this SO -one e.g. among a lot-
    – Eliseo
    Commented Oct 18, 2023 at 7:10
  • I have added the solution can you please check and let me know if is there are any problem ? Commented Oct 18, 2023 at 7:22
  • 1
    Here's an example of a value accessor
    – Pieterjan
    Commented Oct 18, 2023 at 10:49

2 Answers 2

0

To use the control in a separate component without the wrapping form, you can use Angular's NgControl

Here is an example of usage in the case of the question:

In test-field.component.ts:

the ngControl is passed in the constructor and has some required functions

import { Component, Input, Optional, Self } from '@angular/core';
import { NgControl } from '@angular/forms';

@Component({
  selector: 'app-test-field',
  templateUrl: './test-field.component.html',
  styleUrls: ['./test-field.component.scss'],
})
export class TestFieldComponent {
  @Input() label: string = '';

  onChange: any = () => {};
  onTouch: any = () => {};

  constructor(@Optional() @Self() public ngControl: NgControl) {
    if (this.ngControl != null) {
      this.ngControl.valueAccessor = this;
    }
  }

  ngOnInit(): void {}

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouch = fn;
  }

  writeValue(value: any): void {}
}

Now, using the ngControl in test-field.component.html:

<div class="form-group">
  <label for="{{ ngControl?.name }}">{{ label }}</label>
  <input
    type="text"
    class="form-control"
    [formControl]="ngControl?.control"
    [id]="ngControl?.name"
  /><br />
</div>

In TestField.stories.js you can only remove the 'formControlName' input as it is no longer needed:

<form [formGroup]="sampleForm">
  <!-- name input -->
  <app-test-field [label]="label"></app-test-field>  
  <!-- email input... -->  
  <!-- more inputs... --> 
</form>
1
  • In my component html form, I am getting Type 'AbstractControl<any, any> | null | undefined' is not assignable to type 'FormControl<any>'.
    – Jinglemahn
    Commented Oct 18, 2023 at 6:41
0

I did get the same error. I my case was that is was adding the "formControlName" attribute in a HTML element that can't have a value, like a Div or Section, so I fix that adding the "formControlName" directive to a "input" tag

Not the answer you're looking for? Browse other questions tagged or ask your own question.