goetzrobin / spartan

Cutting-edge tools powering Angular full-stack development.
https://spartan.ng
MIT License
1.09k stars 120 forks source link

Adding the hlmMenuItem directive on dropdown separated in multiple components throw an exception #285

Closed Splinteer closed 1 month ago

Splinteer commented 1 month ago

Please provide the environment you discovered this bug in.

Angular 17.3.7 Spartan: 0.0.1-alpha.347

Only added spartan ui to my existing project without nx following the installation page

Which area/package is the issue in?

dropdown-menu

Description

When splitting dropdown menu in multiple components. I have an exception when adding the hlmMenuItem directive.

Please provide the exception or error you saw

ERROR NullInjectorError: R3InjectorError(Standalone[GridComponent])[InjectionToken cdk-menu-stack -> InjectionToken cdk-menu-stack -> InjectionToken cdk-menu-stack -> InjectionToken cdk-menu-stack]: 
  NullInjectorError: No provider for InjectionToken cdk-menu-stack!
    at NullInjector.get (core.mjs:1654:27)
    at R3Injector.get (core.mjs:3093:33)
    at R3Injector.get (core.mjs:3093:33)
    at R3Injector.get (core.mjs:3093:33)
    at R3Injector.get (core.mjs:3093:33)
    at ChainedInjector.get (core.mjs:15246:36)
    at lookupTokenUsingModuleInjector (core.mjs:5730:39)
    at getOrCreateInjectable (core.mjs:5778:12)
    at ɵɵdirectiveInject (core.mjs:11050:19)
    at ɵɵinject (core.mjs:1106:60)

Other information

No response

I would be willing to submit a PR to fix this issue

Splinteer commented 1 month ago

The bug is not originated from the spartan package but related to the @angular/cdk. When separating a dropdown in multiple components, you need to provide this to the component: PARENT_OR_NEW_MENU_STACK_PROVIDER

Ex:

import { PARENT_OR_NEW_MENU_STACK_PROVIDER } from '@angular/cdk/menu';
import { CommonModule } from '@angular/common';
import {
  Component,
  Input,
  TemplateRef,
  input,
} from '@angular/core';
import { HlmButtonDirective } from '@spartan-ng/ui-button-helm';
import { HlmIconComponent } from '@spartan-ng/ui-icon-helm';
import { BrnMenuTriggerDirective } from '@spartan-ng/ui-menu-brain';
import {
  HlmMenuComponent,
  HlmMenuGroupComponent,
  HlmMenuItemDirective,
  HlmMenuItemIconDirective,
  HlmMenuItemSubIndicatorComponent,
  HlmMenuLabelComponent,
  HlmMenuSeparatorComponent,
  HlmMenuShortcutComponent,
  HlmSubMenuComponent,
} from '@spartan-ng/ui-menu-helm';

@Component({
  selector: 'app-dropdown',
  standalone: true,
  imports: [
    CommonModule,
    BrnMenuTriggerDirective,

    HlmMenuComponent,
    HlmSubMenuComponent,
    HlmMenuItemDirective,
    HlmMenuItemSubIndicatorComponent,
    HlmMenuLabelComponent,
    HlmMenuShortcutComponent,
    HlmMenuSeparatorComponent,
    HlmMenuItemIconDirective,
    HlmMenuGroupComponent,

    HlmButtonDirective,
    HlmIconComponent,
  ],
  providers: [
    PARENT_OR_NEW_MENU_STACK_PROVIDER,
  ],
  template: `
      <button
        type="button"
        [disabled]="disabled()"
        [brnMenuTriggerFor]="menu">
        <ng-content select="[button]"></ng-content>
      </button>

    <ng-template #menu>
      <hlm-menu>
        <ng-container [ngTemplateOutlet]="content"></ng-container>
      </hlm-menu>
    </ng-template>
  `,
})
export class DropdownComponent {
  @Input() content: TemplateRef<unknown> | null = null; // Accepts the template for dropdown content

  disabled = input<boolean>(false);
}

It might be a good idea to add it to the documentation