angularangular-reactive-formsformgroupsangular-signals

How pass input value as FormGroup in Angular 17+


I'm trying to use Signals Input in angular 17 to pass a specific form. In the older approach works fine:

@Component({
  selector: 'app-user-data-create-update',
  standalone: true,
  imports: [ReactiveFormsModule, PanelModule, NgStyle],
  templateUrl: './user-data-create-update.component.html',
  styleUrl: './user-data-create-update.component.scss',
})
export class UserDataCreateUpdateComponent {
  // Injects
  private readonly formBuilder: FormBuilder = inject(FormBuilder);

  // Inputs
  @Input() form: Signal<FormGroup<any>>; //works fine
  fontSize = input<string>('0.8rem'); //works fine

  constructor() {
    this.form = signal(
      this.formBuilder.group({
        createdDateTime: [''],
        creatorXID: [''],
        creatorPersonXID: [''],
        creatorPersonEmail: [''],
        updatedDateTime: [''],
        updaterXID: [''],
        updaterPersonXID: [''],
        updaterPersonEmail: [''],
      })
    );
  }
}

Calling component:

<app-user-data-create-update [form]="formGroupProgram"/>

However, I've already tried a lot of things to works with "new" Angular signals with FormGroup and didn't works.

Any idea?


Solution

  • Angular 17 & 18:

    Please initialize the default value inside the function input.

    We can use the input signal to behave the same as @Input and receive a new value from the parent, or use the value initialized inside the input signal initialization.

    @Component({
      selector: 'app-user-data-create-update',
      standalone: true,
      imports: [ReactiveFormsModule, PanelModule, NgStyle],
      templateUrl: './user-data-create-update.component.html',
      styleUrl: './user-data-create-update.component.scss',
    })
    export class UserDataCreateUpdateComponent {
      // Injects
      private readonly formBuilder: FormBuilder = inject(FormBuilder);
    
      // Inputs
      readonly form: InputSignal<FormGroup<any>> = input(this.getForm()); //works fine
      readonly fontSize = input<string>('0.8rem'); //works fine
    
      getForm() {
        return this.formBuilder.group({
            createdDateTime: [''],
            creatorXID: [''],
            creatorPersonXID: [''],
            creatorPersonEmail: [''],
            updatedDateTime: [''],
            updaterXID: [''],
            updaterPersonXID: [''],
            updaterPersonEmail: [''],
        });
      }
    }
    

    Angular 19:

    If you want the form state to change in the future, meaning you will override the value with something else locally in your component, then you require something called local state (linkedSignal - will take a value from the signals inside and can also be modified by the user unlike input signal).

    Here we initialize the input with a different name, but since the different name will affect data binding, we use alias to set the data binding name as form

    @Component({
      selector: 'app-user-data-create-update',
      standalone: true,
      imports: [ReactiveFormsModule, PanelModule, NgStyle],
      templateUrl: './user-data-create-update.component.html',
      styleUrl: './user-data-create-update.component.scss',
    })
    export class UserDataCreateUpdateComponent {
      // Injects
      private readonly formBuilder: FormBuilder = inject(FormBuilder);
    
      // Inputs
      readonly formInput: InputSignal<FormGroup<any>> = input(this.getForm(), { alias: 'form' }); //works fine
      readonly form: WritableSignal<FormGroup<any>> = linkedSignal(() => this.formInput());
      readonly fontSize = input<string>('0.8rem'); //works fine
    
      getForm() {
        return this.formBuilder.group({
            createdDateTime: [''],
            creatorXID: [''],
            creatorPersonXID: [''],
            creatorPersonEmail: [''],
            updatedDateTime: [''],
            updaterXID: [''],
            updaterPersonXID: [''],
            updaterPersonEmail: [''],
        });
      }
    
      ngAfterViewInit() {
        this.form.set(  // <- you can change the form state locally if needed
          this.formBuilder.group({
            createdDateTime: [''],
            creatorXID: [''],
          })
        );
      }
    }