32

Is it not possible to have form input elements within an ng-content and have that "connect" to the ngForm instance of the parent component?

Take this basic template for a parent component:

<form (ngSubmit)="onSubmit(editForm)" #editForm="ngForm" novalidate>               
<ng-content></ng-content>
<button type="submit">Submit</button>
</form>

Then inside the child component, which is put inside "ng-content", something like this:

<input type="text" [(ngModel)]="user.firstName" #firstName="ngModel" name="firstName" required minlength="2">

On submit of the parent form, the child controls are not available, which also means that dirty/validation of whatever is in the child component is not reflected on the parent form.

What is missing here?

SondreB
  • 808
  • 7
  • 14
  • I'm pretty sure this won't work. The element is just displayed within the child component but it's still a child of the parent element. – Günter Zöchbauer Nov 28 '16 at 11:34
  • @GünterZöchbauer Is there any way to hook up the child input fields with the form (ngForm) in the parent component? With ReactiveForms I can populate the parent FormGroup, and use [formGroup] on the child component, but impossible with template driven forms? – SondreB Nov 28 '16 at 11:42
  • That should work with template driven forms as well. Haven't done in in a while. – Günter Zöchbauer Nov 28 '16 at 11:43

1 Answers1

33

There is a good chance that you have come up with another solution at this point but I just figured out a way to do this. Hopefully it will help you or someone else.

import { NgModel } from '@angular/forms';
import { Component, ContentChildren, ViewChild, QueryList, AfterViewInit } from '@angular/core';

@Component({
  selector: 'my-custom-form',
  template: `
    <form (ngSubmit)="onSubmit(editForm)" #editForm="ngForm" novalidate>               
      <ng-content></ng-content>
      <button type="submit">Submit</button>
    </form>
  `,
})
export class MyCustomFormComponent implements AfterViewInit {
  @ContentChildren(NgModel) public models: QueryList<NgModel>;
  @ViewChild(NgForm) public form: NgForm;

  public ngAfterViewInit(): void {
    let ngContentModels = this.models.toArray();
    ngContentModels.forEach((model) => {
      this.form.addControl(model);
    });
  }

  public onSubmit(editForm: any): void {
    console.log(editForm);
  }
}

Then you can use it in your template like this:

<my-custom-form>
  <input name="projectedInput" ngModel>
</my-custom-form>

When you submit the form, you will see that the projectedInput form control is added to the NgForm.

Note: I only tried adding the projected inputs from the AfterViewInit lifecycle hook. It may work earlier, I'm not sure. There also may be some issues with doing this that I'm not aware of. YMMV.

instantaphex
  • 981
  • 1
  • 9
  • 20
  • 3
    If you specify `{descendants: true}` to `ContentChildren` too, then it'll grab descendants too and you don't need to have inputs as direct children :) – Carlos H Romano Aug 31 '17 at 12:29
  • 1
    When I wrote this answer I didn't have any luck with that approach. I'm not sure if it was a bug or what but that was the first thing I tried. I haven't tried it since the late Angular 2 beta. – instantaphex Aug 31 '17 at 12:32
  • any official response or documentation on the above ? – Jek Jan 01 '18 at 17:12
  • @instantaphex when i submit my form (content-projection) the page reload. stackblitz: https://stackblitz.com/edit/angular-zqjxjs – daniel gi Dec 17 '18 at 09:23
  • @danielgi Make sure to add an (ngSubmit) handler to your form. Also make sure to import the FormsModule from @angular/forms. I've forked your stackblitz project to demonstrate. https://angular-znwbwk.stackblitz.io/? – instantaphex Feb 08 '19 at 13:44