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);
}
Related
I have a custom component that contains an input and a button like this
<div class="input-group">
<input type="text" class="form-control form-control-sm"
(input)="change($event)"
ngbDatepicker #d="ngbDatepicker" required
#datef />
<div class="input-group-append">
<button type="button" class="btn btn-sm btn-success" (click)="d.toggle()" type="button">
<i class="fa fa-calendar"></i>
</button>
</div>
</div>
I'd like it to have some funcionality so when the user press enter on the input it should emit a pseudo event
<custom-datepicker (keyup.enter)="handleKeyboard($event)"></custom-datepicker>
I've tried with #HostListener, but I'm getting errors about too much recursion, please, help me
You can just simple use the concept of event emitters wherein you can emit an event from your custom component to your parent component
----Custom Component Html----
<div class="input-group">
<input type="text" class="form-control form-control-sm"
(input)="change($event)"
ngbDatepicker #d="ngbDatepicker" required
#datef />
<div class="input-group-append">
<button type="button" class="btn btn-sm btn-success" (click)="d.toggle()" type="button">
<i class="fa fa-calendar"></i>
</button>
</div>
----Custom Component ts----
#Output()
customEvent = new EventEmitter();
change(event) {
this.customEvent.emit();
}
----Parent Component ----
<custom-datepicker (customEvent)="handleKeyboard($event)"></custom-datepicker>
You can approach this using Reactive Forms FormArray. You would attach an (keyup) or (keyup.enter) handler to the <input />. Within the handler for this keyup event, we push a new FormControl to a FormArray. This example uses FormBuilder to generate a FormGroup that contains a FormArray with a key of things. We use the push method of FormArray to add a new FormControl/AbstractControl within the handler for keyup.
Component:
import { Component } from '#angular/core';
import { FormBuilder, FormGroup, FormArray } from '#angular/forms';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular';
myForm: FormGroup;
constructor(private fb: FormBuilder) {
this.createForm();
}
onEnter() {
this.addThing();
}
get things() {
return this.myForm.get('things') as FormArray;
}
private createForm() {
this.myForm = this.fb.group({
things: this.fb.array([
// create an initial item
this.fb.control('')
])
});
}
private addThing() {
this.things.push(this.fb.control(''));
}
}
Template:
<form [formGroup]="myForm">
<div formArrayName="things">
<div *ngFor="let thing of things.controls; let i=index">
<label [for]="'input' + i">Thing {{i}}:</label>
<input type="text" [formControlName]="i" [name]="'input' + i" [id]="'input' + i" (keyup.enter)="" />
</div>
</div>
</form>
At a very basic level you can loop through each in the form array using the controls property of the respective FormArray element and the value using the value property:
<ul>
<li *ngFor="let thing of things.controls">{{thing.value}}</li>
</ul>
Here is a StackBlitz(https://stackblitz.com/edit/angular-r5zmbg) demonstrating the functionality.
Hopefully that helps!
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;
}
}
I develop a form to add users, the form works, but the problem is
when click on the button submit two successive times, the method add two users, I want the button submit (add) disable after click directly ( I work with angular 5)
HTML Code :
<form [formGroup]="addfrm">
...
...
<div style="text-align:center">
<button class="btn btn-primary" (click)="processAdd()" [disabled]="addfrm.invalid">add</button>
<button data-dismiss="modal" class="btn btn-default">cancel</button>
</div>
</form>
You can define a field within your component and set it to true when submitted.
<button class="btn btn-primary" (click)="processAdd()" [disabled]="addfrm.invalid || submitted">add</button>
within component
export class MyComponent {
submitted = false;
...
processAdd() {
this.submitted = true;
this.someService.post(this.addForm).subscribe(result => {
this.submitted = false; // reset it on response from server
});
}
}
You can try with ngForm directive :
<form [formGroup]="addfrm" #myform="ngForm">
<div style="text-align:center">
<button class="btn btn-primary" (click)="processAdd()" [disabled]="myform.submitted">add</button>
<button data-dismiss="modal" class="btn btn-default">cancel</button>
</div>
</form>
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
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>