npm package: @smartbit4all/tree
Description: **Please note the all versions from `@smartbit4all/tree^2.0.0` are not backward compatible!**
Latest version: 2.2.13

# Smart Tree

**Please note the all versions from `@smartbit4all/tree^2.0.0` are not backward compatible!**

Stable versions:

-   [`@smartbit4all/tree^2.0.0`](https://www.npmjs.com/package/@smartbit4all/tree/v/2.0.0)
-   [`@smartbit4all/tree^1.2.3`](https://www.npmjs.com/package/@smartbit4all/tree/v/1.2.3)

## References

These packages must be updated in case of a new version:

-   There are no references yet

---

## How to use

### Installation

Go to your project, open the terminal and use the following command:

    npm i @smartbit4all/tree

Then import it in the AppModule:

`app.module.ts:`

    import { SmarttreeModule } from '@smartbit4all/tree';
    ...
    @NgModule({
        declarations: [...]
        imports: [
            ...
            SmarttreeModule,
        ],
        providers: [
            ...
            SmarttreeService,
    	    TreeService
        ]
        ...
    })

### Usage

`terminal:`

    >>> cd src/app
    >>> mkdir services
    >>> cd ./services
    >>> ng g s tree

**First option:** Create a custom inheritance of the `SmarttreeService`:
`tree.service.ts:`

    @Injectable({
        providedIn: 'root'
    })
    export class MainTreeService extends SmarttreeService<SmartTreeModel> {
        constructor(
            private apiService: BffApiTreeService
        ) {
            super();
        }

        override async downloadTree(): Promise<SmartTreeModel> {
            const treeModel = await this.apiService.getRootNodes().toPromise();
            if (!treeModel) {
                throw new Error('TreeModel is undefined!');
            }
            return treeModel;
        }

        override async downloadChildren(parentNode: SmartTreeNode): Promise<SmartTreeModel> {
            const children = await this.apiService.getChildrenNodes(parentNode).toPromise();
            if (!children) {
                throw new Error(`Node with uri ${parentNode.identifier} does not have children.`);
            }

            return children;
        }

        override onTreeNodeClick(treeNode: SmartTreeNode): void {
            // Handle tree node click
        }

        override onTreeNodeOpen(treeNode: SmartTreeNode): void {
            if (treeNode.expanded && treeNode.childrenNodes && treeNode.childrenNodes.length) {
                treeNode.expanded = false;

                await this.apiService
                    .nodeClosed({ identifier: treeNode.identifier! })
                    .toPromise()
                    .then(() => {
                        this.downloadTree().then((tree) => {
                            this.treeFromBackend = tree;
                            this.syncTree();
                        });
                    });
                return;
            }
            await this.downloadChildren(treeNode).then((children: SmartTreeNode[]) => {
                treeNode.expanded = true;
                treeNode.children = children.nodes;

                this.syncTree();
            });
        }

        actionLabelTranslator(code: string): string {
            // Return a readable version of the code
            // Example:
            //  code: addUser
            //  return: Add user
         }

        override syncTree(): void {
            if (!this.treeFromBackend) {
                throw new Error('There is no treeFromBackend available!');
            }
            if (!this.smartTreeModel) {
                this.smartTreeModel = {
                    navigationUrlsByNodeType: [],
                    rootNodes: []
                };
            }

            let smartTreeNodes: SmartTreeNode[] = this.treeFromBackend.nodes.map((node: SmartTreeNode) => {
                if (node.actions && node.actions.length) {
                    // The buttons are customizable
                    // Example:
                    let button: SmartTreeNodeButton = {
                        type: SmartTreeNodeButtonType.MENU,
                        icon: 'more_vert',
                        menuItemButtons: node.actions.map((action: UiAction) => {
                            type: SmartTreeNodeButtonType.NORMAL,
                            label: this.actionLabelTranslator(action.code),
                            callback: this.doAction.bind(this),
                            args: [action, node.identifier]
                        }),
                    }
                    node.button = button;
                }
            })

            // Choose from the followings:
            // #1 set up custom buttons
            this.smartTreeModel.rootNodes = this.setButtonsForTreeNodes(this.treeFromBackend.rootNodes);
            // #2 use the tree without custom buttons
            this.smartTreeModel.rootNodes = this.treeFromBackend.rootNodes;

            this.findSelected(this.smartTreeModel.rootNodes);

            this.smartTreeModelChanged.next(this.smartTreeModel);
        }

        // This function is not mandatory and also not defined in the SmarttreeService<T>!
        // The setButtonsForTreeNodes(rootNodes: SmartTreeNode[]) is just a recommendation, place it
        // into your custom TreeService or into a util file.
        setButtonsForTreeNodes(rootNodes: SmartTreeNode[]): SmartTreeNode[] {
            let smartTreeNodes: SmartTreeNode[] = [];

            rootNodes.map((node: SmartTreeNode) => {
                if (node.actions && node.actions.length) {
                    // The buttons are customizable
                    // Example usage:
                    let button: SmartTreeNodeButton = {
                        type: SmartTreeNodeButtonType.MENU,
                        icon: 'more_vert',
                        menuItemButtons: node.actions.map((action: UiAction) => {
                            type: SmartTreeNodeButtonType.NORMAL,
                            label: this.actionLabelTranslator(action.code),
                            callback: this.doAction.bind(this),
                            args: [action, node.identifier]
                        }),
                    }
                    node.button = button;
                }

                if (node.childrenNodes) {
                    node.childrenNodes = this.setButtonsForTreeNodes(node.childrenNodes);
                }

                smartTreeNodes.push(node);
            })

            return smartTreeNodes;
        }

        // Feel free to choose from the followings:
        // First option
        doAction(action: UiAction, identifier: string, args?: any[]): void { ... }
        // Second option
        doAction(args: any[]): void { ... }
    }

**Second option:** Create a custom inheritance of the `SmarttreeGenericService`:

    export class MainTreeService extends SmarttreeGenericService {
        constructor(
            viewContext: SmartViewContextService,
            treeService: TreeService,
        ) {
            super(viewContext, treeService);

            // Example data
            this.configureTree({
                pageName: Pages.Main,
                treeid: `model.tree`,
                viewContextName: Pages.VIEW_CONTEXT
            });
        }

        override getLabelByAction(action: UiAction): string {
            return getActionTranslator(action.code!).title;
        }

        override async doAction(args: any[]): Promise<void> {
            let uiAction: UiAction = args[0];
            let nodeUuid: string = args[1];
            // Do action...
        }
    }

`app.component.html:`

    <smart-tree [treeStyle]="treeStyle!" [treeService]="treeService"></smart-tree>

`app.component.ts:`

    export class AppComponent {
        treeStyle?: TreeStyle;

        constructor(public treeService: MainTreeService) {
            // set up the BffApiTreeService (headers and url)

            this.treeService.initialize();

            this.treeStyle = {
                levelBackgroundColor: ['rgba(34, 107, 193, 0.3)', 'rgba(0, 0, 0, 0.15)'],
                activeStyle: {
                    backgroundColor: '#1A5DBE',
                    color: '#FFF'
                }
            };
        }

    }

---

## Version logs

## @smartbit4all/tree v2.1.12

**Type: Update**
The `SmarttreeService<T>` got a new property: **errorMessage: string**.

This error message will appear when the tree is not available.

## @smartbit4all/tree v2.1.2

**Type: Update**

From now on the backend's responsibility to handle clicking on an already selected node.

## @smartbit4all/tree v2.1.1

**Type: Update**

In this update a generic tree service has been added to the package.

## @smartbit4all/tree v2.0.1

**Type: Update**

`@smartbit4all/icon` support.

## @smartbit4all/tree v2.0.0

**Type: Major update**

The tree requires a new `SmartTreeModel` from the backend with the changed `SmartTreeNode`s:

    export interface SmartTreeModel {
        identifier?: string;
        rootNodes?: Array<SmartTreeNode>;
    }

    export interface SmartTreeNode {
        identifier?: string;
        icon?: string;
        caption?: string;
        classes?: Array<string>;
        hasChildren?: boolean;
        childrenNodes?: Array<SmartTreeNode>;
        expanded?: boolean;
        selected?: boolean;
        level?: number;
        shortDescription?: string;
        nodeType?: string;
        button?: object;
        actions?: Array<UiAction>;
    }

SmartreeService changes:

-   `SmarttreeService<T, N>` -> `SmarttreeService<T>`
-   The `convertRootNodesToSmartTreeNodes(rootNodes: Array<N>)` function has been removed.

## @smartbit4all/tree v1.1.4

**Type: Bugfix**

Click event on customly defined button was not stopped from propagation.

## @smartbit4all/tree v1.1.1

**Type: Bugfix**

A bug has been fixed which caused that the tree could only expand the root nodes of the tree.

## @smartbit4all/tree v1.1.0

**Type: Feature**

This update contains two changes:

1. SmartForm treeStyle before this update could not set the styling of the root nodes. From now the root nodes styling can be setted too.
2. SmartTreeNode interface has been extended with a new SmartTreeNodeButton typed property. With this property you can define and add buttons to every treeNode and define the callback functions.

How to use the features of this update:

1.  TreeStyle: levelBackground is an array contains the colors in string. The index of an element refers to the level of the treeNode

        this.treeStyle = {
            levelBackgroundColor: ['rgba(34, 107, 193, 0.3)', 'rgba(0, 0, 0, 0.15)'],
                activeStyle: {
                    backgroundColor: '#1A5DBE',
                    color: '#FFF'
                }
            };

2.  SmartTreeNodeButton: add button property to the treeNode object.
    SmartTreeNodeButton has 3 different type:

-   ICON
-   MENU
-   NORMAL

## @smartbit4all/tree v1.0.1

**Type: Update**

The package has been published.

## @smartbit4all/tree v0.1.0

**Type: Feature**

The usage of `tree` has been redesigned in order to support the BFF APIs.

New things in this update:

-   `SmarttreeServiceDescriptor<T, N>`
-   `export class SmarttreeService<T, N> implements SmarttreeServiceDescriptor<T, N>`

Things that have been changed:

-   `SmarttreeComponent` **does not require** a `SmarttreeModel` anymore
-   `SmarttreeComponent` **requires** an inheritance of the `SmarttreeService`
-   Node click events are no longer handled by the component
-   Node click events can be handled manually in the inheritance of the `SmarttreeService`

## @smartbit4all/tree v0.0.8

**Type: Feature**

This version contains a routing update, which allows you to navigate to a named outlet.

**The change:**

The `navigationUrl` property of the `SmartTreeNodeType` has changed from _string_ to _any[]_.

    export interface SmartTreeNodeType {
        nodeType: string;
        navigationUrl: any[];
    }

## @smartbit4all/tree v0.0.7

**Type: Bugfix**

A bug has been fixed which caused routing problems while navigating with objectUri.

The change itself: `this.router.navigate(['${navigationUrlByNodeType.navigationUrl}'], { queryParams: { uri: node.objectUri }});`

## @smartbit4all/tree v0.0.6

**Type: Bugfix**

The last deselected node could not be selected directly.

## @smartbit4all/tree v0.0.5

**Type: Bugfix**

There were no option to deselect an already selected tree node.

## @smartbit4all/tree v0.0.4

**Type: Bugfix**

Navigating on node click was not working properly.

`this.router.navigateByUrl` has been replaced with `this.router.navigate`.

## @smartbit4all/tree v0.0.3

**Type: Feature**

The generated TreeModel has been replaced with SmartTreeModel.

The new SmartTreeModel and SmartTreeNode have new features like setting the node type and its navigation target. Also some optional fields have been changed to required ones.