npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ngx-st-dynamic-menu

v19.0.0

Published

- [Overview](#overview) - [Installation](#installation) - [Basic Usage](#basic-usage) - [Inputs](#inputs) - [MenuElementModel](#menuelementmodel) - [Usage Examples](#usage-examples) - [Best Practices](#best-practices)

Readme

Dynamic Menu Component - Complete Documentation

Table of Contents


Overview

The st-dynamic-menu component creates a dynamic action menu or button from an array of menu items. Features include:

  • Dropdown menu for multiple actions
  • Single button for single action
  • Material Design icons support
  • Router navigation support
  • Disabled state for individual items
  • Button color customization

Installation

npm install ngx-st-dynamic-menu

Import the module:

import { StDynamicMenuModule } from 'ngx-st-dynamic-menu';

@NgModule({
  imports: [StDynamicMenuModule]
})
export class AppModule { }

Basic Usage

// Component
menuActions: MenuElementModel[] = [
  { label: 'Edit', action: () => this.edit() },
  { label: 'Delete', action: () => this.delete() },
  { label: 'Archive', action: () => this.archive() }
];
<st-dynamic-menu [actionMenu]="menuActions"></st-dynamic-menu>

Inputs

actionMenu ⭐ (Required)

  • Type: MenuElementModel[]
  • Required: Yes
  • Description: Array of menu items to display. Renders as dropdown if multiple items, or single button if one item.
  • Example:
    actionMenu: MenuElementModel[] = [
      { label: 'Edit', action: () => this.edit() },
      { label: 'Delete', action: () => this.delete() }
    ];

singleShowAsButton

  • Type: boolean
  • Default: false
  • Description: When true and there's only one menu item, displays it as a button instead of a dropdown.
  • Example:
    [singleShowAsButton]="true"

buttonColor

  • Type: 'primary' | 'accent' | 'warn' | undefined
  • Default: undefined
  • Description: Material button color when displayed as single button.
  • Example:
    buttonColor="primary"
    buttonColor="warn"

MenuElementModel

Interface for menu items:

interface MenuElementModel {
  label: string;              // Required: Display text
  action?: () => void;        // Optional: Click handler function
  url?: string[];             // Optional: Router link (alternative to action)
  icon?: string;              // Optional: Material icon name
  disabled?: boolean;         // Optional: Disable the menu item
  color?: 'primary' | 'accent' | 'warn';  // Optional: Button color
}

Properties

label (Required)

  • Type: string
  • Description: Text to display for the menu item.
  • Example:
    { label: 'Edit User' }
    { label: 'Delete' }

action (Optional)

  • Type: () => void
  • Description: Function to execute when item is clicked. Use this OR url, not both.
  • Example:
    { label: 'Save', action: () => this.save() }

url (Optional)

  • Type: string[]
  • Description: Router link array. Use this OR action, not both.
  • Example:
    { label: 'View Details', url: ['/users', '123'] }

icon (Optional)

  • Type: string
  • Description: Material icon name to display.
  • Example:
    { label: 'Edit', icon: 'edit', action: () => this.edit() }
    { label: 'Delete', icon: 'delete', action: () => this.delete() }

disabled (Optional)

  • Type: boolean
  • Description: Disables the menu item.
  • Example:
    { label: 'Save', action: () => this.save(), disabled: this.form.invalid }

color (Optional)

  • Type: 'primary' | 'accent' | 'warn'
  • Description: Material button color (applies when shown as button).
  • Example:
    { label: 'Delete', action: () => this.delete(), color: 'warn' }

Usage Examples

Example 1: Basic Dropdown Menu

// Component
@Component({
  selector: 'app-user-actions',
  template: `
    <st-dynamic-menu [actionMenu]="userActions"></st-dynamic-menu>
  `
})
export class UserActionsComponent {
  userActions: MenuElementModel[] = [
    {
      label: 'Edit',
      icon: 'edit',
      action: () => this.editUser()
    },
    {
      label: 'Change Password',
      icon: 'lock',
      action: () => this.changePassword()
    },
    {
      label: 'Delete',
      icon: 'delete',
      action: () => this.deleteUser()
    }
  ];
  
  editUser(): void {
    console.log('Edit user');
  }
  
  changePassword(): void {
    console.log('Change password');
  }
  
  deleteUser(): void {
    console.log('Delete user');
  }
}

Example 2: Single Action as Button

// Component
@Component({
  selector: 'app-save-button',
  template: `
    <st-dynamic-menu
      [actionMenu]="saveAction"
      [singleShowAsButton]="true"
      buttonColor="primary">
    </st-dynamic-menu>
  `
})
export class SaveButtonComponent {
  saveAction: MenuElementModel[] = [
    {
      label: 'Save Changes',
      icon: 'save',
      action: () => this.save(),
      disabled: false
    }
  ];
  
  save(): void {
    console.log('Saving...');
  }
}

Example 3: Menu with Navigation

// Component
@Component({
  selector: 'app-navigation-menu',
  template: `
    <st-dynamic-menu [actionMenu]="navigationActions"></st-dynamic-menu>
  `
})
export class NavigationMenuComponent {
  navigationActions: MenuElementModel[] = [
    {
      label: 'View Details',
      icon: 'visibility',
      url: ['/users', '123']
    },
    {
      label: 'Edit User',
      icon: 'edit',
      url: ['/users', '123', 'edit']
    },
    {
      label: 'User History',
      icon: 'history',
      url: ['/users', '123', 'history']
    }
  ];
}

Example 4: Conditional Menu Items

// Component
@Component({
  selector: 'app-conditional-menu',
  template: `
    <st-dynamic-menu [actionMenu]="getActions()"></st-dynamic-menu>
  `
})
export class ConditionalMenuComponent {
  user = {
    id: 123,
    canEdit: true,
    canDelete: false,
    isActive: true
  };
  
  getActions(): MenuElementModel[] {
    const actions: MenuElementModel[] = [];
    
    if (this.user.canEdit) {
      actions.push({
        label: 'Edit',
        icon: 'edit',
        action: () => this.edit()
      });
    }
    
    if (this.user.isActive) {
      actions.push({
        label: 'Deactivate',
        icon: 'block',
        action: () => this.deactivate()
      });
    } else {
      actions.push({
        label: 'Activate',
        icon: 'check_circle',
        action: () => this.activate()
      });
    }
    
    if (this.user.canDelete) {
      actions.push({
        label: 'Delete',
        icon: 'delete',
        action: () => this.delete(),
        color: 'warn'
      });
    }
    
    return actions;
  }
  
  edit(): void {
    console.log('Edit');
  }
  
  activate(): void {
    console.log('Activate');
  }
  
  deactivate(): void {
    console.log('Deactivate');
  }
  
  delete(): void {
    console.log('Delete');
  }
}

Example 5: Disabled Menu Items

// Component
@Component({
  selector: 'app-form-actions',
  template: `
    <form [formGroup]="form">
      <!-- Form fields -->
      
      <st-dynamic-menu [actionMenu]="formActions"></st-dynamic-menu>
    </form>
  `
})
export class FormActionsComponent {
  form = this.fb.group({
    name: ['', Validators.required],
    email: ['', [Validators.required, Validators.email]]
  });
  
  get formActions(): MenuElementModel[] {
    return [
      {
        label: 'Save',
        icon: 'save',
        action: () => this.save(),
        disabled: this.form.invalid
      },
      {
        label: 'Reset',
        icon: 'refresh',
        action: () => this.reset(),
        disabled: this.form.pristine
      },
      {
        label: 'Cancel',
        icon: 'close',
        action: () => this.cancel()
      }
    ];
  }
  
  constructor(private fb: FormBuilder) {}
  
  save(): void {
    if (this.form.valid) {
      console.log('Saving:', this.form.value);
    }
  }
  
  reset(): void {
    this.form.reset();
  }
  
  cancel(): void {
    console.log('Cancelled');
  }
}

Example 6: Document Actions

// Component
@Component({
  selector: 'app-document-actions',
  template: `
    <st-dynamic-menu [actionMenu]="documentActions"></st-dynamic-menu>
  `
})
export class DocumentActionsComponent {
  document = {
    id: 1,
    title: 'Report.pdf',
    canEdit: true,
    canShare: true,
    canDelete: false
  };
  
  documentActions: MenuElementModel[] = [
    {
      label: 'Download',
      icon: 'download',
      action: () => this.download()
    },
    {
      label: 'Print',
      icon: 'print',
      action: () => this.print()
    },
    {
      label: 'Share',
      icon: 'share',
      action: () => this.share(),
      disabled: !this.document.canShare
    },
    {
      label: 'Edit',
      icon: 'edit',
      action: () => this.edit(),
      disabled: !this.document.canEdit
    },
    {
      label: 'Delete',
      icon: 'delete',
      action: () => this.delete(),
      disabled: !this.document.canDelete,
      color: 'warn'
    }
  ];
  
  download(): void {
    console.log('Downloading document');
  }
  
  print(): void {
    console.log('Printing document');
  }
  
  share(): void {
    console.log('Sharing document');
  }
  
  edit(): void {
    console.log('Editing document');
  }
  
  delete(): void {
    console.log('Deleting document');
  }
}

Example 7: Table Row Actions

// Component
@Component({
  selector: 'app-table-row',
  template: `
    <tr>
      <td>{{ item.name }}</td>
      <td>{{ item.status }}</td>
      <td>
        <st-dynamic-menu [actionMenu]="getRowActions(item)"></st-dynamic-menu>
      </td>
    </tr>
  `
})
export class TableRowComponent {
  @Input() item: any;
  
  getRowActions(item: any): MenuElementModel[] {
    return [
      {
        label: 'View',
        icon: 'visibility',
        url: ['/items', item.id]
      },
      {
        label: 'Edit',
        icon: 'edit',
        url: ['/items', item.id, 'edit']
      },
      {
        label: 'Duplicate',
        icon: 'content_copy',
        action: () => this.duplicate(item)
      },
      {
        label: 'Delete',
        icon: 'delete',
        action: () => this.delete(item),
        color: 'warn'
      }
    ];
  }
  
  duplicate(item: any): void {
    console.log('Duplicating:', item);
  }
  
  delete(item: any): void {
    console.log('Deleting:', item);
  }
}

Example 8: Admin Actions with Permissions

// Component
@Component({
  selector: 'app-admin-actions',
  template: `
    <st-dynamic-menu [actionMenu]="adminActions"></st-dynamic-menu>
  `
})
export class AdminActionsComponent {
  permissions = {
    canManageUsers: true,
    canManageSettings: false,
    canViewLogs: true,
    canExportData: true
  };
  
  get adminActions(): MenuElementModel[] {
    const actions: MenuElementModel[] = [];
    
    if (this.permissions.canManageUsers) {
      actions.push({
        label: 'Manage Users',
        icon: 'people',
        url: ['/admin', 'users']
      });
    }
    
    if (this.permissions.canManageSettings) {
      actions.push({
        label: 'Settings',
        icon: 'settings',
        url: ['/admin', 'settings']
      });
    }
    
    if (this.permissions.canViewLogs) {
      actions.push({
        label: 'View Logs',
        icon: 'list_alt',
        url: ['/admin', 'logs']
      });
    }
    
    if (this.permissions.canExportData) {
      actions.push({
        label: 'Export Data',
        icon: 'file_download',
        action: () => this.exportData()
      });
    }
    
    return actions;
  }
  
  exportData(): void {
    console.log('Exporting data');
  }
}

Best Practices

  1. Use clear, action-oriented labels:

    { label: 'Save Changes' }  // Good
    { label: 'Save' }          // Good
    { label: 'Click Here' }    // Bad
  2. Add icons for better UX:

    { label: 'Edit', icon: 'edit', action: () => this.edit() }
  3. Use appropriate colors:

    { label: 'Delete', color: 'warn' }  // Destructive actions
    { label: 'Save', color: 'primary' }  // Primary actions
  4. Disable invalid actions:

    { label: 'Save', disabled: this.form.invalid }
  5. Choose between action and url:

    // For navigation - use url
    { label: 'View', url: ['/items', id] }
       
    // For operations - use action
    { label: 'Delete', action: () => this.delete() }
  6. Filter actions based on permissions:

    getActions(): MenuElementModel[] {
      return this.allActions.filter(action => 
        this.hasPermission(action.permission)
      );
    }

Component Behavior

Multiple Items (Dropdown)

  • Displays as Material menu button with dropdown
  • Click to open dropdown
  • Click item to execute action or navigate

Single Item

  • Default: Shows as dropdown with one item
  • With singleShowAsButton: Shows as regular button

Navigation vs Action

  • url: Navigates using Angular Router
  • action: Executes provided function

Common Use Cases

  1. Entity Actions: Edit, delete, duplicate entities
  2. Document Actions: Download, print, share documents
  3. Table Row Actions: Actions for table rows
  4. Form Actions: Save, reset, cancel forms
  5. Admin Actions: Management and configuration actions
  6. Navigation Menus: Quick access to related pages

This documentation covers all inputs and usage patterns for the Dynamic Menu component.