I have this custom button:
Html template:
<button mat-button
[color]="color"
(cbClick)="onClick($event)"
[disabled]="disabled"
[ngClass]="isStroked ? 'mat-stroked-button' : 'mat-raised-button'">
<ng-content></ng-content>
</button>
Typescript:
@Component({
selector: 'cb-button',
templateUrl: './button.component.html',
styleUrls: ['./button.component.scss']
})
export class ButtonComponent {
/**
* Warning - calling this "click" causes bug due to naming conflict
*/
@Output() public readonly clicked = new EventEmitter<boolean>();
@Output() public readonly popupClick = new EventEmitter();
@Input() public color = 'primary';
@Input() public disabled = false;
@Input() public isStroked = false;
@Input() public popup?: Popup;
constructor() { }
public readonly onClick = (event: MouseEvent): void => {
this.clicked.emit(true);
};
public readonly onPopupClick = (event: MouseEvent): void =>{
this.popupClick?.emit(event);
};
}
using it:
<span fxFlex></span>
<cb-button class="cb-margin"
(clicked)="generateUserSecurityReport()">
User Security Report
</cb-button>
For some reason in one area of my app, but not in other areas, it is displaying with 2 buttons. When I remove <ng-content>
it doesn't create 2 buttons:
Why is it nesting a second button in some scenarios?
EDIT:
HTML produced:
<cb-button _ngcontent-wwl-c684="" class="cb-margin" _nghost-wwl-c681="" ng-version="11.0.7">
<button _ngcontent-wwl-c681="" mat-button="" class="mat-focus-indicator mat-button mat-button-base mat-primary mat-raised-button" ng-reflect-color="primary" ng-reflect-disabled="false" ng-reflect-ng-class="mat-raised-button">
<span class="mat-button-wrapper">
<button
_ngcontent-wwl-c681=""
mat-button=""
class="mat-focus-indicator mat-button mat-button-base mat-primary mat-raised-button ng-scope"
ng-reflect-color="primary"
ng-reflect-disabled="false"
ng-reflect-ng-class="mat-raised-button"
>
<span class="mat-button-wrapper"> User Security Report </span>
<span matripple="" class="mat-ripple mat-button-ripple" ng-reflect-disabled="false" ng-reflect-centered="false" ng-reflect-trigger="[object HTMLButtonElement]"></span><span class="mat-button-focus-overlay"></span>
</button>
</span>
<span matripple="" class="mat-ripple mat-button-ripple" ng-reflect-disabled="false" ng-reflect-centered="false" ng-reflect-trigger="[object HTMLButtonElement]"></span><span class="mat-button-focus-overlay"></span>
</button>
</cb-button>
EDIT:
When i remove the downgrade decorator from the module it works as expected:
@NgModule({
declarations: ButtonComponent,
imports: [
CommonModule,
FlexLayoutModule,
MatIconModule,
MatButtonModule,
CbPopupTipModule,
CbClickModule
],
exports: ButtonComponent
})
@CbDowngrades({
components: [
{ selector: 'cb-button', component: ButtonComponent }
]
})
export class CbButtonModule { }
The code used to do the downgrade:
import { downgradeComponent, downgradeInjectable } from '@angular/upgrade/static';
import { ClassCtor } from '../types/classctor.type';
import { NGJS_APP_NAME } from '../declarations/app.constants';
import { kebabCaseToCamelCase } from 'cb-hub-lib';
const DOWNGRADE_MODULE = angular.module(NGJS_APP_NAME);
function cbDowngradeComponent(ngJsSelector: string, componentClass: ClassCtor): void {
DOWNGRADE_MODULE
.directive(
ngJsSelector,
downgradeComponent({ component: componentClass })
);
}
function cbDowngradeInjectable(ngJsInjectionToken: string, injectableClass: ClassCtor): void {
DOWNGRADE_MODULE
.factory(ngJsInjectionToken, downgradeInjectable(injectableClass));
}
function cbDowngradeComponents(components: CbDowngradeComponent[] = []): void {
components.forEach((component) => {
const camelCaseSelector = kebabCaseToCamelCase(component.selector);
cbDowngradeComponent(camelCaseSelector, component.component);
});
}
function cbDowngradeInjectables(injectables: (ClassCtor | CbDowngradeInjectable)[] = []): void {
injectables.forEach((injectable) => {
if ((injectable as ClassCtor).name) {
const injectableCtor = (injectable as ClassCtor);
cbDowngradeInjectable(injectableCtor.name, injectableCtor);
} else {
const injectableConfig = (injectable as CbDowngradeInjectable);
cbDowngradeInjectable(injectableConfig.token, injectableConfig.injectable);
}
});
}
interface CbDowngrades {
components?: CbDowngradeComponent[];
injectables?: CbDowngradeInjectable[];
}
interface CbDowngradeInjectable {
/** ThisShouldBePascalCase */
token: string;
injectable: ClassCtor;
}
interface CbDowngradeComponent {
/** this-should-be-kebab-case */
selector: string;
component: ClassCtor;
}
/** Decorate your modules with this decorator to downgrade any components or injectables for usage in angularjs
*/
export function CbDowngrades(cbDowngrades: CbDowngrades): ClassDecorator {
return (constructor: Function) => {
cbDowngradeComponents(cbDowngrades.components);
cbDowngradeInjectables(cbDowngrades.injectables);
};
}