18

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.

peterh
  • 11,875
  • 18
  • 85
  • 108
Shreenil Patel
  • 1,054
  • 1
  • 9
  • 20
  • 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 timeslot – 0mpurdy Jul 22 '17 at 15:17
  • 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] – Shreenil Patel Jul 22 '17 at 15:34
  • ok I have created a [plunker here](https://plnkr.co/edit/W4JZVDoOaWzSAo3oQtDg?p=preview) demonstrating the problem you describe I'll try and see how to fix it now :) – 0mpurdy Jul 22 '17 at 15:39
  • 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. @0mpurdy – Shreenil Patel Jul 22 '17 at 15:45
  • You can try the syntax given in [this answer](https://stackoverflow.com/a/39347582/1009922). – ConnorsFan Jul 22 '17 at 16:50

7 Answers7

29

Use it this way:

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

Same is for readonly.

Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
baHI
  • 1,510
  • 15
  • 20
21

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

0mpurdy
  • 3,198
  • 1
  • 19
  • 28
  • @user2136053 then you will surely like this as well: [attr.disabled]="isDisabled ? '' : null" (note the inner '' are 2 single quotes) – baHI Nov 28 '18 at 10:32
  • Late to the party, however i think [disabled]="false" does not disable the element. However [attr.disabled]="false" does. – Viking22 May 09 '20 at 15:42
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;
}
SiddAjmera
  • 38,129
  • 5
  • 72
  • 110
Aravind
  • 40,391
  • 16
  • 91
  • 110
  • 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'. (" – ShaileshDev Dec 01 '17 at 06:42
  • 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? – Jacques Jul 24 '18 at 13:40
  • 1
    @Jacques if you want the click event not to fire add `pointer-events:none` – Aravind Jul 24 '18 at 14:00
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.

Muhammed Moussa
  • 4,589
  • 33
  • 27
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

Community
  • 1
  • 1
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

Durgesh Pal
  • 695
  • 5
  • 13
0

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

YulePale
  • 6,688
  • 16
  • 46
  • 95