Angular Directive for a Specific Component
Some Angular directives only make sense when they are applied to a specific Angular component. For example, the host component might be injected in the directive constructor so that directive code can manipulate it:
@Directive({
selector: '[appGeneral]'
})
export class GeneralDirective {
constructor(private checkboxComponent: MatCheckbox) {
// manipulate MatCheckbox instance
}
}
When the above directive is applied to a <mat-checkbox>
element, it works fine. However, when applied to any other component, it fails to instantiate because the dependency injector can't find a MatCheckbox
instance to inject:
NullInjectorError: StaticInjectorError(AppModule)[GeneralDirective -> MatCheckbox]:
StaticInjectorError(Platform: core)[GeneralDirective -> MatCheckbox]:
NullInjectorError: No provider for MatCheckbox!
This can be avoided by marking the argument optional for dependency injector and checking its value before using it:
@Directive({
selector: '[appGeneral]'
})
export class GeneralDirective {
constructor(@Optional() private checkboxComponent: MatCheckbox) {
if (this.checkboxComponent != null) {
// manipulate MatCheckbox instance
}
}
}
This approach works fine. If the directive is applied to a wrong component, it will simply be ignored. However, Juan Mendes suggested a simpler solution in a comment to an older blogpost.
The directive's selector
option accepts a large subset of CSS selectors. It's not limited to an attribute name as one could incorrectly assume based on the code generated by Angular CLI. This means that the component restriction can be included in the selector and no other code changes are necessary:
@Directive({
selector: 'mat-checkbox[appSpecific]'
})
export class SpecificDirective {
constructor(private checkboxComponent: MatCheckbox) {
// manipulate MatCheckbox instance
}
}
If the appSpecific
attribute is added to any other element than <mat-checkbox>
, it will be completely ignored. Angular will not even instantiate it. This will result in better performance and simpler code (because no checks are needed in the directive code) than my first approach.