The code snippet below shows how to disable (angular material) components if the shouldDisable$ stream from the ExampleService emits true. This can be a useful solution when we want to block actions when the user does not have the appropriate permissions resulting from:
- user role
- a feature that is blocked for the user etc.
(*) DestroyedDirective
was implemented according to the idea of Kristiyan Kostadinov.
@Directive({
selector: "[disableInteractiveElements]",
standalone: true,
hostDirectives: [DestroyedDirective],
})
export class DisableInteractiveElementsDirective implements OnInit {
private readonly destroyed$ = inject(DestroyedDirective).destroyed$;
constructor(
private readonly service: ExampleService,
private readonly elementRef: ElementRef,
@Optional() @Self() private readonly button: MatButton,
@Optional() @Self() private readonly select: MatSelect
) {}
ngOnInit(): void {
this.service.shouldDisable$
.pipe(takeUntil(this.destroyed$))
.subscribe((shouldDisable) => {
if (this.button) {
this.button.disabled = shouldDisable;
} else if (this.select) {
this.select.disabled = shouldDisable;
} else if (
this.elementRef.nativeElement &&
"disabled" in this.elementRef.nativeElement
) {
this.elementRef.nativeElement.disabled = shouldDisable;
}
});
}
}
Usage in template:
<button mat-button mat-raised-button>Always available</button>
<button disableInteractiveElements mat-button mat-raised-button>
Could be disabled
</button>
<mat-select disableInteractiveElements placeholder="Settings">
<mat-option value="'setting1'">Setting 1</mat-option>
<mat-option value="'setting2'">Setting 2</mat-option>
</mat-select>
<input disableInteractiveElements type="text" />
In the case shown above, the first button will always be active, while the other elements, i.e. the second button, select and input, will be active if service.shouldDisable$
emits false.