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-menuImport 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
Use clear, action-oriented labels:
{ label: 'Save Changes' } // Good { label: 'Save' } // Good { label: 'Click Here' } // BadAdd icons for better UX:
{ label: 'Edit', icon: 'edit', action: () => this.edit() }Use appropriate colors:
{ label: 'Delete', color: 'warn' } // Destructive actions { label: 'Save', color: 'primary' } // Primary actionsDisable invalid actions:
{ label: 'Save', disabled: this.form.invalid }Choose between action and url:
// For navigation - use url { label: 'View', url: ['/items', id] } // For operations - use action { label: 'Delete', action: () => this.delete() }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
- Entity Actions: Edit, delete, duplicate entities
- Document Actions: Download, print, share documents
- Table Row Actions: Actions for table rows
- Form Actions: Save, reset, cancel forms
- Admin Actions: Management and configuration actions
- Navigation Menus: Quick access to related pages
This documentation covers all inputs and usage patterns for the Dynamic Menu component.
