0

Consider I have a form with multiple sections that are rendered using the ngTemplateOutlet.

<form [formGroup]="form">
    <ng-container *ngIf="true; then fooSection"></ng-container>
</form>

<ng-template #fooSection>
  <input type="checkbox" formControlName="isEulaAccepted">
<ng-template>

The problem is that formControlName can't access the parent formGroup directive.

Is it possible to make it work this way?

Slava Fomin II
  • 26,865
  • 29
  • 124
  • 202
  • Is the `FormsModule` imported in both modules (the module declaring the component with the `ngTemplateOutlet` + the module declaring the component with the ``)? If not, the `formControlName` will be nothing more than an html attribute, no directive... – Pieterjan Sep 18 '22 at 15:45

3 Answers3

1

You can't do that, I mean when you want to using formControlName, then you should have to put it inside [formGroup]="formGroup" section.

For an example, you only can do something like this code below:

<form [formGroup]="form">
  <ng-container *ngIf="true; then fooSection"></ng-container>

  <ng-template #fooSection>
  <input type="checkbox" formControlName="isEulaAccepted">
  <ng-template>
</form>

Update: Or you can do this, if you want to put outside [formGroup] section.

In your typescript, you can do add this:

public get isEulaAccepted(): FormControl {
  return this.formGroup.get('isEulaAccepted') as FormControl;
}

Or if you only have one formControl, so you should not have to using formGroup, just using formControl like this:

public isEulaAccepted: FormControl = new FormControl(null);

And, in your html, you can use this one:

<ng-container *ngIf="true; then fooSection"></ng-container>

<ng-template #fooSection>
  <input type="checkbox" [formControl]="isEulaAccepted" />
  <ng-template></ng-template
></ng-template>

Now, it will working fine.

Titus Sutio Fanpula
  • 3,467
  • 4
  • 14
  • 33
1

Note: FormControlName directive injects the ControlContainer with @Host() parameter decorator, so if it is not found in the parent, it throws an error. But you can provide the ControlContainer for it in another way.

  • create a wrapper component in the same file as the main component such this
let group;

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  
})
export class AppComponent {
  name = 'Angular ' + VERSION.major;

  @ViewChild(FormGroupDirective, { static: true }) formGroup: FormGroupDirective 

  form = new FormGroup({
    isEulaAccepted: new FormControl(),
  });

  ngOnInit() {
    group = this.formGroup;
  }
}

@Component({
  selector: 'wrapper',
  template: `<ng-content></ng-content>`,
  providers: [
    {
      provide: ControlContainer,
      useFactory: (app: WrapperComponent) => {
        return group;
      },
      deps: [forwardRef(() => WrapperComponent)],
    },
  ],
})
export class WrapperComponent {}

and use the WrapperComponent in template such this

<form [formGroup]="form">
  <ng-template
    *ngTemplateOutlet="fooSection;"
  ></ng-template>
</form>

<ng-template #fooSection>
  <wrapper>
    <input type="checkbox" formControlName="isEulaAccepted" />
  </wrapper>
</ng-template>

{{ formGroup.value | json }}

This is the final solution

Mehdi Shakeri
  • 584
  • 3
  • 11
  • That's pretty clever, however, I've found that its easier to just use the `formGroup` directive multiple times - looks like Angular is OK with that :) – Slava Fomin II Sep 24 '22 at 15:33
0

I've found out that the most simple solution is to use the formGroup directive multiple times:

<form [formGroup]="form">
    <ng-container *ngIf="true; then fooSection"></ng-container>
</form>

<ng-template #fooSection>
  <ng-container [formGroup]="form">
    <input type="checkbox" formControlName="isEulaAccepted">
  </ng-container>
<ng-template>

Looks like Angular is OK with that. However, other answers is useful too when it comes to a more general solution.

Slava Fomin II
  • 26,865
  • 29
  • 124
  • 202