Form data not shown in component while using ng-template

886 views Asked by At

Iam trying to create a form which i can re-use by using ng-template. But somehow the data is not accessable in the component when I save the form and the function doAddMenuItem(postForm) is called.

Here is the form with the ngTemplateOutlet:

<form (submit)="doAddMenuItem(postForm)" #postForm="ngForm">
  <ng-template [ngTemplateOutlet]="MsgRef"></ng-template>
  <button class="button medium button__save">Save</button>
</form>

And the ng-template:

<ng-template #MsgRef>
  <ul class="list">
    <li>Titel:</li>
    <li><input type="text" name="title" [(ngModel)]="menuItemObj.title"></li>
  </ul>
  <ul class="list">
    <li>Symbol:</li>
    <li><input type="text" name="symbol" [(ngModel)]="menuItemObj.symbol"></li>
  </ul>
  <ul class="list">
    <li>Meer weten link:</li>
    <li><input type="text" name="more_info_link" [(ngModel)]="menuItemObj.more_info_link"></li>
  </ul>
  <ul class="list">
    <li>Bericht:</li>
    <li>
      <editor [init]="froalaOptions" name="text" [(ngModel)]="menuItemObj.text"></editor>
    </li>
  </ul>
</ng-template>

And the function in the component:

doAddMenuItem(formData) {
  console.log(formData.value);
}
1

There are 1 answers

0
John Malkoln On

It seems NgForm is able to track controls only in nested templates.

To make it work you need to move #MsgRef template within form tag.

<form (submit)="doAddMenuItem(postForm)" #postForm="ngForm">
  <ng-template [ngTemplateOutlet]="MsgRef"></ng-template>
  <button class="button medium button__save">Save</button>

  <ng-template #MsgRef>...</ng-template>
</form>

To make your controls really reusable I propose you to create custom form control by implementing ControlValueAccessor interface. So you will be able to use this custom control in any form:

<form (submit)="doAddMenuItem(postForm)" #postForm="ngForm">
  <app-menu-item-control name="menuItem" [(ngModel)]="menuItem"></app-menu-item-control>
  <button class="button medium button__save">Save</button>
</form>

Model:

export interface MenuItem {
    title: string;
    symbol: string;
    moreInfoLink: string;
    text: string;
}

MenuItem form control:

@Component({
    selector: 'app-menu-item-control',
    template: `
        <ul class="list">
            <li>Titel:</li>
            <li><input type="text" name="title" [(ngModel)]="menuItem.title" (ngModelChange)="onModelChange()"></li>
        </ul>
        <ul class="list">
            <li>Symbol:</li>
            <li><input type="text" name="symbol" [(ngModel)]="menuItem.symbol" (ngModelChange)="onModelChange()"></li>
        </ul>
        <ul class="list">
            <li>Meer weten link:</li>
            <li><input type="text" name="moreInfoLink" [(ngModel)]="menuItem.moreInfoLink" (ngModelChange)="onModelChange()"></li>
        </ul>
        <ul class="list">
            <li>Bericht:</li>
            <li>
                <textarea name="text" [(ngModel)]="menuItem.text" (ngModelChange)="onModelChange()"></textarea>
            </li>
        </ul>
    `,
    providers: [
        {
            provide: NG_VALUE_ACCESSOR,
            useExisting: forwardRef(() => MenuItemControlComponent),
            multi: true,
        },
    ],
})
export class MenuItemControlComponent implements ControlValueAccessor {

    menuItem: MenuItem = { moreInfoLink: '', symbol: '', text: '', title: '', };

    onChange: (item: MenuItem) => void;

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

    registerOnTouched(fn: any): void {}

    writeValue(item: MenuItem): void { this.menuItem = { ...item }; }

    onModelChange(): void {
        this.onChange(this.menuItem);
    }
}