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

  &nbsp;&nbsp;
  <button type="button" class="btn btn-default" (click)="newHero()">New Hero</button>
  <i>without</i> reset

</form>

4 answers

  • answered 2019-10-15 13:41 malbarmawi

    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 ⚡⚡

  • answered 2019-10-15 13:57 Az264

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

  • answered 2019-10-15 14:02 cmprogram

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

  • answered 2019-10-15 14:08 noririco

    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
        />