Angular 2+ attr.disabled is not working for div when I try to iterate ngFor loop
Asked Answered
S

7

21

I'm working on a appointment booking app, where I'm displaying time slots for appointments using *ngFor to loop.

html

<div *ngFor="let item of allTimeSlots">
    <div class="col-sm-3">
        <div class="choice" data-toggle="wizard-slot" [attr.disabled]="item.status" (click)="slotSelected($event)">
            <input type="radio" name="slot" value="{{item.timeSlot}}">
            <span class="icon">{{item.timeSlot}}</span> {{item.status}}
        </div>
    </div>
</div>

typescript

for (var index = 0; index < this.totalMinutes; index += 15, i++) {
  this.allTimeSlots[i] = new allTimeSlots("", false);

  this.allTimeSlots[i].timeSlot = (hour <= 12 ? hour : hour - 12) + ":" + (minute <= 9 ? ("0" + minute) : minute) + " " + ampm;
  this.bookedTimeSlots.forEach(element => {
    if (this.allTimeSlots[i].timeSlot == element) {
      this.allTimeSlots[i].status = true;
    }
  });
}

Here's screen shot of time slots which displays true if the time slot is booked and false if available for debugging purpose. enter image description here

When I run this code it doesn't throw any error but all the div elements created by *ngFor are disabled. I tried to use *ngIf instead of disabled, it works pretty well. But the problem is I want to display whether the time slot is available or not.

Stroller answered 22/7, 2017 at 15:13 Comment(5)
can you check if all the statuses of the time slots are true? (console.log(this.allTimeSlots) after the for loop), if they are then the issue is with how you are checking for a timeslotUpright
i checked it, Even that {{item.status}} is for debugging purpose only. it prints the correct value true and false as i have given to it. but it doesn't work in [attr.disabled]Stroller
ok I have created a plunker here demonstrating the problem you describe I'll try and see how to fix it now :)Upright
Yes, Exactly same did happen to me, I have edited the question and added my screenshot in it, from which it can be better understood. @UprightStroller
You can try the syntax given in this answer.Philbo
C
21

Disabled cannot be used for a div element and only applied to the below elements

<button>    
<fieldset>  
<input> 
<keygen>    
<optgroup>  
<option>    
<select>    
<textarea>  

See this

So for your issue, you can handle it by using:

<div 
  class="choice" 
  data-toggle="wizard-slot" 
  [class.disabled]="item.status" 
  (click)="slotSelected($event)">
  <input 
    type="radio" 
    name="slot" 
    value="{{item.timeSlot}}" 
    [disabled]="item.status">
  <span class="icon">{{item.timeSlot}}</span> {{item.status}}
</div>

and you should be adding styles

.disabled {
  cursor: not-allowed;
}
Copse answered 22/7, 2017 at 16:59 Comment(3)
Not working for me. Giving error => Uncaught Error: Template parse errors: Can't bind to 'disabled' since it isn't a known property of 'div'. ("Humes
should point out that this solution doesn't stop the click event from firing on that element as would normally be the case with the disabled attribute?Redmon
@Redmon if you want the click event not to fire add pointer-events:noneCopse
E
32

Use it this way:

[attr.disabled]="isDisabled ? '' : null"

Same is for readonly.

Elicia answered 28/11, 2018 at 10:32 Comment(3)
If you have already logic with Reactive Forms, that will overwrite.Angola
this is what I wanted. Angular is differentPhotoconductivity
This is the correct and simplest solution. Here's what I went with: [attr.disabled]="isProcessing ? true: null" Works in Angular 14. Thank you!Sarasvati
U
23

Use [disabled] instead of [attr.disabled]

This is because [attr.disabled]="false" will add disabled="false" to the element which in html, will still disable the element

Syntax that will not disable an element

<button>Not Disabled</button>
<button [disabled]="false">Not Disabled</button>

Syntax that will disable an element

<button disabled></button>
<button disabled="true"></button>
<button disabled="false"></button>
<button [attr.disabled]="true"></button>
<button [attr.disabled]="false"></button>
<button [disabled]="true"></button>

disabled will disable an element whether it is true or false, it's presence means that the element will be disabled. Angular will not add the disabled element at all for [disabled]="variable" if variable is false.

As you mentioned in your comment, you are using div elements and not buttons, which angular 2 doesn't recognise the disabled element of. I would recommend using a button if you are going to be capturing click events but if not you could do something like:

<div [ngClass]="{ 'disabled': item.status }"></div>

and have a CSS class to show the time slot as booked.

More information on [ngClass] is available in the documentation

Upright answered 22/7, 2017 at 15:40 Comment(2)
@user2136053 then you will surely like this as well: [attr.disabled]="isDisabled ? '' : null" (note the inner '' are 2 single quotes)Elicia
Late to the party, however i think [disabled]="false" does not disable the element. However [attr.disabled]="false" does.Grandchild
C
21

Disabled cannot be used for a div element and only applied to the below elements

<button>    
<fieldset>  
<input> 
<keygen>    
<optgroup>  
<option>    
<select>    
<textarea>  

See this

So for your issue, you can handle it by using:

<div 
  class="choice" 
  data-toggle="wizard-slot" 
  [class.disabled]="item.status" 
  (click)="slotSelected($event)">
  <input 
    type="radio" 
    name="slot" 
    value="{{item.timeSlot}}" 
    [disabled]="item.status">
  <span class="icon">{{item.timeSlot}}</span> {{item.status}}
</div>

and you should be adding styles

.disabled {
  cursor: not-allowed;
}
Copse answered 22/7, 2017 at 16:59 Comment(3)
Not working for me. Giving error => Uncaught Error: Template parse errors: Can't bind to 'disabled' since it isn't a known property of 'div'. ("Humes
should point out that this solution doesn't stop the click event from firing on that element as would normally be the case with the disabled attribute?Redmon
@Redmon if you want the click event not to fire add pointer-events:noneCopse
P
4

CSS

.disabled-contenct {
  pointer-events: none;
  opacity: 0.4;
}

HTML

<div [class.disabled-contenct]="!isDisabledContent"></div>

TS

isDisabledContent: boolean;

then you can toggle isDisabledContent any time.

Progestational answered 3/4, 2019 at 9:21 Comment(1)
This does not prevent keyboard interactions.Semiliterate
B
0

As 0mpurdy answered, you should use [disabled] attribute. The variable doesn't have to be a boolean, but the expression should.

In your code, it determines if item.status is falsy or truthy. So if it's not null/NaN/undefined you will get "true" and the div will be disabled.

Instead insert an expression:

[disabled]="item.status === 'occupied'"


Edit

Sorry I missed that. Since you cannot disable a div put the disabled attribute inside the input tag.

<input type="radio" [disabled]="item.status === 'occupied'" name="slot" value="{{item.timeSlot}}">

since you want to display the selection as disabled, you'll need to put it directly inside the input. the radio button will be unclickable and a 'cursor: not-allowed' will show.

Hope that helps

Bacon answered 22/7, 2017 at 16:15 Comment(2)
I tried all these ways. but [disabled] doesn't works on div element.Stroller
Thanks for contributing! As of now i am using div element to do work of radio i need to disable that div only. The Answer which is approved above worked properly for me.Stroller
G
0

Attribute directive will surely help you for disabling or enabling div or any other HTML element and it’s child elements.

DisableDirective

First, create an attribute directive that will take one parameter (appDisable)as input. It will disable/enable DOM elements and their child DOM elements based on the input.

import { AfterViewInit, Directive, ElementRef, Input, OnChanges, Renderer2 } from '@angular/core';

const DISABLED = 'disabled';
const APP_DISABLED = 'app-disabled';
const TAB_INDEX = 'tabindex';
const TAG_ANCHOR = 'a';

@Directive({
  selector: '[appDisable]'
})
export class DisableDirective implements OnChanges, AfterViewInit {

  @Input() appDisable = true;

  constructor(private eleRef: ElementRef, private renderer: Renderer2) { }

  ngOnChanges() {
    this.disableElement(this.eleRef.nativeElement);
  }

  ngAfterViewInit() {
    this.disableElement(this.eleRef.nativeElement);
  }

  private disableElement(element: any) {
    if (this.appDisable) {
      if (!element.hasAttribute(DISABLED)) {
        this.renderer.setAttribute(element, APP_DISABLED, '');
        this.renderer.setAttribute(element, DISABLED, 'true');

        // disabling anchor tab keyboard event
        if (element.tagName.toLowerCase() === TAG_ANCHOR) {
          this.renderer.setAttribute(element, TAB_INDEX, '-1');
        }
      }
    } else {
      if (element.hasAttribute(APP_DISABLED)) {
        if (element.getAttribute('disabled') !== '') {
          element.removeAttribute(DISABLED);
        }
        element.removeAttribute(APP_DISABLED);
        if (element.tagName.toLowerCase() === TAG_ANCHOR) {
          element.removeAttribute(TAB_INDEX);
        }
      }
    }
    if (element.children) {
      for (let ele of element.children) {
        this.disableElement(ele);
      }
    }
  }
}

Do not forget to register DisableDirective in the AppModule

How to use the directive in the component?
Pass the boolean value (true/false) in the directive from the component.

<div class="container" [appDisable]="true">
</div>

You can refer the complete Post HERE

Running example on Stackblitz

Greenshank answered 17/7, 2020 at 6:31 Comment(0)
E
0

There is a way you can do it using attribute binding. It is a simple method. See the docs on how to implement.

Entomologize answered 6/7, 2021 at 8:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.