LIDOR SYSTEMS

Advanced User Interface Controls and Components

Context Menu with Edit Options for Angular TreeView

Created: 19 May 2017

Angular TreeView component by default doesn't have a Context Menu attached. Having a context menu is useful in cases when you want to extend the TreeView functionality and provide better user interface. For example, if you want to edit or copy/paste items, you can create a context menu with options that can provide that kind of functionality. In this article, you will learn how to do that.

TreeView component is part of IntegralUI Web
a suite of UI Components for development of web apps

If you have any questions, don't hesitate to contact us at support@lidorsystems.com

To open the context menu, right-click anywhere inside the TreeView space. Four options will appear:

  • Edit - displays a text editor for selected item
  • Cut - marks the selected item as movable
  • Copy - creates an exact deep copy of selected item
  • Paste - places the marked or cloned item below selected item

As demo shows, using a context menu you can dynamically edit and move items in the Angular TreeView.

How to Add a ContextMenu to the Angular TreeView Component

At first, to add a context menu to the TreeView, you only need to create an object that holds all menu settings. Then add the iuiContextMenu property in HTML and link it with the menu settings object. In addition, add an event handler for menuItemClick event, which is fired when a menu option is clicked:

// Context Menu settings
private menuSettings: any = {
    appRef: null,
    items: [
        { id: 1, text: "Edit" },
        { id: 2, type: "separator" },
        { id: 3, text: "Cut" },
        { id: 4, text: "Copy" },
        { id: 5, text: "Paste" }
    ]
}

// ContextMenu events ----------------------------------------------------------------

menuItemClick(e: any){
    if (e.item){
        // Action depends on selected menu option
        switch (e.item.id){
            case 1: // Edit
                break;

            case 3: // Cut
                break;

            case 4: // Copy
                break;

            case 5: // Paste
                break;
        }
   }
}                        
<iui-treeview [items]="data" [controlStyle]="treeStyle" [iuiContextMenu]="menuSettings" (itemClick)="menuItemClick($event)" #treeview>
    <template let-item>
        <span *ngIf="item!=editItem" [ngStyle]="{ opacity: item == moveItem ? 0.5 : 1 }">{{item.text}}</span>
        <input *ngIf="item==editItem" type="text" [(ngModel)]="item.text" (keydown)="editorKeyDown($event)" [iuiFocus]="editorFocused" (focus)="selectContent($event)" (blur)="editorLostFocus()" />
    </template>
</iui-treeview>
                            

Note Make sure that the appRef of menu object has its value set to point to the reference of the root component. This is required for dynamic creation of ContextMenu and its visibility.

Now, whenever TreeView is right-clicked, the context menu will appear. However, it is not yet functional.

How to Edit Item using a Context Menu

To edit items, at first you need to have some kind of text editor. For example, you can use the input element that will appear whenever the item is in edit mode, otherwise will remain hidden. The item template in this case is:

private isEditActive: boolean = false;
private editItem: any = null;
private originalText: string = '';
private editorFocused: boolean = false; 

// Selects the whole text in the text editor
selectContent(e: any){
    if (e.target){
        setTimeout(function(){
            e.target.setSelectionRange(0, e.target.value.length);
        }, 1);
    }
}                            
<iui-treeview [items]="data" [controlStyle]="treeStyle" [iuiContextMenu]="menuSettings" (itemClick)="menuItemClick($event)" #treeview>
    <template let-item>
        <span *ngIf="item!=editItem" [ngStyle]="{ opacity: item == moveItem ? 0.5 : 1 }">{{item.text}}</span>
        <input *ngIf="item==editItem" type="text" [(ngModel)]="item.text" (keydown)="editorKeyDown($event)" [iuiFocus]="editorFocused" (focus)="selectContent($event)" (blur)="editorLostFocus()" />
    </template>
</iui-treeview>
                            

Now we can add functionality to Edit option of the context menu. In menuItemClick event handler, add a code that will check for the id value of the menu option and provide a corresponding action In case of Edit option, show a text editor in place of selected item:

menuItemClick(e: any){
    if (e.item){
        // Action depends on selected menu option
        switch (e.item.id){
            case 1: // Edit
                this.showEditor(this.treeview.selectedItem);
                break;

            case 3: // Cut
                break;

            case 4: // Copy
                break;

            case 5: // Paste
                break;
        }
    }
}

showEditor(item: any){
    // A timeout is required in this case, because when edit option from context menu is selected
    // there is a small delay prior context menu closes and focus is transfered from context menu to the item
    // In other cases (when context menu is not used), the timout is not needed
    
    let self = this;

    let editTimeout = setTimeout(function(){
        self.originalText = item.text;
        self.isEditActive = true;
        self.editItem = item;
        self.editorFocused = true;

        clearTimeout(editTimeout);
    }, 1);
}                          

The showEditor method sets the selected item in edit mode. This updates the HTML, so that now the item label is hidden and the text editor appears.

When text editor is active, to confirm changes press ENTER, to cancel them press ESCAPE or click anywhere outside the editor. This is done by handling the keyDown event for the input element, and blur event handler that closes the editor when you click outside of its space.

closeEditor(){
    this.editItem = null;
    this.originalText = '';
    this.editorFocused = false;
}

editorKeyDown(e: any){
    if (this.editItem){
        switch (e.keyCode){
            case 13: // ENTER
                this.closeEditor();
                break;
                
            case 27: // ESCAPE
                this.editItem.text = this.originalText;
                this.closeEditor();
                break;
        }
    }
}

editorLostFocus(){
    if (this.editItem)
        this.editItem.text = this.originalText;

    this.closeEditor();
}                            

How to Cut, Copy and Paste Items from Context Menu

Other options from the context menu will move the item or its copy at new position within the TreeView.

The Cut option simply marks the selected item as movable. This updates the style of the item in HTML so that it will appear as grayed.

The Copy option creates a deep copy of the selected item.

menuItemClick(e: any){
    if (e.item){
        // Action depends on selected menu option
        switch (e.item.id){
            case 1: // Edit
                this.showEditor(this.treeview.selectedItem);
                break;

            case 3: // Cut
                // Mark the currently selected item for moving
                this.moveItem = this.treeview.selectedItem;
                break;

            case 4: // Copy
                // Create a clone of currently selected item
                this.clone = this.treeview.cloneItem(this.treeview.selectedItem);
                break;

            case 5: // Paste
                break;
        }
    }
}                            

Finally, the Paste option check whether item that is pasted is the marked item or a clone. If it is the marked item, it is removed from the tree hierarchy and then placed at position below the currently selected item. To place the pasted item below selected item, we need to locate the parent item of selected item and then call the insertItemAt method with new index at which item will be placed.

menuItemClick(e: any){
    if (e.item){
        // Action depends on selected menu option
        switch (e.item.id){
            case 1: // Edit
                this.showEditor(this.treeview.selectedItem);
                break;

            case 3: // Cut
                // Mark the currently selected item for moving
                this.moveItem = this.treeview.selectedItem;
                break;

            case 4: // Copy
                // Create a clone of currently selected item
                this.clone = this.treeview.cloneItem(this.treeview.selectedItem);
                break;

            case 5:  // Paste
                let pasteItem: any = null;

                // Get the item to be pasted
                // From CUT
                if (this.moveItem){
                    pasteItem = this.moveItem;
                    this.treeview.removeItem(pasteItem);
                }
                // From COPY
                else if (this.clone)
                    pasteItem = this.clone;

                // Paste the item at position below the selected item
                if (pasteItem){
                    let parent: any = this.treeview.getItemParent(this.treeview.selectedItem);
                    let list: Array = parent && parent.items ? parent.items : this.data;

                    if (list){
                        let index: number = list.indexOf(this.treeview.selectedItem);
                        if (index >= 0)
                            this.treeview.insertItemAt(pasteItem, index+1, parent);
                    }
                }

                this.moveItem = null;
                break;
        }
    }
}                            

Conclusion

Angular TreeView component doesn't have a built-in ContextMenu functionality. You can add this functionality, by using the ContextMenu component. You can create custom context menu with options that allows you to edit, copy and paste items in the TreeView.

The TreeView component is part of IntegralUI Web.

Newsletter


Sign-up to our newsletter and you will receive news on upcoming events, latest articles, samples and special offers.
Name: Email: *
*By checking this box, I agree to receive a newsletter from Lidor Systems in accordance with the Privacy Policy. I understand that I can unsubscribe from these communications at any time by clicking on the unsubscribe link in all emails.