angularangular-reactive-formsangular-forms

Typed Angular form with formbuilder and null as initial value


I want to make use of typed forms in Angular.
In my scenario I have a form group that should have some properties.
All of them should be nullable and start with an initial value of "null".
But they should also be typed.

I also want to use the form builder to make my code short & concise.

However, I am failing to do what I want.

One of my approaches looks like this:

   formGroup = this.fb.group<{
      User: FormControl<string | null>,
      Site: FormControl<string | null>,
      AccessLevel: FormControl<string | null>,
      Application: FormControl<string | null>,
      BusinessProcessArea: FormControl<string | null>,
    }>({
      User: [null, Validators.required],
      Site: [null, Validators.required],
      AccessLevel: [null, Validators.required],
      Application: [null, Validators.required],
      BusinessProcessArea: [null, Validators.required],
   });

However, typescript does not like what I am doing and throws an error here.
Also I need to double all the properties what makes this piece of code also hard to maintain.

I would love to do something like this:

   formGroup = this.fb.group({
      User<string>: [null, Validators.required],
      Site<string>: [null, Validators.required],
      AccessLevel<string>: [null, Validators.required],
      Application<string>: [null, Validators.required],
      BusinessProcessArea<string>: [null, Validators.required],
   });

I know that this is not valid Typescript and that I cannot put the type definition there.

But this should show my wish to:

How can I achieve this?


Solution

  • I find that defining an interface for my typed forms gives me a clearer syntax overall. An example is given below:

    
    // person-form.model.ts
    
    export interface IPersonForm {
        firstName: FormControl<string | null>;
        lastName: FormControl<string | null>;
        identifier: FormControl<string | null>;
    }
    
    // person-form.component.ts
    
    @Component({
        selector: 'app-person-form',
        standalone: true,
        imports: [],
        templateUrl: './person-form.component.html',
        styleUrl: './person-form.component.scss',
    })
    export class PersonFormComponent
        implements OnInit
    {
       
        private fb = inject(FormBuilder);
        public personForm!: FormGroup<IPersonForm>;
    
        ngOnInit(): void {
          this.buildForm()
        }
    
        private buildForm(): void {
          this.personForm = this.fb.group({
            firstName: [null, [Validators.required]],
            lastName: [null, [Validators.required],
            identifier: [null, [Validators.maxLength(255)]],
          });
        }
    
           
    
    
    

    Additionally, unless you have a specific need for null I would recommend typing the controls as string only and defining the initial value as empty string - this is a 'falsy' value and will trigger the required validator correctly without the use of null.