Angular Material Form Control Example does not run - html

I am attempting to use an Angular Material FormControl to validate a control. In order to learn this control, I am using a code example from the Angular Material website (Input with error messages example at https://v8.material.angular.io/components/input/examples). I am looking at an example using v8.2.3 of Angular Material.
The ts code for the example is below:
import {Component} from '#angular/core';
import {FormControl, Validators} from '#angular/forms';
/**
* #title Input with error messages
*/
#Component({
selector: 'input-errors-example',
templateUrl: 'input-errors-example.html',
styleUrls: ['input-errors-example.css'],
})
export class InputErrorsExample {
emailFormControl = new FormControl('', [
Validators.required,
Validators.email,
]);
}
and the HTML for the example is below:
<form class="example-form">
<mat-form-field class="example-full-width">
<input matInput placeholder="Email" [formControl]="emailFormControl">
<mat-error *ngIf="emailFormControl.hasError('email') && !emailFormControl.hasError('required')">
Please enter a valid email address
</mat-error>
<mat-error *ngIf="emailFormControl.hasError('required')">
Email is <strong>required</strong>
</mat-error>
</mat-form-field>
</form>
There isn't anything unusual about the contents of the CSS file.
The problem is that the example will not compile and run the program as written. When attempting to compile the example code, I get the following failure:
Can't bind to 'formControl' since it isn't a known property of 'input'.
Obviously, something in this example is either missing or in error. The example does not name any other dependent classes or modules that would support [formControl] as a property, there are no variables named formControl named in the code, and I have made no changes to the example code that would make anything not work.
Has anyone used this form control as it is used in this example? If so, what am I missing here? How did you get this mechanism to work?

You have to import ReactiveFormsModule in the module which contains this component. Maybe you missed that.

Did you import the Forms Module in your app module?

Related

Two Way Binding with Mat-Error causes ngModelChange to fire excessively

I have a mat input field in which I bind two-way data with ngModel, I would also like to incorporate mat-error with validation using a formControl.
<mat-form-field [formGroup]="myForm">
<input matInput formControlName="myFormName" autocomplete="off"
(ngModelChange)="dataChanged(myValue)" [ngModel]="myValue">
<mat-error>
Error! Value outside <strong>{{minVal}}</strong>
and <strong>{{maxVal}}</strong>.
</mat-error>
</mat-form-field>
However this gives me an issue where ngModelChange fires multiple times when the component is initialized and the ngModel value myValue is populated with data through HTTP calls in a service. Something about mixing template driven and reactive forms?
Essentially, what I am trying to achieve is two way bind data to an input while also having mat-errors without having ngModelChange fire unnecessarily just because I populate the input with data on init.
You would have a form that looks something like this:
import { Component } from "#angular/core";
import {
FormGroup,
FormControl,
Validators
} from "#angular/forms";
/** #title Input with a custom ErrorStateMatcher */
#Component({
selector: "input-error-state-matcher-example",
templateUrl: "./input-error-state-matcher-example.html",
styleUrls: ["./input-error-state-matcher-example.css"]
})
export class InputErrorStateMatcherExample {
minVal = 8;
maxVal = 20;
myForm = new FormGroup({
myFormName: new FormControl("", [
Validators.minLength(this.minVal),
Validators.maxLength(this.maxVal)
])
});
}
Here, you've defined your minLength and maxLength validators.
You've also binded the form with your template using [formGroup] and formControlName directives.
So everything will be taken care of by Angular for you.
You just have to access the errors property on the myForm.controls['myFormName'] control. Something like this:
<mat-form-field [formGroup]="myForm">
<input matInput formControlName="myFormName" autocomplete="off">
<mat-error *ngIf="myForm.controls['myFormName'].errors">
Error! Value outside <strong>{{minVal}}</strong>
and <strong>{{maxVal}}</strong>.
</mat-error>
</mat-form-field>
Here's a Working Demo for your ref.

How to prevent html injection in Angular 5

I want to prevent users to enter html injection to textbox. I have researched some example but they are generally about allowing html tags through pipes and saying angular automatically sanitizes html tags. But in my example when I enter <script>alert('blabla')</script> to text box, it is accepted and registered to db like this..
How can I prevent it ?
My template code is:
<div fxLayout="row">
<mat-form-field fxFlex="20">
<input matInput name="label" [(ngModel)]="product.label" placeholder="Label"
required>
</mat-form-field>
</div>
and my ts file is:
import { Product } from '../../../';
#Component({
selector: '....',
templateUrl: '....',
styleUrls: ['....']
})
export class ProductEditComponent implements OnInit {
product: Product = <Product>{};
constructor(some services etc.) {
}
ngOnInit() {
//some code
}
Note again: I want to prevent entering html script injection, not allowing it with bypass or pipe...
you could use DomSanitizer for that
import { DomSanitizer } from "#angular/platform-browser"
import { SecurityContext } from "#angular/core";
constructor(private sanit:DomSanitizer){
var dom = this.sanitizer.sanitize(SecurityContext.HTML, "<script> alert('HELLO'); </script>");
console.log(dom);
}
if it returns null, then it was an issue with html,else it returns passed html

Two way data binding without ngmodel directive angular

I see in console ngmodel is deprecated and will be removed on angular 7. and i have a directive using it for do a two way databinding, how i can do it wihout [(ngmodel)]?
import {Directive, ElementRef, HostListener} from '#angular/core';
#Directive({
selector: '[onlyFloat]'
})
export class OnlyFloatDirective {
private regex: RegExp = new RegExp(/^-?[0-9]+(\.[0-9]*){0,1}$/g);
private specialKeys: Array<string> = [ 'Backspace', 'Tab', 'End', 'Home', '-' ];
constructor(private el: ElementRef) {
}
#HostListener('keydown', [ '$event' ])
onKeyDown(event: KeyboardEvent) {
if (this.specialKeys.indexOf(event.key) !== -1) {
return;
}
let current: string = this.el.nativeElement.value;
let next: string = current.concat(event.key);
if (next && !String(next).match(this.regex)) {
event.preventDefault();
}
}
}
HTML:
<div class="ui-g-12 ui-md-6">
<label >Valor da Venda</label><br>
<input type="text" pInputText onlyFloat [(ngModel)]="produtoAgilForm.controls['ValorVenda'].value" placeholder="Valor Venda" formControlName="ValorVenda">
</div>
Just for clarity, note that ngModel is only deprecated when used with Reactive forms. That has been the recommendation for a while ... but now it is deprecated in v6 and will be removed in v7.
See this part of the docs for more information: https://angular.io/api/forms/FormControlName
And in case that part of the docs is removed when ngModel is removed:
Support for using the ngModel input property and ngModelChange event
with reactive form directives has been deprecated in Angular v6 and
will be removed in Angular v7.
Now deprecated:
<form [formGroup]="form"> <input
formControlName="first" [(ngModel)]="value"> </form>
this.value = 'some value';
This has been deprecated for a few reasons.
First, developers have found this pattern confusing. It seems like the
actual ngModel directive is being used, but in fact it's an
input/output property named ngModel on the reactive form directive
that simply approximates (some of) its behavior. Specifically, it
allows getting/setting the value and intercepting value events.
However, some of ngModel's other features - like delaying updates
withngModelOptions or exporting the directive - simply don't work,
which has understandably caused some confusion.
Here is the recommended change per the above referenced docs:
To update your code before v7, you'll want to decide whether to stick
with reactive form directives (and get/set values using reactive forms
patterns) or switch over to template-driven directives.
After (choice 1 - use reactive forms):
<form [formGroup]="form">
<input formControlName="first">
</form>
this.form.get('first').setValue('some value');
And to answer your question ... you shouldn't need ngModel here. Your binding should be handled by your use of the formControlName. And to set the value, use the above shown code.
Is your directive not working? If not, could you provide a stackblitz to demonstrate?

Angular 5 FormGroup reset doesn't reset validators

I have a form on my page and when I call FormGroup.reset() it sets the forms class to ng-pristine ng-untouched but FormControl.hasError(...) still returns truthy. What am I doing wrong here?
Template
<form [formGroup]="myForm" (ngSubmit)="submitForm(myForm)">
<mat-form-field>
<input matInput formControlName="email" />
<mat-error *ngIf="email.hasError('required')">
Email is a required feild
</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput type="password" formControlName="password" />
<mat-error *ngIf="password.hasError('required')">
Password is a required feild
</mat-error>
</mat-form-field>
<button type="submit">Login</button>
</form>
Component
export class MyComponent {
private myForm: FormGroup;
private email: FormControl = new FormContorl('', Validators.required);
private password: FormControl = new FormControl('', Validators.required);
constructor(
private formBuilder: FormBuilder
) {
this.myForm = formBuilder.group({
email: this.email,
password: this.password
});
}
private submitForm(formData: any): void {
this.myForm.reset();
}
}
Plunker
https://embed.plnkr.co/Hlivn4/
It (FormGroup) behaves correctly. Your form requires username and password, thus when you reset the form it should be invalid (i.e. form with no username/password is not valid).
If I understand correctly, your issue here is why the red errors are not there at the first time you load the page (where the form is ALSO invalid) but pop up when you click the button. This issue is particularly prominent when you're using Material.
AFAIK, <mat-error> check the validity of FormGroupDirective, not FormGroup, and resetting FormGroup does not reset FormGroupDirective. It's a bit inconvenient, but to clear <mat-error> you would need to reset FormGroupDirective as well.
To do that, in your template, define a variable as such:
<form [formGroup]="myForm" #formDirective="ngForm"
(ngSubmit)="submitForm(myForm, formDirective)">
And in your component class, call formDirective.resetForm():
private submitForm(formData: any, formDirective: FormGroupDirective): void {
formDirective.resetForm();
this.myForm.reset();
}
GitHub issue: https://github.com/angular/material2/issues/4190
In addition to Harry Ninh's solution, if you'd like to access the formDirective in your component without having to select a form button, then:
Template:
<form
...
#formDirective="ngForm"
>
Component:
import { ViewChild, ... } from '#angular/core';
import { NgForm, ... } from '#angular/forms';
export class MyComponent {
...
#ViewChild('formDirective') private formDirective: NgForm;
constructor(... )
private someFunction(): void {
...
formDirective.resetForm();
}
}
After reading the comments this is the correct approach
// you can put this method in a module and reuse it as needed
resetForm(form: FormGroup) {
form.reset();
Object.keys(form.controls).forEach(key => {
form.get(key).setErrors(null) ;
});
}
There was no need to call form.clearValidators()
Add the property -
#ViewChild(FormGroupDirective) formGroupDirective: FormGroupDirective;
and use this instead of this.myForm.reset();
this.formGroupDirective.resetForm();
This will reset the error display and also do the job of form.reset(). But the form, along with the fields, will still show ng-invalid class
Check this answer for more details - https://stackoverflow.com/a/56518781/9262627
The below solution works for me when trying to reset specific form controller in form group -
this.myForm.get('formCtrlName').reset();
this.myForm.get('formCtrlName').setValidators([Validators.required, Validators.maxLength(45), Validators.minLength(4), Validators.pattern(environment.USER_NAME_REGEX)]);
this.myForm.get('formCtrlName').updateValueAndValidity();
form.reset() won't work on custom form control like Angular Material that's why the function is not working as expected.
My workaround for this is something like this
this.form.reset();
for (let control in this.form.controls) {
this.form.controls[control].setErrors(null);
}
this.form.reset() the issue with this is that it will reset your formcontrol values but not the errors so you need to reset them individually by this line of code
for (let control in this.form.controls) {
this.form.controls[control].setErrors(null);
}
With this you don't need to use FormGroupDirective which is a cleaner solution for me.
Github issue: https://github.com/angular/angular/issues/15741
I found that after calling resetForm() and reset(), submitted was not being reset and remained as true, causing error messages to display. This solution worked for me. I found it while looking for a solution to calling select() and focus() on an input tag, which also wasn't working as expected. Just wrap your lines in a setTimeout(). I think setTimeout is forcing Angular to detect changes, but I could be wrong. It's a bit of a hack, but does the trick.
<form [formGroup]="myFormGroup" #myForm="ngForm">
…
<button mat-raised-button (click)="submitForm()">
</form>
submitForm() {
…
setTimeout(() => {
this.myForm.resetForm();
this.myFormGroup.reset();
}, 0);
}
resetForm() {
this.myFormGroup.reset();
this.myFormGroup.controls.food.setErrors(null);
this.myFormGroup.updateValueAndValidity();
}
UPDATE FROM 2021 - ANGULAR 11.2
The fact to use a [formGroup]="form and a #formDirective="ngForm" directly into the HTML function is not a good practise. Or maybe you would prefer to use #ViewChild, and do it directly from your .ts. Actually, the problem don't come from Angular, but Material.
If you take a look at their GitHub, you will see this :
/** Provider that defines how form controls behave with regards to displaying error messages. */
#Injectable({providedIn: 'root'})
export class ErrorStateMatcher {
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
return !!(control && control.invalid && (control.touched || (form && form.submitted)));
}
}
The form will keep its submitted state. So you just have to delete the last part of the function.
Here is my solution (tested and working). I have a Material Module, into I've implemented this :
export class ShowOnInvalidTouchedErrorStateMatcher implements ErrorStateMatcher {
isErrorState(control: FormControl): boolean {
return !!(control && control.invalid && control.touched);
}
}
#NgModule({
providers: [
{
provide: ErrorStateMatcher, useClass: ShowOnInvalidTouchedErrorStateMatcher
}
],
exports: [
MatSnackBarModule,
MatTabsModule,
...
]
});
If you want to use this ErrorStateMatcher on only one form, it's possible. Please see this Material example. This is the same principle.
I had no luck with resetting the form directive. But You can also change the input state to pending to do that as well.
this.myForm.get("email").reset();
this.myForm.get("password").reset();
To anyone whom this may help, I am running Angular 9.1.9 and I didn't want to reset the form/controls just the overall validity of the form so I just ran:
this.registerForm.setErrors(null);
...where registerForm: FormGroup and that reset the form errors, leading to:
this.registerForm.valid
...returning true.
The same can be done for controls:
this.registerForm.get('email').setErrors(null)
As soon as the form is touched, these errors are re-evaluated anyway so if that's not good enough, you may need to have a boolean flag to further pin-down exactly when you want to start showing/hiding error UI.
I did not need to touch the directive in my case.
I was also having the same set of problems. My problem was that i was using mat-form-field and formGroup. After resetting the form submitted flag was not resetting.
So, the solution that worked for me is, putting a directive of ngForm along with formGroup and passing onSubmit(form). Added
#ViewChild('form') form;
in component and then I used
this.form.resetForm();
Nothing from above worked for me (Angular 7.2, Angular Material 7.3.7).
Try to pass with submit method an event on view:
<form [formGroup]="group" (ngSubmit)="onSubmit($event)">
<!-- your form here -->
</form>
Then use it to reset currentTarget and your form afterwards:
public onSubmit(event): void {
// your code here
event.currentTarget.reset()
this.group.reset()
}
Simple fix: use button with type="reset" and function submitForm() together
<form [formGroup]="MyForm" (ngSubmit)="submitForm()">
<input formControlName="Name">
<mat-error>
<span *ngIf="!tunersForm.get('Name').value && tunersForm.get('Name').touched"></span>
</mat-error>
<button type="reset" [disabled]="!MyForm.valid" (click)="submitForm()">Save</button>
</form>

Angular 2 Form "Cannot Find Control"

I am trying to use Angular 2 Forms for validation, but when I try to add more than one control. It seems like it just gets ignored. I have followed many different guides to see how everyone else does it, but none of those ways seem to work.
What I have been doing is this in my template:
<form [formGroup]="form" novalidate (ngSubmit)="save(form.valid)">
<div class="row" id="message-wrapper">
<label>Message</label>
<small [hidden]="form.controls.message.valid || (form.controls.message.pristine && !submitted)">
Message is required (minimum 10 characters).
</small>
<textarea
class="textarea-scaled"
type="text"
[(ngModel)]="campaign.message"
formControlName="message"
placeholder="This will be sent out by supporters with a URL back to this campaign">
</textarea>
</div>
<div class="row" id="promo-wrapper">
<label>Promotion: </label>
<small [hidden]="form.controls.promotion.valid ||(form.controls.promotion.pristine && !submitted)">
Promotion is required and should be between 10 and 100 characters
</small>
<textarea
class="textarea-scaled"
type="text"
[(ngModel)]="campaign.promotion"
formControlName="promotion"
placeholder="What would you like to be sent out in promotional messages?">
</textarea>
</div>
</form>
Then in my component I do this:
form: FormGroup;
constructor(private builder: FormBuilder,
private _dataservice: DataService) {
this.form = builder.group({
"message": ['', [Validators.required, Validators.minLength(10)]],
"promotion": ['', [Validators.required, Validators.minLength(10)]]
});
}
But I keep getting a "Cannot find control 'promotion'" console error...
Any help will be appreciated!
This may not be the answer to the original question, but this may be useful if you jumped here from google.
You need to check these things.
You must have a "name" attribute for all the controls which has [ngModel]
If you exclude some fields from validation, then add [ngModelOptions]="{standalone: true}" (remember the first rule, still you need a "name")
Make sure you have formControlName attribute for the controls that you are going to validate. (remember the first rule)
I tried to create new FormGroup in my component.
I've imported ReactiveFormsModule from angular/forms and added to app.module.ts imports.
but I was getting Cannot find name 'FormGroup' and Cannot find name 'FormControl' errors
Here is my component
export class SignupFormComponent {
form1 = new FormGroup({
username: new FormControl(),
password: new FormControl()
});
}
Adding the below import statement in component resolved my issue.
import { FormGroup, FormControl } from '#angular/forms';
Not the answer to your question but Posting as this might help someone who faces same error.