I'm trying to bind the model in angular template driven forms. I created a model class and using it to populate the input field.
HTML:
<div class="form-group col-md-2 col-12" [class.text- danger]="nameCode.invalid && nameCode.touched">
<label for="inputName" class="form-control-label"> Name</label>
<input type="text" class="form-control" [class.is-form-invalid]="nameCode.invalid && nameCode.touched" id="inputName" name="lotCode"[(ngModel)]="formModel.name" #nameCode="ngModel" aria-describedby="nameHelp" autocomplete="new-password" required>
<small id="nameHelp" class="text-danger" *ngIf="nameCode.invalid && nameCode.touched">Required</small>
Component:
export class AppComponent {
formModel: FormModel= new FormModel();
}
export class FormModel {
name: "abc"
}
https://stackblitz.com/edit/angular-yue9fe?file=src%2Fapp%2Fapp.component.ts
name: "abc" should be name= "abc" (or name: string = "abc"). Right now you're declaring type of name as "abc", which is not what you want.
You have bind the name as "abc" dataType. So if you want to bind your model with html you can define your formModel class like,
export class FormModel {
constructor(public name="abc"){}
}
Related
I've got a problem with input type date. I want to bind data from component. Here's my field:
<div class="col-md-6">
<label for="dateOfReport">Data zgłoszenia błędu:</label>
<input type="date" formControlName="dateOfReport" id="dateOfReport" class="form-control" [value]="report.dateOfReport | date:'dd.MM.yyyy'"> {{report.dateOfReport | date:'dd.MM.yyyy'}}
</div>
Here's how variable dateOfReport look:
new Date().toJSON().slice(0, 10)
Where I fail? {{ .. }} shows good date, but my field doesn't take it as default.
When using Reactive Forms, you should not bind data directly to the control, instead use setValue() or patchValue() of FormGroup.
I found an answer to bind the date to the input when creating a form using reactive forms.
I'm using Angular 11.
Here is the HTML
<div class="form-group">
<label for="dateOfReport">Report Date</label>
<input type="date" class="form-control" name="dateOfReport" id="dateOfReport" formControlName="dateOfReport" />
</div>
And here is my angular component
// ...other imports
import { formatDate } from "#angular/common";
// ...
//... creating the form
this.form = this.formBuilder.group({
dateOfReport: [formatDate(new Date(), "yyyy-MM-dd", "en"), [Validators.required]],
// other form fields
});
I am new to angular let say, i have reactive form like follow
ngOnInit() {
this.registerFormGroup = this.formBuilder.group({
email: [ '', Validators.compose([Validators.required, Validators.email])],
password: this.formBuilder.group({
first: [ '', Validators.required ],
second: [ '', Validators.required, ]
})
});
}
and my angular template looks like follow
<div class="container">
<form [formGroup]="registerFormGroup"
(ngFormSubmit)="registerUser(registerFormGroup.value)" novalidate>
<div class="form-group" >
<label for="email">Email</label>
<input type="email" formControlName="email" placeholder="Enter Email"
class="form-control">
</div>
<div *ngIf="!registerFormGroup.get('password').get('first').valid"
class="alert alert-danger">
</div>
<div class="form-group text-center">
<button type="submit" class="btn btn-success btn-lg"
[disabled]="!registerFormGroup.valid">Submit</button>
</div>
For example, email field has two validations such as required and email type validate so depends upon the error I have to display error message so in my template am using like
<div *ngIf="!registerFormGroup.get('email').valid && (registerFormGroup.get('email').touched)"
class="alert alert-danger">
</div>
Instead of adding same registerFormGroup.get('email') again and again i trying to create template expression like #emailForm="registerFormGroup.get('email')" in
<input type="email" formControlName="email" placeholder="Enter Email" class="form-control" #emailForm="registerFormGroup.get('email')">
so that i can use use like
<div *ngIf="!emailForm.valid" class="alert alert-danger">
</div>
but i am getting error like
compiler.es5.js:1690 Uncaught Error: Template parse errors:
There is no directive with "exportAs" set to "registerFormGroup.get('email')" ("l>
]#emailForm="registerFormGroup.get('email')">
what mistake i made??
You can create common function to access form like below:
validateFormControl(controName: string) {
let control = registerFormGroup.get(controName);
return control.invalid && control.touched;
}
In Templete use this call wherever you need, you just need to pass your control name in function, you can modify this function as per your need as well and you do not need to use form.get all the time. This makes your template more cleaner and performance efficient.
<div *ngIf="validateFormControl('email')"
class="alert alert-danger">
error message
</div>
<div *ngIf="validateFormControl('password')"
class="alert alert-danger">
error message
</div>
Create a method on the component that returns if the form is valid or not and return it in the component
checkError(){ // either you can pass the form from the template or use the component for decleration
return registerFormGroup.get('email').valid;
}
In the template call
<div *ngIf="checkError()" class="alert alert-danger">
// Always make sure not to use controls in the template it will result in AOT compilation error
</div>
try this :
component.html
<div class="container">
<form [formGroup]="registerFormGroup"
(ngFormSubmit)="registerUser(registerFormGroup.value)" novalidate>
<div class="form-group" [ngClass]="{'has-error':!registerFormGroup.controls['email'].valid}">
<label for="email">Email</label>
<input type="email" formControlName="email" placeholder="Enter Email"
class="form-control">
<p class="alert alert-danger" *ngIf="registerFormGroup.controls['email'].dirty && !registerFormGroup.controls['email'].valid">Invalid email address</p>
</div>
<div class="form-group text-center">
<button type="submit" class="btn btn-primary" [disabled]="!registerFormGroup.valid">Submit</button>
</div>
</form>
</div>
component.ts
import { FormGroup, FormBuilder, Validators } from '#angular/forms';
export class AppComponent implements OnInit {
registerFormGroup: any;
constructor(
private formBuilder: FormBuilder
) {}
ngOnInit() {
this.registerFormGroup = this.formBuilder.group({
email: [null , Validators.compose([Validators.required, Validators.email])]
});
}
}
Angular team strongly discourage from using functions outputs in templates due to change detection strategy. You might be interested in following solution, using ngForm directive:
<div class="container">
<form [formGroup]="registerFormGroup"
(ngFormSubmit)="registerUser(registerFormGroup.value)" novalidate>
<div class="form-group" >
<label for="email">Email</label>
<input type="email" formControlName="email" placeholder="Enter Email "
class="form-control" #email="ngForm">
</div>
<div *ngIf="email.invalid"
class="alert alert-danger">
</div>
It's still a hush to copy-paste it in template, but at least you might have direct reference to control via it's template variable. I personally go with controller getter function still, but I'm posting this answer for sake of answer completeness.
Although I agree with the accepted answer (you should have a dedicated method in your form component that will encapsulate the validation process)
sometimes you need a quick check in the template:
The trick is to expose the formGroup from your component and use it like so:
Template:
<input id="name" class="form-control"
formControlName="name" required
[class.is-invalid]="
f.name.invalid &&
(f.name.touched ||
f.name.touched.dirty)">
Component:
//easily access your form fields
get f() { return this.checkoutForm.controls; }
I was also looking for a more elegant solution than calling form.get multiple times. Here is what I come up with using ngif
<div class="col-sm-6" *ngIf="form.get('sponsor') as sponsor">
<input type="text" formControlName="sponsor" id="sponsor" class="form-control" name="sponsor"
[class.is-invalid]="sponsor.errors && sponsor.touched">
</div>
using the as feature on ngIf to create a template variable
I am just learning angular 2 and have a case where I am creating a form that will be a piece of equipment. One of the fields that are associated with the equipment is a building. When the user is editing or adding a new piece of equipment I want them to be presented with a drop-down with a list of the building they can assign the equipment to. The equipment-detail component is as follows:
export class EquipmentDetailComponent implements OnInit {
equipment: IEquipment;
equipmentForm: FormGroup;
buildingList: Ibuilding[];
constructor(private EquipmentService: EquipmentService,
private BuildingsService: BuildingsService) { }
ngOnInit() {
this.equipment = this.EquipmentService.getEquipmentDetail(1);
this.buildingList = this.BuildingsService.getBuildingList();
let id = new FormControl(this.equipment.id);
let unit = new FormControl(this.equipment.unit);
let active = new FormControl(this.equipment.active);
let building = new FormControl(this.equipment.building.id);
this.equipmentForm = new FormGroup({
id: id,
unit: unit,
active: active,
building: building
})
}
saveEquipment(formValues){
console.log(formValues);
}
onSelect(id){
this.equipment.building = this.BuildingsService.getBuildingDetail(id);
console.log(this.equipment.building)
}
}
The equipment-detail html is as follows:
<div class="col-md-2">
<form [formGroup]="equipmentForm" (ngSubmit)="saveEquipment(equipmentForm.value)" autocomplete="off" novalidate>
<div class="form-group">
<label form="unit">Unit:</label>
<input formControlName="unit" id="unit" type="text" class="form-control" placeholder="Unit...."/>
</div>
<div class="form-group">
<label form="active">Active:</label>
<input formControlName="active" id="active" type="checkbox" class="form-control"/>
</div>
<div class="form-group">
<label form="building">Building:</label>
<select class="form-control" formControlName="building" (change)="onSelect($event.target.value)">
<option *ngFor="let building of buildingList" value={{building.id}}>
{{building.buildingName}}
</option>
</select>
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>
</div>
When I click the dropdown option I get an object that represents the building that I selected. The result is
Object {id: 2, buildingName: "Building 2", active: true}
active: true
buildingName: "Building 2"
id: 2
When I click the save button and look at the data that is associated with the formcontrol the building is no longer an object it is just the buildingId.
Object {id: 1, unit: "Item 1", active: true, building: "2"}
active: true
building: "2"
id: 1
unit:"Item 1"
__proto__: Object
How do I get the main equipment object to be set with the building object and not just the building id?
It is because you had value attribute with building.id, that's why it is assigning building.id on option selection. You should consider changing your value attribute binding to ngValue with whole building object.
<select class="form-control" formControlName="building" (change)="onSelect($event.target.value)">
<option *ngFor="let building of buildingList" [ngValue]="building">
{{building.buildingName}}
</option>
</select>
I have a form field:
<div class="form-group">
<label>Name</label>
<input type="text" name="name" class="form-control" ng-model="abc.user.name" ng-focus="abc.setFocus('name')" required>
</div>
What I need to do is set add a class to the parent element, here <div class="form-group">, when the input has focus and remove it when the field loses focus.
I know how to do this in jQuery, but not in an Angular way. I have many form fields that need to behave like this, so I'm trying to avoid setting a variable and looking for that with an ng-class. I'd rather have some way for the field to simple act on its parent, which I can use the same method in every form field.
A directive is possibly the simplest generic approach if all you need to do is manipulate the dom.
<div class="form-group" focus-class="focused">
<label>Name</label>
<input name="name" class="form-control" ng-model="abc.user.name" required>
</div>
JS
angular.module('myApp').directive('focusClass', function(){
return {
link:function(scope, elem, attrs){
elem.find('input').on('focus', function(){
elem.toggleClass(attrs.focusClass);
}).on('blur', function(){
elem.toggleClass(attrs.focusClass);
});
}
}
});
You can perform this
<div class="form-group {{focusIsSet ? 'is-focused': ''}}">
<label>Name</label>
<input type="text" name="name" class="form-control" ng-model="abc.user.name" ng-focus="focusIsSet = true" ng-blur="focusIsSet = false" required>
</div>
Where $scope.focusIsSet a boolean property. So depends of its state you can manage classes in <div class="form-group"> with that expression {{focusIsSet ? 'is-focused': ''}}
You change it with ng-focus and ng-blur directives
UPDATE
I think you can hold states for each input with that way
<div class="form-group {{checkFocusState('abc.user.name') ? 'is-focused': ''}}">
<label>Name</label>
<input type="text" name="name" class="form-control" ng-model="abc.user.name" ng-focus="setFocus('abc.user.name')" ng-blur="setBlur('abc.user.name')" required>
</div>
</div>
JS code
var inputsFocusState = {};
$scope.checkFocusState = function(propertyPathName) {
if(inputsFocusState[propertyPathName] == true) {
return true;
}
return false
}
$scope.setBlur = function(propertyPathName) {
inputsFocusState[propertyPathName] = false;
}
$scope.setFocus = function(propertyPathName) {
inputsFocusState[propertyPathName] = true;
}
Otherwise, you can create each focus property for each input in html template
P.S. ng-class is good option too
P.S.S I had similar case, but forms were completely dynamic.
So I split each property in object like user.name = {value: 'john', buttons: [...], label: 'Name', //and much more}.
Also better to change 'user.name.path' to something like 'user-name-path'.
I'm creating an input text component with angular2. I need to add a class at this control if is valid and if it's required. This is the component:
import { Component, Input } from "#angular/core";
import { NgForm } from '#angular/forms';
#Component({
selector: "input-control",
template: `
<div [class.has-success]="required" class="form-group form-md-line-input form-md-floating-label">
<input [class.edited]="model[property]"
[(ngModel)]="model[property]"
[attr.ngControl]="property"
[name]="property"
type="text"
class="form-control"
id="{{property}}"
value=""
[attr.required]="required">
<label [attr.for]="property">{{label}}</label>
<span class="help-block">{{description}}</span>
</div>
`
})
export class InputControlComponent {
#Input()
model: any;
#Input()
property: string;
#Input()
label: string;
#Input()
description: string;
#Input()
required: boolean;
#Input()
form: NgForm;
}
In the first row of the template I set the "has-success" class if the input is required but I need to set it if it's valid too. Somethig like this:
[class.has-success]="required && form.controls[property].valid"
The html is this:
<form role="form" *ngIf="active" (ngSubmit)="onSubmit(databaseForm)" #databaseForm="ngForm">
<div class="form-body">
<div class="row">
<div class="col-md-6">
<input-control [model]="model" [property]="'code'" [form]="databaseForm" [label]="'#Localizer["Code"]'" [description]="'#Localizer["InsertCode"]'" [required]="true"></input-control>
</div>
<div class="col-md-6">
<input-control [model]="model" [property]="'description'" [form]="databaseForm" [label]="'#Localizer["Description"]'" [description]="'#Localizer["InsertDescription"]'"></input-control>
</div>
</div>
</div>
</form>
I think that you can't use the template-driven form a sub-component and make it be part of a form of the parent component without implementing a custom value accessor with Angular2 prior to version RC2.
See this question:
Angular 2 custom form input
With version RC2+, I think that it's possible out of the box like this:
<form #databaseForm="ngForm">
<input-control name="code" [ngModelOptions]="{name: 'code'}"
[(ngModel)]="model.code"/>
</form>