Template Driven Forms required allows empty string - Angular 7/8 - html

I'm having an issue with my form validation allowing empty strings. required works but allows the user to click space. the solution I found online works which is to use ng-pattern pattern=".*[^ ].*" but for some reason it does nor work with textarea. if the user copys and paste a an html page ng pattern throws an error. the weird thing is when I change the textarea to an input field it works fine. What's the correct solution to fix this. it allows the user to paste certain things but I can't pinpoint what is causing the error when I copied my whole html page and pasted it the textarea for testing. it seems like the spacing inbetween is causing it. StackBlitz: https://stackblitz.com/edit/angular-nhvgf1?file=src/app/hero-form/hero-form.component.html
this is what I pasted in the textarea which should not throw an error: <button type="submit" class="btn btn-success" [disabled]="!heroForm.form.valid">Submit</button>
<button type="button" class="btn btn-default" (click)="newHero(); heroForm.reset()">New Hero</button>
<i>with</i> reset
<form (ngSubmit)="onSubmit()" #heroForm="ngForm">
<div class="form-group">
<label for="name">Name</label>
<textarea type="text" class="form-control" id="name"
required
pattern=".*[^ ].*"
[(ngModel)]="model.name" name="name"
#name="ngModel"></textarea>
<div [hidden]="name.valid || name.pristine"
class="alert alert-danger">
Name is required
</div>
</div>
<button type="submit" class="btn btn-success" [disabled]="!heroForm.form.valid">Submit</button>
<button type="button" class="btn btn-default" (click)="newHero(); heroForm.reset()">New Hero</button>
<i>with</i> reset
<button type="button" class="btn btn-default" (click)="newHero()">New Hero</button>
<i>without</i> reset
</form>

you can use this pattern for your purpose "(\s*[^\s]+\s*)+"

you can just create a custom validator and this reusable approach
export class StrictRequiredDirective implements Validator {
validator: ValidatorFn;
constructor() {
this.validate = this.isStrictRequired();
}
validate(c: FormControl) {
return this.validate(c);
}
isStrictRequired() :ValidatorFn{
return (c: AbstractControl) => {
console.log(c.value)
if (c.value && String(c.value).trim()) {
return null
} else {
return {strictRequired : true}
}
}
}
}
stackblitz demo ⚡⚡

Try to use this pattern \S+.*
It allows the user to use space BUT it won't be valid (if its only spaces):
ONLY SPACES IS NOT VALID IN THIS CASE!
This answer is only to match your initial request, but still I think that trimming this for the user can be a better User Experience.
To use a trimmer directive you can use this:
import { ElementRef, HostListener } from '#angular/core';
import { Output, EventEmitter, Renderer2 } from '#angular/core';
import { Directive, Input } from '#angular/core';
#Directive({
selector: '[forceTrim]'
})
export class TrimDirective {
#Output() ngModelChange = new EventEmitter();
constructor(
private _renderer: Renderer2,
private _elementRef: ElementRef) {
}
#HostListener("input", ["$event.target.value"])
handleInput(inputValue: any): void {
const valueToProcess = inputValue.trim();
this._renderer.setProperty(this._elementRef.nativeElement, "value", valueToProcess);
this.ngModelChange.emit(valueToProcess);
}
}
and in template use:
<input
type="text"
forceTrim
/>

An easy and reliable solution, is simply to add your form as a parameter, to onSubmit, and then do an additional check at the beginning of your onSubmit function.
HTML
<form (ngSubmit)="onSubmit(heroForm)" #heroForm="ngForm">
<div class="form-group">
<label for="name">Name</label>
<textarea type="text" class="form-control" id="name"
required
pattern=".*[^ ].*"
[(ngModel)]="model.name" name="name"
#name="ngModel"></textarea>
<div [hidden]="name.valid || name.pristine"
class="alert alert-danger">
Name is required
</div>
</div>
<button type="submit" class="btn btn-success" [disabled]="!heroForm.form.valid">Submit</button>
<button type="button" class="btn btn-default" (click)="newHero(); heroForm.reset()">New Hero</button>
<i>with</i> reset
<button type="button" class="btn btn-default" (click)="newHero()">New Hero</button>
<i>without</i> reset
</form>
TYPESCRIPT
import { NgForm } from '#angular/forms';
...
onSubmit(theForm: NgForm) {
if(theForm.controls.name.value.trim() == '') {handleErrors(theForm, 1); return;}
... the rest of your onSubmit Code.
}
handleErrors(theForm: NgForm, errorCode: number) {
switch (errorCode) {
case 1:
console.log("Empty textarea.");
theForm.controls.name.setError({Empty_Textarea: true});
return;
default:
console.log("Unhandled error");
return;
}
}

Related

Refresh input type file

I'm working on an Angular project.
When I import several documents I have the message "two documents". nothing problematic.
The problem arises when I press the delete button that I created.
It allows to empty my list but to display there is always written "two documents"
I wish I had that. like when we get to the page for the first time ("no file selected") :
How could I do to reload this input without reloading the page?
My code :
html :
<div class="form-group">
<label for="pj">Pièce jointe</label>
<div fxLayout="row wrap" fxLayoutAlign="start center">
<input type="file" name="pj" id="pj" (change)="onFileChange($event)" multiple>
<button type="button" (click)="clearFile()" class="btn btn-link">
<i class="fas fa-trash fa-lg"></i>
</button>
</div>
</div>
ts :
clearFile() {
this.message.files = null;
}
Thanks
If you use a reactive form, you can just call reset() on the form control.
component.html
<form [formGroup]="form">
<input type="file" multiple formControlName="files" />
<button type="button" (click)="clearFile()">
Delete
</button>
</form>
component.ts
form: FormGroup;
ngOnInit() {
this.form = new FormGroup({
files: new FormControl('')
});
}
clearFile() {
this.form.get('files').reset();
}
DEMO: https://stackblitz.com/edit/angular-huvm38
you can use ViewChild to access the input in your component. First, you need to add #someValue to your input so you can read it in the component:
<input #myInput type="file" name="pj" id="pj" (change)="onFileChange($event)" multiple>
Then in your component you need to import ViewChild from #angular/core:
import { ViewChild } from '#angular/core';
Then you use ViewChild to access the input from template:
// ng 8 #ViewChild('myInput', {static: false}) myInput: ElementRef;
#ViewChild('myInput') myInput: ElementRef;
Now you can use myInput to reset the selected file because it's a reference to input with #myInput, for example create method reset() that will be called on click event of your button:
reset() {
console.log(this.myInput.nativeElement.files);
this.myInput.nativeElement.value = "";
console.log(this.myInput.nativeElement.files);
}

How to fetch data from database to textbox typescript

How to fetch data from database to textbox typescript? My Get function is working it display on console but not on textbox.
export class ProfileComponent implements OnInit {
Profile: Profile = new Profile;
constructor(
private ProfileService: ProfileService,
private Security: Security){
}
ngOnInit() {
this.GetProfileByID(this.Security.UserID);
}
GetProfileByID(UserID: any) {
this.ProfileService.GetProfileByID(UserID).then(
(response) => {
console.table(response.Body)
}
)
}
}
Here's my html
`
<form #updateForm="ngForm">
<div class="input-container mb-3">
<div class="col-12">
<label>* Name</label>
<input name="name" #name="ngModel" id="TACName" type="text" [(ngModel)]="Profile.Name" pInputText required/>
<div class="errortxt" [hidden]="name.valid || name.pristine" class="alert alert-danger">
<div class="errortxt" [hidden]="!name.hasError('required')"> Name is Required!</div>
</div>
</div>
</div>
<button class="ui-button-raised submit-btn" [disabled]="!updateForm.valid || !Agreement" pButton label="SAVE" type="submit" (click)="Save(updateForm)"></button>
</form>
`
here's the result on console
According the output result in the console...
GetProfileByID(UserID: any) {
this.ProfileService.GetProfileByID(UserID).then(
(response) => {
this.Profile.Name = response.Body.Name;
}
)
}
There is a typo, you should use new Profile() instead of new Profile.
Really recommending to not use [(ngModel)]. Use FormGroup instead and then after receiving your response from service, use FormGroup.get(controlName: string).patchValue. That will fix your issue for sure.

Fill input fields through buttons in Angular

this is my first time working with Angular and I'm trying to create a form field that can be filled through buttons or writing on it.
The problem is that when I click on the buttons to fill de field and then send click on the send button in the function onSubmit() I just get '' in this.registerForm.value.disp
And when I write directly on the field and click on the send button, this.registerForm.value.disp show me correctly what I wrote.
I don't figure out what is happening here and how to fix it.
This is my instruction.component.ts
export class InstructionComponent implements OnInit {
constructor(
private formBuilder: FormBuilder,
private instructionService: InstructionService,
private location: Location) { }
registerForm: FormGroup;
loading = false;
dispPattern = "^DISPOSITIVO[1-4]$";
ngOnInit() {
this.registerForm = this.formBuilder.group({
disp: ['',Validators.pattern(this.dispPattern)]
});
}
onSubmit() {
var dispInput = this.registerForm.value.disp
console.log(dispInput);
if (this.registerForm.invalid) {
console.log('bad');
return;
}
console.log(this.registerForm.value);
this.instruccionService.create({text: dispInput})
....
and my instruction.component.html
<form [formGroup]="registerForm" (ngSubmit)="onSubmit()" >
<div class="container-fluid h-100">
<div class="row">
<div class="col-3">
<button class="btn btn-primary btn-block"
onClick="autoFill1('DISPOSITIVO2'); return false;"
role="button">Dispositivo 2</button>
<button class="btn btn-primary btn-block"
onClick="autoFill1('DISPOSITIVO3'); return false;"
role="button">Dispositivo 3</button>
<div class="form-group">
<input type="text" formControlName="disp" name="disp" id="disp" class="form-control" placeholder="Elegir dispositivo" required>
</div>
</div>
</div>
</div>
<input class="btn btn-primary" type="submit" value="Send">
</form>
and autoFill1() is a script to fill the field
function autoFill1(v1){
document.getElementById('disp').value = v1;
}
change your autoFill1() function
function autoFill1(v1){
this.registerForm.get("dept").setValue(v1);
}

Not able to store dynamic FormArray in FormGroup

I have a FormGroup which has three FormControl fields and one FormArray fields as shown in the figure below. The requirement is to take the manager name from user and once add button is pressed, manager details should be displayed in table. In table a remove button is provided, when remove button is pressed manager should be removed form the table and list. When the form is submitted list of managers should be saved. Everything works fine except formArray logic.
I tried to find a solution to this online (followed various links:- https://alligator.io/angular/reactive-forms-formarray-dynamic-fields/,
Angular 4 Form FormArray Add a Button to add or delete a form input row), but did not helped much. There is not much material on how to store formArray in formGroup. Please suggest.
Below is my code, please have a look:-
1. manager-create-modal.component.html
<div>
<form [formGroup]="createForm" (ngSubmit)="onFormCreation()">
<div class="row">
<div class="column">
<div class="form-inline">
<div class="form-group">
<label for="remote_access_method">Remote Access Method: <font color="orange"> *</font> </label>
<input type="text" size='38' class="form-control" formControlName="remote_access_method" >
</div>
</div>
<br>
<div class="form-inline">
<div class="form-group">
<label for="status">Current Status: <font color="orange"> *</font> </label>
<input type="text" size='38' class="form-control" formControlName="status">
</div>
</div>
<br>
<div class="form-inline">
<div class="form-group">
<label for="secregid">Registration ID:<font color="orange"> *</font> </label>
<input type="text" size='38' class="form-control" formControlName="secregid">
</div>
</div>
<br><br>
<div class="form-inline">
<div class="form-group">
<br><br>
<div formArrayName="manager_formArray">
Enter name: <input type="text" class="form-control" formControlName="MgrName" size='50' >
<button type="button" class="btn btn-primary btn-sm" (click)="addPM()">Add</button>
<br><br>
<table class="table table-hover">
<tr><th>#</th><th>Manager Name</th><th>Remove</th></tr>
<tr *ngFor="let pm of createForm.get('manager_formArray').value; let id = index">
<td>{{id+1}}</td>
<td>{{pm.MgrName}}</td>
<td>
<span class="table-remove">
<button type="button" class="btn btn-primary btn-sm" (click)="removeMgr(id)">Remove</button>
</span>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<br>
</div>
<br><br>
<div class="form-group">
<button class="btn btn-primary">Submit</button>
</div>
</form>
</div>
2. manager-create-modal.component.ts
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormBuilder, FormArray, FormControl, Validators } from '#angular/forms';
#Component({
selector: 'app-manager-create-modal',
templateUrl: './manager-create-modal.component.html',
styleUrls: ['./manager-create-modal.component.css']
})
export class ManagerCreateModalComponent implements OnInit {
createForm: FormGroup;
manager_formArray: FormArray;
remote_access_method: FormControl;
status: FormControl;
secregid: FormControl;
constructor(private formBuilder: FormBuilder) { }
createFormControls(){
this.remote_access_method = new FormControl('');
this.status = new FormControl('');
this.secregid = new FormControl('');
this.manager_formArray = new FormArray([ this.createItem() ]);
}
createItem(): FormGroup {
return this.formBuilder.group({
MgrName: ''
});
}
createFormVariables(){
this.createForm = new FormGroup({
remote_access_method : this.remote_access_method,
status : this.status,
secregid : this.secregid,
manager_formArray: this.manager_formArray,
})
}
ngOnInit() {
this.createFormControls();
this.createFormVariables();
}
addPM(mgr: any): void {
console.log("inside addPM");
this.manager_formArray.push(this.formBuilder.group({MgrName:''}));
console.log("list after addition:"+this.manager_formArray.value);
for(let i = 0; i < this.manager_formArray.length; i++) {
console.log(this.manager_formArray.at(i).value);
}
}
get managerFormArray() {
return this.manager_formArray.get('MgrName') as FormArray;
}
onFormCreation(){
console.log("success")
}
}
The manager name is not displayed in the table and I keep on getting below error:-
ERROR Error: Cannot find control with path: 'manager_formArray ->
MgrName' at _throwError (forms.js:1731) at setUpControl
(forms.js:1639) at
FormGroupDirective.push../node_modules/#angular/forms/fesm5/forms.js.FormGroupDirective.addControl
(forms.js:4456) at
FormControlName.push../node_modules/#angular/forms/fesm5/forms.js.FormControlName._setUpControl
(forms.js:4961) at
FormControlName.push../node_modules/#angular/forms/fesm5/forms.js.FormControlName.ngOnChanges
(forms.js:4911) at checkAndUpdateDirectiveInline (core.js:9031)
at checkAndUpdateNodeInline (core.js:10299) at
checkAndUpdateNode (core.js:10261) at debugCheckAndUpdateNode
(core.js:10894) at debugCheckDirectivesFn (core.js:10854) inside
addPM manager-create-modal.component.ts:50 list after
addition:[object Object],[object Object]
manager-create-modal.component.ts:53 {MgrName: ""}
manager-create-modal.component.ts:53 {MgrName: ""}
I even don't understand why elements are not getting added to manager_formArray. Please help me out.
You have a few issues. First of all, it is better to move the Input which adds more FormGroups to your FormArray outside of the <div formArrayName="manager_formArray">- element. I created a new FormControl this.mgrNameInput = new FormControl(''); for this reason (see StackBlitz for more details).
You also need to add the message to the new entry when you press the Add-button, calling the addPM()-method:
addPM(){ // removed the argument, using the controller inside the method instead.
this.manager_formArray.push(this.formBuilder.group({MgrName:this.mgrNameInput.value}));
this.mgrNameInput.reset(); // reset the input field.
}
I also added the remove-method when removing an entry.
removeMgr(index: number){
this.manager_formArray.removeAt(index);
}
Please check the StackBlitz for the complete example

ngSubmit not working

I have an angular 4 form where I am trying to submit data
HTML Code
<form class="form-horizontal" [formGroup]="signupForm"
(ngSubmit)="onFormSubmit()" >
<fieldset>
<legend>Scheduler</legend>
<!--- Gender Block -->
<div class="form-group">
<label for="scheduleJob">Schedule Job</label>
<input type="text" formControlName = "schedulejob"
id="scheduleJob"
placeholder="Schedule Job">
Button code
<div class="form-group">
<button type="reset" class="btn btn-default">Cancel</button>
<button type="submit" class="btn btn-primary">Submit</button>
</div>
Scheduler.TS file
import { Component, OnInit } from '#angular/core';
import { Scheduler } from 'rxjs/Scheduler';
/* Import FormControl first */
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
#Component({
selector: 'app-scheduler',
templateUrl: './scheduler.component.html',
styleUrls: ['./scheduler.component.css']
})
export class SchedulerComponent implements OnInit {
//Gender list for the select control element
private scheduleTypeList: string[];
//Property for the user
private scheduler:Scheduler;
private signupForm: FormGroup;
constructor(private fb:FormBuilder) { }
ngOnInit() {
this.scheduleTypeList = ['Type 1', 'Type 2', 'Type 3'];
this.signupForm = this.fb.group({
schedulejob: ['', Validators.required] ,
frequency: ['', Validators.required],
days: ['', Validators.required],
zone: ['', Validators.required],
schedulerjobtype:['', Validators.required]
})
}
public onFormSubmit() {
this.scheduler = this.signupForm.value;
if(this.signupForm.valid) {
this.scheduler = this.signupForm.value;
console.log(this.scheduler);
// alert(this.scheduler);
/* Any API call logic via services goes here */
}
//alert(this.scheduler);
console.log(this.scheduler);
}
}
Why is the execution not passed to onFormSubmit on submit click and alert or console.log not printing values?
Like I said in the comment, if your button is not inside your form it will not submit it.
So change to:
<form>
...
<button type="submit">Submit</button>
</form>
It is possible however to have it outside if you do it a bit differently, as described in this question.
I was having the same issue because I was not using a <form> tag to wrap my form.
a small trick you can do is to place an auxiliary hidden button inside the form and let the main button programmatically click the auxiliary button:
<form [formGroup]="form" (ngSubmit)="submit()">
...
<button type="submit" hidden #btn></button>
</form>
...
<button (click)="btn.click()">Submit</button>
you need to put the button code inside the form like below
<form class="form-horizontal"
[formGroup]="signupForm"
(ngSubmit)="onFormSubmit()" >
<fieldset>
<legend>Scheduler</legend>
<!--- Gender Block -->
<div class="form-group">
<label for="scheduleJob">Schedule Job</label>
<input type="text" formControlName = "schedulejob"
id="scheduleJob"
placeholder="Schedule Job">
<!-- Button Code Here -->
<div class="form-group">
<button type="reset" class="btn btn-default">Cancel</button>
<button type="submit" class="btn btn-primary">Submit</button>
</div>
<!-- Form ends here -->
</form>
You can also use the form attribute in the submit button. This attribute uses the form's id to refer to it:
<form id="formID">
...
<button type="submit" form="formID">Submit</button>
</form>