Created
March 5, 2019 13:25
Using Dynamic Template-Driven Forms In Angular 7.2.7
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<form #petsForm="ngForm" (submit)="processForm( petsForm )"> | |
<h2> | |
Pets | |
</h2> | |
<ng-template ngFor let-pet [ngForOf]="form.pets" let-index="index" let-isLast="last"> | |
<!-- | |
NOTE: We are using the "nameControl" template variable to define our CSS | |
class. Each template variable is scoped to the template in which it was | |
defined; which means, each "nameControl" instance is scoped to the ngFor | |
loop-iteration of the given Pet model. | |
--> | |
<div | |
class="pet" | |
[class.pet--invalid]="( nameControl.touched && nameControl.invalid )"> | |
<!-- | |
Each form control has to have a unique "name" property so that it can be | |
registered with the parent NgForm instance (unless it is denoted as | |
"standalone"). As such, we are using attribute interpolation to give each | |
input a locally-unique name based on the model data (XXX_{{ pet.id }}). | |
--> | |
<select name="type_{{ pet.id }}" [(ngModel)]="pet.type"> | |
<option value="Dog">Dog</option> | |
<option value="Cat">Cat</option> | |
</select> | |
<!-- | |
NOTE: We are defining a "nameControl" template variable that will give us | |
access to the "NgModel" instance for this form input. We are then using | |
this reference to adjust the CSS class-list on the parent container. | |
--> | |
<input | |
#nameControl="ngModel" | |
type="text" | |
name="name_{{ pet.id }}" | |
[(ngModel)]="pet.name" | |
required | |
autofocus | |
size="20" | |
placeholder="Name..." | |
/> | |
<input | |
type="text" | |
name="age_{{ pet.id }}" | |
[(ngModel)]="pet.age" | |
size="10" | |
placeholder="Age..." | |
/> | |
<label for="isPastOn_{{ pet.id }}"> | |
<input | |
type="checkbox" | |
id="isPastOn_{{ pet.id }}" | |
name="isPastOn_{{ pet.id }}" | |
[(ngModel)]="pet.isPastOn" | |
(keydown.tab)="( ( isLast && addPet() ) || true )" | |
/> | |
Is pasted-on? | |
</label> | |
<a (click)="removePet( index )" title="Remove Pet" class="remove"> | |
× | |
</a> | |
</div> | |
</ng-template> | |
<p class="actions"> | |
<a (click)="addPet()">Add Another Pet</a> | |
</p> | |
<!-- | |
Since we are [implicitly] registering each form control with the parent NgForm | |
instance, the validity of the form will be an aggregation of the individual | |
control validity. As such, we can disable the form submission if the form looks | |
invalid as a whole. | |
--> | |
<button type="submit" [disabled]="( ! petsForm.form.valid )"> | |
Process Form | |
</button> | |
</form> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Import the core angular services. | |
import { Component } from "@angular/core"; | |
// ----------------------------------------------------------------------------------- // | |
// ----------------------------------------------------------------------------------- // | |
interface Pet { | |
id: number; | |
type: string; | |
name: string; | |
age: string; // NOTE: This is a String because it is an open-ended form value. | |
isPastOn: boolean; | |
} | |
@Component({ | |
selector: "my-app", | |
styleUrls: [ "./app.component.less" ], | |
templateUrl: "./app.component.htm" | |
}) | |
export class AppComponent { | |
public form: { | |
pets: Pet[]; | |
}; | |
// I initialize the app component. | |
constructor() { | |
this.form = { | |
pets: [] | |
}; | |
// Add an initial pet form-entry. | |
this.addPet(); | |
} | |
// --- | |
// PUBLIC METHODS. | |
// --- | |
// I add a new pet record to the form-model. | |
public addPet() : void { | |
// CAUTION: When we output the form controls, we need to provide a unique name | |
// for each input (so that it can be registered with the parent NgForm). For the | |
// sake of this demo, we're going to use the current TIMPESTAMP (Date.now()) as a | |
// hook into something unique about this model. | |
this.form.pets.push({ | |
id: Date.now(), // <--- uniqueness hook. | |
type: "Dog", | |
name: "", | |
age: "", | |
isPastOn: false | |
}); | |
} | |
// I process the form-model. | |
public processForm( form: any ) : void { | |
console.warn( "Handling form submission!" ); | |
console.group( "Form Data" ); | |
console.log( this.form.pets ); | |
console.groupEnd(); | |
console.group( "Form Model" ); | |
console.log( form ); | |
console.groupEnd(); | |
} | |
// I remove the pet at the given index. | |
public removePet( index: number ) : void { | |
this.form.pets.splice( index, 1 ); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment