Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular ng-template with form-group

I was hoping to create a component that serves two purposes because the code behind is the same.

So I decided to use ng-template.

The first part is a modal popup and the html looks like this:

<div class="modal fade" #modal="mdbModal" tabindex="-1" role="dialog" aria-labelledby="myBasicModalLabel"
    aria-hidden="true" mdbModal ng-if="!static && saveForm">
    <div class="modal-dialog">
        <div class="modal-content">
            <form class="card" [formGroup]="saveForm" (ngSubmit)="onSubmit()">
                <div class="modal-header">
                    <h4 class="modal-title pull-left">Are you sure?</h4>
                    <button type="button" class="close pull-right" aria-label="Close" (click)="modal.hide()">
                        <span aria-hidden="true">&times;</span>
                    </button>
                </div>
                <div class="modal-body">
                    <div *ngTemplateOutlet="form"></div>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-default" (click)="modal.hide()">Cancel</button>
                    <button type="submit" class="btn btn-primary" (click)="modal.hide()">Save</button>
                </div>
            </form>
        </div>
    </div>
</div>

The second part is the static form, which looks like this:

<form class="card" [formGroup]="saveForm" (ngSubmit)="onSubmit()" [ngClass]="{ 'mb-0': modal }"
    *ngIf="static && saveForm">
    <div class="card-header">
        <h3 class="card-title">{{editing ? 'Update': 'Create'}} your filter</h3>
    </div>
    <div class="card-body p-6">
        <div *ngTemplateOutlet="form"></div>
    </div>
    <div class="card-footer text-right">
        <div class="d-flex">
            <a routerLink="/feeds" class="btn btn-link" *ngIf="!modal">Back</a>
            <button type="submit" class="btn btn-primary ml-auto">Save</button>
        </div>
    </div>
</form>

and the template they share looks like this:

<ng-template #form>
    <div class="form-group">
        <label class="form-label" for="name">Name</label>
        <input type="text" class="form-control" formControlName="name" placeholder="Enter the filter name"
            [ngClass]="{ 'is-invalid': submitted && f.name.errors }" />
        <div *ngIf="submitted && f.name.errors" class="invalid-feedback">
            <div *ngIf="f.name.errors.required">
                Name is required
            </div>
        </div>
    </div>
    <div class="form-group">
        <label class="form-label" for="name">Field name</label>
        <input type="text" class="form-control" formControlName="fieldName" placeholder="Enter the field name"
            [ngClass]="{ 'is-invalid': submitted && f.fieldName.errors }" />
        <div *ngIf="submitted && f.fieldName.errors" class="invalid-feedback">
            <div *ngIf="f.fieldName.errors.required">
                Field name is required
            </div>
        </div>
    </div>
    <div class="form-group">
        <label class="form-label" for="image">Operator</label>
        <select class="form-control" formControlName="filterOperator"
            [ngClass]="{ 'is-invalid': submitted && f.filterOperator.errors }">
            <option value="8">Between</option>
            <option value="2">Equal</option>
            <option value="4">Greater than</option>
            <option value="6">Less than</option>
            <option value="0">Like</option>
            <option value="9">Not between</option>
            <option value="3">Not equal</option>
            <option value="5">Not greater than</option>
            <option value="7">Not less than</option>
            <option value="1">Not like</option>
            <option value="10">Regular expression</option>
        </select>
        <div *ngIf="submitted && f.filterOperator.errors" class="invalid-feedback">
            <div *ngIf="f.filterOperator.errors.required">
                Operator is required
            </div>
        </div>
    </div>
    <div class="form-group">
        <label class="form-label" for="name">Expression</label>
        <input type="text" class="form-control" formControlName="expression" placeholder="Enter the expression"
            [ngClass]="{ 'is-invalid': submitted && f.expression.errors }" />
        <div *ngIf="submitted && f.expression.errors" class="invalid-feedback">
            <div *ngIf="f.expression.errors.required">
                Expression is required
            </div>
        </div>
    </div>
</ng-template>

I was hoping this would just work, but I get an error stating:

formControlName must be used with a parent formGroup directive. You'll want to add a formGroup directive and pass it an existing FormGroup instance (you can create one in your class).

Which is annoying. Does anyone know how I can solve this issue?

here is the entire html:

<div class="modal fade" #modal="mdbModal" tabindex="-1" role="dialog" aria-labelledby="myBasicModalLabel"
    aria-hidden="true" mdbModal ng-if="!static && saveForm">
    <div class="modal-dialog">
        <div class="modal-content">
            <form class="card" [formGroup]="saveForm" (ngSubmit)="onSubmit()">
                <div class="modal-header">
                    <h4 class="modal-title pull-left">Are you sure?</h4>
                    <button type="button" class="close pull-right" aria-label="Close" (click)="modal.hide()">
                        <span aria-hidden="true">&times;</span>
                    </button>
                </div>
                <div class="modal-body">
                    <div *ngTemplateOutlet="form"></div>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-default" (click)="modal.hide()">Cancel</button>
                    <button type="submit" class="btn btn-primary" (click)="modal.hide()">Save</button>
                </div>
            </form>
        </div>
    </div>
</div>

<form class="card" [formGroup]="saveForm" (ngSubmit)="onSubmit()" [ngClass]="{ 'mb-0': modal }"
    *ngIf="static && saveForm">
    <div class="card-header">
        <h3 class="card-title">{{editing ? 'Update': 'Create'}} your filter</h3>
    </div>
    <div class="card-body p-6">
        <div *ngTemplateOutlet="form"></div>
    </div>
    <div class="card-footer text-right">
        <div class="d-flex">
            <a routerLink="/feeds" class="btn btn-link" *ngIf="!modal">Back</a>
            <button type="submit" class="btn btn-primary ml-auto">Save</button>
        </div>
    </div>
</form>

<ng-template #form>
    <div class="form-group">
        <label class="form-label" for="name">Name</label>
        <input type="text" class="form-control" formControlName="name" placeholder="Enter the filter name"
            [ngClass]="{ 'is-invalid': submitted && f.name.errors }" />
        <div *ngIf="submitted && f.name.errors" class="invalid-feedback">
            <div *ngIf="f.name.errors.required">
                Name is required
            </div>
        </div>
    </div>
    <div class="form-group">
        <label class="form-label" for="name">Field name</label>
        <input type="text" class="form-control" formControlName="fieldName" placeholder="Enter the field name"
            [ngClass]="{ 'is-invalid': submitted && f.fieldName.errors }" />
        <div *ngIf="submitted && f.fieldName.errors" class="invalid-feedback">
            <div *ngIf="f.fieldName.errors.required">
                Field name is required
            </div>
        </div>
    </div>
    <div class="form-group">
        <label class="form-label" for="image">Operator</label>
        <select class="form-control" formControlName="filterOperator"
            [ngClass]="{ 'is-invalid': submitted && f.filterOperator.errors }">
            <option value="8">Between</option>
            <option value="2">Equal</option>
            <option value="4">Greater than</option>
            <option value="6">Less than</option>
            <option value="0">Like</option>
            <option value="9">Not between</option>
            <option value="3">Not equal</option>
            <option value="5">Not greater than</option>
            <option value="7">Not less than</option>
            <option value="1">Not like</option>
            <option value="10">Regular expression</option>
        </select>
        <div *ngIf="submitted && f.filterOperator.errors" class="invalid-feedback">
            <div *ngIf="f.filterOperator.errors.required">
                Operator is required
            </div>
        </div>
    </div>
    <div class="form-group">
        <label class="form-label" for="name">Expression</label>
        <input type="text" class="form-control" formControlName="expression" placeholder="Enter the expression"
            [ngClass]="{ 'is-invalid': submitted && f.expression.errors }" />
        <div *ngIf="submitted && f.expression.errors" class="invalid-feedback">
            <div *ngIf="f.expression.errors.required">
                Expression is required
            </div>
        </div>
    </div>
</ng-template>
like image 711
r3plica Avatar asked Sep 18 '19 11:09

r3plica


1 Answers

You should pass the formGroup to the template:

<ng-template #form let-form="form">
  <div [formGroup]="form">
  ...
  </div>
</ng-template>

And then call it this way:

<ng-container *ngTemplateOutlet="form; context: {form: saveForm}"></ng-container>
like image 159
tano Avatar answered Oct 17 '22 03:10

tano