Reputation: 889
I'm attaching a button component (AddButtonComponent
) dynamically to the DOM of another component (ParentComponent
) using Angular Material for styling. If I create buttons normally in the template with <button matButton>A button</button>
, the button gets styled perfectly. If I create it dynamically, it gets styled too.
Now, I need to inject a string to attach a classname to my button, but it loses all styles if I use a custom Injector
. I think that when I inject my string, it is overriding current app injections with that injector.
I tried to add the ParentComponent
injector to the parent
attribute, but it also didn't work.
What am I doing wrong?
(Obviously I can call the instance
of the ElementRef
and set the attribute value, but it is so ugly. I believe this can be achieved.)
Here is a code example:
import {
Component,
OnInit,
Optional,
Inject,
Injector,
InjectionToken,
ElementRef,
ViewContainerRef,
ComponentFactoryResolver,
} from '@angular/core';
export const CLASS_INJECTION: InjectionToken<string> = new InjectionToken<string>('ClassInjection');
@Component({
selector: 'app-add-button`,
template: '<button matButton [class]="classInjection">A button</button>',
styles: []
})
export class AddButtonComponent implements OnInit {
constructor(@Optional() @Inject(CLASS_INJECTION) public classInjection: string) { }
}
@Component({
selector: 'app-parent-component',
template: '<button matButton>Button styled</button>',
styles: []
})
export class ParentComponent implements OnInit {
constructor(
private _injector: Injector,
private componentFactoryResolver: ComponentFactoryResolver,
private viewContainerRef: ViewContainerRef
) { }
ngOnInit() {
const addButtonComponentFactory = this.componentFactoryResolver.resolveComponentFactory(AddButtonComponent);
const addButtonComponentRef = this.viewContainerRef.createComponent(addButtonComponentFactory, undefined, Injector.create({
providers: [{provide: ELEMENT_TYPE, useValue: widget.type === 'column' ? 'SECTION' : 'MODULE'}],
parent: this._injector
}));
// <Code to add element into DOM...>
}
}
Upvotes: 1
Views: 475
Reputation: 54761
<button matButton [class]="classInjection">A button</button>
matButton
will assign CSS classes when it's created, but the [class]
binding you've used will replace all existing CSS classes. So the Material Design CSS styles are lost. If you look in the Chrome inspector, then you'll see that they've been removed.
If you know that only SECTION
and MODULE
will be used, then use the [class.style]
binding to selectively add or remove a specific style.
<button matButton [class.SECTION]="classInjection === 'SECTION'"
[class.MODULE]="classInjection === 'MODULE'">A button</button>
If you don't know what the CSS styles will be, then you'll have to manually add them. That can be done by accessing the <button>
element via @ViewChild
and then manually add the CSS style.
@Component({
selector: 'app-add-button',
template: '<button matButton>A button</button>',
styles: []
})
export class AddButtonComponent implements OnInit {
@ViewChild(MatButton, {read: ElementRef})
public btn: ElementRef<HTMLElement>;
constructor(@Optional() @Inject(CLASS_INJECTION) public classInjection: string) {
}
public ngOnInit(): void {
this.btn.nativeElement.classList.add(this.classInjection);
}
}
I have been very frustrated in the past when using [class]
as it overwrites alls styles, and there isn't an alternative method to use. Both [class]
and [ngClass]
have the same behavior, and if you have to do the above frequently you could write a custom directive called [addClass]
that appends CSS styles only.
Upvotes: 1