Refresh input type file - html

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);
}

Related

How to assign formGroup to an array for form validation

I have a repeater section in angular, i followed the below method to achive it, but i want to validate the form (Enable or disable the final submit button based on the validation),Here is the sample code.
ts file:-
private formBuilder : FormBuilder
) { }
formInfoHolder = [];
onAdd(){
let newForm = {
let newForm = {
subText : new FormControl('',[Validators.required,Validators.maxLength(250)]),
header : new FormControl('',[Validators.required,Validators.maxLength(20)]),
link : new FormControl('',[Validators.required,Validators.pattern('[a-zA-Z]*')]),
}
}
this.formInfoHolder.push(this.formBuilder.group(newForm))
}
delete(toDelete){
let del : any[] = [];
for(let form of this.formInfoHolder){
if(toDelete !== form){
del.push(form);
}
}
this.formInfoHolder = del;
}
then in HTML Component
<div *ngFor="let form of formInfoHolder">
<form [formGroup]="form">
<label for="username"> Username </label>
<input id="username" type="text" formControlName="username">
<!-- insert other input elements with the formControlName -->
<label for="delete"> Delete </label>
<input id="delete" type="button" (click)="delete(form)">
</form>
</div>
<button (click)="onAdd()">Add</button>
<button [disabled]="//Here i want to validate" (click)="saveInfo()">Save</button>
How do i validate the form(enable the button only if all the fields are filled) which is stored in an array.
Here is a Stackblitz example for what you are looking for
I have used formarray and added formgroups into it, adding and removing formgroups becomes easy in formarray. Also validation also becomes easy because I only have check if the main form in valid

How to implement a pseudo event for a custom component in Angular?

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!

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

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;
}
}

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>