2

I want to make <input> which using material matInput to become readonly using my custom directive. The directive isControlReadonly will be used to set readonly based on security criteria. The problem is it works on <input> but has no effect on <input matInput>

So.. this first input works, the second doesn't:

  <!-- this works -->
  <input type="input" formControlName="test_field" isControlReadonly>

  <!-- this doesn't works -->
  <mat-form-field appearance="standard">
     <mat-label>Name</mat-label>
     <input matInput type="input" formControlName="customer_name" isControlReadonly>
  </mat-form-field>

This is the directive:

import { Directive, ElementRef, OnInit, ViewChildren } from '@angular/core';
import { SecurityService } from './security.service';

@Directive({selector: '[isControlReadonly]'})
export class IsReadonlyDirective implements OnInit {

  constructor(
    private elementRef: ElementRef,
    private securityService: SecurityService
  ) { }

  ngOnInit(): void {
    var ro = !this.securityService.user.canEdit
    this.elementRef.nativeElement.setAttribute('readonly', ro)
  }

Please help how to make that custom directive works with matInput?

Edit Note: I don't want to set readonly from reactive form since directive is much cleaner code. I don't want to add logic to my component, I want the logic in the directive since it is centralized and will be used on all forms.

Thanks

Forexplore-A
  • 21
  • 1
  • 3
  • You are using readonly attribute in reactive forms, please check this anwser https://stackoverflow.com/a/54920060/6823766 – Armen Stepanyan Sep 18 '20 at 07:16
  • I don't want to set readonly from reactive form since directive is much cleaner code. I don't want to add logic to my component, I want the logic in the directive since it is centralized and will be used on all forms. – Forexplore-A Sep 18 '20 at 07:29
  • By using reactive forms you are already moving the logic into the component. That's what reactive forms are all about - having the logic in the typescript code instead of spreading it all over the template. If you want to keep the logic on the template, then template-driven forms are the way to go. – TotallyNewb Sep 18 '20 at 07:51
  • Please read the case carefully. The question is "why it is not working with matInput"? And in my approach there is no "code spreading all over the template" like you said. That's why I use directive, so the logic will be in the directive. Can you imagine I have to do security check on all my reactive form? instead I can just put logic in single place in the directive. So it is much cleaner than you suggest – Forexplore-A Sep 18 '20 at 08:17

2 Answers2

0

EDIT: provided disable solution before. To make a input readonly move your logic to "ngAfterViewInit" Here is a working example for your directive:

@Directive({selector: '[isControlReadonly]'})
export class IsReadonlyDirective implements OnInit, AfterViewInit {

  constructor(
    private elementRef: ElementRef,
    private control: NgControl,
    private securityService: SecurityService
  ) { }

  ngAfterViewInit(): void {
   var ro = !this.securityService.user.canEdit
   this.elementRef.nativeElement.setAttribute('readonly', ro)
  }
}
  • Thanks it works for ```disable()``` but I need ```readonly``` because it receives focus and not grayed out – Forexplore-A Sep 18 '20 at 08:51
  • my fault! Move your logic to "ngAfterViewInit" and implement "AfterViewInit". Then everything should be working fine. ngAfterViewInit(): void { var ro = !this.securityService.user.canEdit this.elementRef.nativeElement.setAttribute('readonly', ro) } – Kerschbaumer Stefan Sep 18 '20 at 09:00
  • This is what I am looking for. It works! Thank you very much – Forexplore-A Sep 18 '20 at 09:20
0

Your directive works fine for all kind of inputs but in case of mat-input, the readonly attribute is set by your directive is overridden by Angular Material's own readonly @Input.

Refer Mat-Input's source code here : Mat-Input Readonly @Input for more info

So what you can do is get your code execution of setting attribute out of the stack and hand it over to event loop. This way your code shall be executed after Angular's manipulations. And the most common way to accomplish that is setTimeout with 0 sec delay

setTimeout(() => {
      this.elementRef.nativeElement.setAttribute('readonly', ro) 
    });

Here is a working BLITZ

dev_ob
  • 36
  • 1