1

I'm using angular reactive forms for a component library, and there are some requirements for the project that would be made much easier if I could just get the associated maxlength validator (if one exists) inside a form control (it's supposed to show how many characters are left when the EU is typing)

Specifically, I want to do it from within the component, based on whatever maxlength validator was used, as this is a generic branded component library for use within my company.

As far as I can tell, the only way to do this is to pass in the maxlength separately from the validators - but figured I'd ask The Internets to see if there's a cooler way.

I've implemented (and modernized for angular 12) the ControlValueAccessorConnector solve from https://medium.com/angular-in-depth/dont-reinvent-the-wheel-when-implementing-controlvalueaccessor-a0ed4ad0fafd - so I have access to all those properties.

Any help - or a validation that what I'm looking for is not gonna happen - would be appreciated!

4
  • See the usage comments here. The maxlength error object has requiredLength and actualLength properties that may help you. Commented Jan 28, 2022 at 22:49
  • Doesn't that only get returned on error though? The design has the maximum length and the characters entered, regardless of if it exceeds or not - kinda like the "characters left" below the comment form Commented Jan 28, 2022 at 23:34
  • Would you mind building a minimum reproducible version of what you are asking, in here: stackblitz.com/fork/angular-ivy ? then you can add a link to in into your question. it would make it easier for you to get help. Commented Jan 29, 2022 at 0:16
  • @CountSpatula Can you please add a bit of the code in the question? How you are creating the form & how you are passing the control to your library component? Commented Jan 29, 2022 at 6:41

1 Answer 1

2

thre're not a "relation" between a FormControl and a input (a FormControl is independient of the input), so you can not access to the htmlElement input if you not use one around-work: a directive

If the selector of one directive is '[formControl], [formControlName]' it is applied to all the inputs with formControl and FormControlName.

We inject in constructor the NgControl and the ElementRef

constructor(private control:NgControl,private el:ElementRef) { }

So we can access to the function validator of the control using this.control.control.validator. See that in this way we access to the "function". And as all the function we can feed with a FormControl and get the result. In this case we are feeding with a FormControl very large

The directive can be some like

@Directive({
  selector: ''[formControl], [formControlName]''
})
export class AddMaxLengthDirective implements OnInit{

  constructor(private control:NgControl,private el:ElementRef) { }
  ngOnInit()
  {
    //we need check if the control has "any" validator
    const err=this.control.control.validator?
        this.control.control.validator(
            new FormControl(".".repeat(10000000))):null

    if (err && err.maxlength && err.maxlength.requiredLength)
         this.el.nativeElement.setAttribute(
                 'maxlength',err.maxlength.requiredLength)

  }

}

When we has the attribute maxlength in our input, we can write in .html some like

<input #input [formControl]="control">
<span *ngIf="input.getAttribute('maxlength') as max">
  left {{ +max - input.value.length }} characters
</span>

You can see in this stackblitz. If you use F12, you can inspect the DOM and see how is added the maxlength attribute

NOTE: don't forget add the directive in your module!

Update We can also improve the directive to add automatically the message. For this we can use the Netanel Basal's idea in his article Makes your angular form's error appear magically

We create a counterComponent

@Component({
  selector: 'app-help',
  template: `<p class="count-help">{{_text}}</p>`,
  changeDetection: ChangeDetectionStrategy.OnPush,
  styleUrls: ['./count.component.css']
})
export class CountComponent {
  _text;

  @Input() set text(value) {
    if (value !== this._text) {
      this._text = value;
      this.cdr.detectChanges();
    }
  };
 constructor(private cdr: ChangeDetectorRef) { }

}

And our directive makes use of the Netanel Basal's idea

export class AddMaxLengthDirective implements OnInit {
  ref: ComponentRef<CountComponent>
  max:number=0;
  subscription:any;
  constructor(
    private control: NgControl,
    private el: ElementRef,
    private vcr: ViewContainerRef,
    private resolver: ComponentFactoryResolver
  ) {}
  ngOnInit() {
    const err = this.control.control.validator
      ? this.control.control.validator(new FormControl('.'.repeat(10000000)))
      : null;
      if (err && err.maxlength && err.maxlength.requiredLength) {

      this.max=err.maxlength.requiredLength;
      this.el.nativeElement.setAttribute(
        'maxlength',
        this.max
      );
      const factory = this.resolver.resolveComponentFactory(CountComponent);
      this.ref = this.vcr.createComponent(factory);
      this.subscription=this.control.valueChanges.pipe(startWith(this.control.value))
      .subscribe(res=>{
        this.ref.instance.text='left '+(this.max-res.length)+' characters'
      })
    }
  }
  ngOnDestroy()
  {
    this.subscription && this.subscription.unsubscribe()
  }
}

See the new stackblitz

Sign up to request clarification or add additional context in comments.

2 Comments

I update the answer because we need check if the FormControl has any validator
Thanks - I think that'll get me there, you definitely understood what I was trying to accomplish!!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.