I am create a button directive to show mat spinner inside mat button to show loading when api call and its working fine when i am pass [loading]="true" inside button tag but also show NullInjectorError: No provider for MatButton! error in console.
I am using lazy loaded component when I am load button directive inside login.module.ts and register.module.ts it show The Directive 'ButtonDirective' is declared by more than one NgModule.
so i want to remove no provider error and want to load directive in multiple modules and i am using angular v16+.
button.directive.ts
import { ComponentRef, Directive, Input, OnChanges, Renderer2, SimpleChanges, ViewContainerRef } from '@angular/core';
import { MatButton } from '@angular/material/button';
import { ThemePalette } from '@angular/material/core';
import { MatProgressSpinner } from '@angular/material/progress-spinner';
@Directive({
selector: `button`,
})
export class ButtonDirective implements OnChanges {
private spinner!: ComponentRef<MatProgressSpinner> | null;
@Input() loading = false;
@Input() disabled = false;
@Input() color: ThemePalette;
constructor(
private matButton: MatButton,
private viewContainerRef: ViewContainerRef,
private renderer: Renderer2
) {}
ngOnChanges(changes: SimpleChanges): void {
if (!changes['loading']) {
return;
}
if (changes['loading'].currentValue) {
this.matButton._elementRef.nativeElement.classList.add('button-loading');
this.matButton.disabled = true;
this.createSpinner();
} else if (!changes['loading'].firstChange) {
this.matButton._elementRef.nativeElement.classList.remove(
'button-loading'
);
this.matButton.disabled = this.disabled;
this.destroySpinner();
}
}
private createSpinner(): void {
if (!this.spinner) {
this.spinner = this.viewContainerRef.createComponent(MatProgressSpinner);
this.spinner.instance.color = this.color;
this.spinner.instance.diameter = 20;
this.spinner.instance.mode = 'indeterminate';
this.renderer.appendChild(
this.matButton._elementRef.nativeElement,
this.spinner.instance._elementRef.nativeElement
);
}
}
private destroySpinner(): void {
if (this.spinner) {
this.spinner.destroy();
this.spinner = null;
}
}
}
login.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { LoginRoutingModule } from './login-routing.module';
import { LoginComponent } from './login.component';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { ButtonDirective } from 'src/app/utility/button.directive';
@NgModule({
declarations: [
LoginComponent,
ButtonDirective
],
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
LoginRoutingModule,
MatFormFieldModule,
MatInputModule,
MatButtonModule,
MatIconModule
]
})
export class LoginModule { }
You buttonDirective should belong to a "dedicated" module. This can be, e.g. a utils module, e.g.
@NgModule({
imports: [..],
declarations:[ButtonDirective,...]
exports:[ButtonDirective,....]
})
export class UtilsModule{}
Then import the UtilsModule in all the modules where you need the directive. You can check this link (Really in the officials docs are not very well explained)