<!DOCTYPE html>
<html>

<head>
  <base href="." />
  <title>angular playground</title>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous">
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
  <script src="https://unpkg.com/core-js@2.4.1/client/shim.min.js"></script>
  <script src="https://unpkg.com/zone.js/dist/zone.js"></script>
  <script src="https://unpkg.com/zone.js/dist/long-stack-trace-zone.js"></script>
  <script src="https://unpkg.com/reflect-metadata@0.1.3/Reflect.js"></script>
  <script src="https://unpkg.com/systemjs@0.19.31/dist/system.js"></script>
  <script src="config.js"></script>
  <script>
    System.import('app')
      .catch(console.error.bind(console));
  </script>
</head>

<body>
  <div class="alert alert-danger" role="alert">
    This component is discontinued anymore. It's compatible with both Angular 2 & 4
  </div>
  <div class="alert alert-warning" role="alert">
    But if you are using Angular 4, should switch to my new component: <a href="https://www.npmjs.com/package/ngx-treeview">ngx-treeview</a>
  </div>
  <leo-app>
    loading...
  </leo-app>
</body>

</html>
### Angular ng2-dropdown-treeview component demo.
This component is discontinued anymore. It's compatible with both Angular 2 & 4. 
If you are using Angular 4, please switch to my new component: https://www.npmjs.com/package/ngx-treeview
System.config({
  //use typescript for compilation
  transpiler: 'typescript',
  //typescript compiler options
  typescriptOptions: {
    emitDecoratorMetadata: true
  },
  paths: {
    'npm:': 'https://unpkg.com/'
  },
  //map tells the System loader where to look for things
  map: {
    
    'app': './src',
    
    '@angular/core': 'npm:@angular/core/bundles/core.umd.js',
    '@angular/common': 'npm:@angular/common/bundles/common.umd.js',
    '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
    '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
    '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
    '@angular/http': 'npm:@angular/http/bundles/http.umd.js',
    '@angular/router': 'npm:@angular/router/bundles/router.umd.js',
    '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
    '@angular/animations': 'npm:@angular/animations/bundles/animations.umd.js',
    '@angular/platform-browser/animations': 'npm:@angular/platform-browser/bundles/platform-browser-animations.umd.js',
    '@angular/animations/browser': 'npm:@angular/animations/bundles/animations-browser.umd.js',
    
    '@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js',
    '@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js',
    '@angular/compiler/testing': 'npm:@angular/compiler/bundles/compiler-testing.umd.js',
    '@angular/platform-browser/testing': 'npm:@angular/platform-browser/bundles/platform-browser-testing.umd.js',
    '@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js',
    '@angular/http/testing': 'npm:@angular/http/bundles/http-testing.umd.js',
    '@angular/router/testing': 'npm:@angular/router/bundles/router-testing.umd.js',
    
    'rxjs': 'npm:rxjs',
    'typescript': 'npm:typescript@2.2.1/lib/typescript.js',
    'lodash' : 'npm:lodash/lodash.min.js',
    'ng2-dropdown-treeview' : 'npm:ng2-dropdown-treeview/bundles/ng2-dropdown-treeview.umd.js',
  },
  //packages defines our app package
  packages: {
    app: {
      main: './main.ts',
      defaultExtension: 'ts'
    },
    rxjs: {
      defaultExtension: 'js'
    }
  }
});
//main entry point
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {AppModule} from './app.module';

platformBrowserDynamic().bootstrapModule(AppModule)
import { Component } from '@angular/core';
import { TreeviewI18n } from 'ng2-dropdown-treeview';
import { I18n } from './i18n';
import { DefaultTreeviewI18n } from './default-treeview-i18n';

@Component({
    selector: 'leo-app',
    template: `
<div class="container">
    <h2>Angular 2 dropdown-treeview component demo</h2>
    <hr />
    <br />
    <div class="row">
        <label for="item-category" class="col-3 col-form-label">Language</label>
        <div class="col-9">
            <select class="form-control" [(ngModel)]="language">
                <option value="en">
                    English
                </option>
                <option value="vi">
                    Tiếng Việt
                </option>
            </select>
        </div>
    </div>
    <hr>
    <h4>Example 1: Primary features</h4>
    <leo-book></leo-book>
    <br />
    <h4>Example 2: Performance with 1000 items</h4>
    <leo-room></leo-room>
    <br />
    <h4>Example 3: Using pipe & i18n</h4>
    <leo-city></leo-city>
    <br />
    <h4>Example 4: Tree-view without drop-down & custom TreeviewConfig & custom TreeviewEventParser & custom template</h4>
    <leo-product></leo-product>
</div>
  `,
    providers: [
        { provide: TreeviewI18n, useClass: DefaultTreeviewI18n }
    ]
})
export class AppComponent {
    constructor(
        private i18n: I18n
    ) { }

    set language(language: string) {
        this.i18n.language = language;
    }

    get language() {
        return this.i18n.language;
    }
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { DropdownTreeviewModule } from 'ng2-dropdown-treeview';
import { AppComponent } from './app.component';
import { BookComponent } from './book/book.component';
import { CityComponent } from './city/city.component';
import { RoomComponent } from './room/room.component';
import { ProductComponent } from './product/product.component';
import { I18n } from './i18n';
import { DisabledOnSelectorDirective } from './disabled-on-selector.directive';

@NgModule({
    imports: [
        BrowserModule,
        FormsModule,
        DropdownTreeviewModule.forRoot()
    ],
    declarations: [
        BookComponent,
        CityComponent,
        RoomComponent,
        ProductComponent,
        AppComponent,
        DisabledOnSelectorDirective
    ],
    providers: [
        I18n
    ],
    bootstrap: [AppComponent]
})
export class AppModule { }
import { Injectable } from '@angular/core';

@Injectable()
export class I18n {
    language = 'en';
}
import { Injectable } from '@angular/core';
import { TreeviewItem, TreeviewI18n } from 'ng2-dropdown-treeview';
import { I18n } from './i18n';

@Injectable()
export class DefaultTreeviewI18n extends TreeviewI18n {
    constructor(
        protected i18n: I18n
    ) {
        super();
    }

    getText(checkededItems: TreeviewItem[], isAll: boolean): string {
        if (isAll) {
            return this.i18n.language === 'en' ? 'All' : 'Tất cả';
        }

        switch (checkededItems.length) {
            case 0:
                return this.i18n.language === 'en' ? 'Select options' : 'Chọn mục';
            case 1:
                return checkededItems[0].text;
            default:
                return this.i18n.language === 'en'
                    ? `${checkededItems.length} options selected`
                    : `${checkededItems.length} mục đã được chọn`;
        }
    }

    allCheckboxText(): string {
        if (this.i18n.language === 'en') {
            return 'All';
        } else {
            return 'Tất cả';
        }
    }

    filterPlaceholder(): string {
        if (this.i18n.language === 'en') {
            return 'Filter';
        } else {
            return 'Lọc';
        }
    }

    filterNoItemsFoundText(): string {
        if (this.i18n.language === 'en') {
            return 'No items found';
        } else {
            return 'Không có mục nào được tìm thấy';
        }
    }

    tooltipCollapseExpand(isCollapse: boolean): string {
        return isCollapse
            ? this.i18n.language === 'en' ? 'Expand' : 'Mở rộng'
            : this.i18n.language === 'en' ? 'Collapse' : 'Thu lại';
    }
}
import { Directive, Input, OnChanges, ElementRef, Renderer } from '@angular/core';

@Directive({
    selector: '[leoDisabledOnSelector]'
})
export class DisabledOnSelectorDirective implements OnChanges {
    @Input('leoDisabledOnSelector') leoDisabledOnSelector: string;
    @Input() disabled: boolean;
    private readonly nativeElement: HTMLElement;

    constructor(
        private el: ElementRef,
        private renderer: Renderer) {
        this.nativeElement = el.nativeElement;
    }

    ngOnChanges() {
        const elements = this.nativeElement.querySelectorAll(this.leoDisabledOnSelector);
        for (let i = 0; i < elements.length; i++) {
            this.renderer.setElementProperty(elements[i], 'disabled', this.disabled);
        }
    }
}
import { Injectable } from '@angular/core';
import { TreeviewItem } from 'ng2-dropdown-treeview';
import { I18n } from '../i18n';
import { DefaultTreeviewI18n } from '../default-treeview-i18n';

@Injectable()
export class CityTreeviewI18n extends DefaultTreeviewI18n {
    constructor(
        protected i18n: I18n
    ) {
        super(i18n);
    }

    getText(checkededItems: TreeviewItem[], isAll: boolean): string {
        if (isAll) {
            return this.i18n.language === 'en' ? 'All cities' : 'Tất cả thành phố';
        }

        switch (checkededItems.length) {
            case 0:
                return this.i18n.language === 'en' ? 'Select cities' : 'Chọn thành phố';
            case 1:
                return checkededItems[0].text;
            default:
                return this.i18n.language === 'en'
                    ? `${checkededItems.length} cities selected`
                    : `${checkededItems.length} thành phố đã được chọn`;
        }
    }

    allCheckboxText(): string {
        return super.allCheckboxText();
    }

    filterPlaceholder(): string {
        return super.filterPlaceholder();
    }

    filterNoItemsFoundText(): string {
        if (this.i18n.language === 'en') {
            return 'No cities found';
        } else {
            return 'Không có thành phố nào được tìm thấy';
        }
    }

    tooltipCollapseExpand(isCollapse: boolean): string {
        return super.tooltipCollapseExpand(isCollapse);
    }
}
import { Component, OnInit } from '@angular/core';
import { TreeviewI18n } from 'ng2-dropdown-treeview';
import { City, CityService } from './city.service';
import { CityTreeviewI18n } from './city-treeview-i18n';

@Component({
    selector: 'leo-city',
    template: `
<div class="row">
    <div class="col-12">
        <div class="alert alert-success" role="alert">
            Selected cities: {{values | json}}
        </div>
    </div>
    <div class="col-12">
        <div class="form-group row">
            <label for="city-category" class="col-3 col-form-label">City category</label>
            <div class="col-9">
                <leo-dropdown-treeview [items]="cities | leoTreeview:'name'" (selectedChange)="values = $event">
                </leo-dropdown-treeview>
            </div>
        </div>
    </div>
</div>
`, providers: [
        CityService,
        { provide: TreeviewI18n, useClass: CityTreeviewI18n }
    ]
})
export class CityComponent implements OnInit {
    cities: City[];
    values: City[];

    constructor(
        private service: CityService
    ) { }

    ngOnInit() {
        this.cities = this.service.getCities();
    }
}
import { Injectable } from '@angular/core';

export interface City {
    id: number;
    name: string;
    postCode: number;
}

@Injectable()
export class CityService {
    getCities(): City[] {
        return [
            {
                id: 1,
                name: 'Ho Chi Minh',
                postCode: 700000
            },
            {
                id: 2,
                name: 'Ha Noi',
                postCode: 100000
            },
            {
                id: 3,
                name: 'Da Nang',
                postCode: 550000
            }
        ];
    }
}
import { Component, Injectable, OnInit, ViewChild } from '@angular/core';
import * as _ from 'lodash';
import {
    TreeviewI18n, TreeviewItem, TreeviewConfig, TreeviewHelper, TreeviewComponent,
    TreeviewEventParser, OrderDownlineTreeviewEventParser, DownlineTreeviewItem
} from 'ng2-dropdown-treeview';
import { ProductService } from './product.service';

@Injectable()
export class ProductTreeviewConfig extends TreeviewConfig {
    isShowAllCheckBox = true;
    isShowFilter = true;
    isShowCollapseExpand = false;
    maxHeight = 500;
}

@Component({
    selector: 'leo-product',
    template: `
<template #tpl let-item="item"
    let-toggleCollapseExpand="toggleCollapseExpand"
    let-onCheckedChange="onCheckedChange">
    <div class="form-check">
        <i *ngIf="item.children" (click)="toggleCollapseExpand()" aria-hidden="true"
            class="fa" [class.fa-caret-right]="item.collapsed" [class.fa-caret-down]="!item.collapsed"></i>
        <label class="form-check-label">
            <input type="checkbox" class="form-check-input"
                [(ngModel)]="item.checked" (ngModelChange)="onCheckedChange()" [disabled]="item.disabled" />
            {{item.text}}
        </label>
        <label class="form-check-label">
            <i class="fa fa-trash" aria-hidden="true" title="Remove" (click)="removeItem(item)"></i>
        </label>
    </div>
</template>
<div class="row">
    <div class="col-6">
        <div class="form-group">
            <div class="d-inline-block">
                <leo-treeview [items]="items" [template]="tpl" (selectedChange)="onSelectedChange($event)">
                </leo-treeview>
            </div>
        </div>
    </div>
    <div class="col-6">
        <div class="alert alert-success" role="alert">
            Selected products:
            <div *ngFor="let row of rows">{{row}}</div>
        </div>
    </div>
</div>
`, providers: [
        ProductService,
        { provide: TreeviewEventParser, useClass: OrderDownlineTreeviewEventParser },
        { provide: TreeviewConfig, useClass: ProductTreeviewConfig }
    ]
})
export class ProductComponent implements OnInit {
    @ViewChild(TreeviewComponent) treeviewComponent: TreeviewComponent;
    items: TreeviewItem[];
    rows: string[];

    constructor(
        private service: ProductService
    ) { }

    ngOnInit() {
        this.items = this.service.getProducts();
    }

    onItemCheckedChange(item: TreeviewItem) {
        console.log(item);
    }

    onSelectedChange(downlineItems: DownlineTreeviewItem[]) {
        this.rows = [];
        downlineItems.forEach(downlineItem => {
            const item = downlineItem.item;
            const value = item.value;
            const texts = [item.text];
            let parent = downlineItem.parent;
            while (!_.isNil(parent)) {
                texts.push(parent.item.text);
                parent = parent.parent;
            }
            const reverseTexts = _.reverse(texts);
            const row = `${reverseTexts.join(' -> ')} : ${value}`;
            this.rows.push(row);
        });
    }

    removeItem(item: TreeviewItem) {
        TreeviewHelper.removeItem(item, this.items);
        this.treeviewComponent.raiseSelectedChange();
    }
}
import { Injectable } from '@angular/core';
import { TreeviewItem } from 'ng2-dropdown-treeview';

export class ProductService {
    getProducts(): TreeviewItem[] {
        const fruitCategory = new TreeviewItem({
            text: 'Fruit', value: 1, children: [
                { text: 'Apple', value: 11 },
                { text: 'Mango', value: 12 }
            ]
        });
        const vegetableCategory = new TreeviewItem({
            text: 'Vegetable', value: 2, children: [
                { text: 'Salad', value: 21 },
                { text: 'Potato', value: 22 }
            ]
        });
        vegetableCategory.children.push(new TreeviewItem({ text: 'Mushroom', value: 23, checked: false }));
        vegetableCategory.correctChecked(); // need this to make 'Vegetable' node to change checked value from true to false
        return [fruitCategory, vegetableCategory];
    }
}
import { Component, OnInit } from '@angular/core';
import { TreeviewConfig, TreeviewItem } from 'ng2-dropdown-treeview';
import { RoomService } from './room.service';

@Component({
    selector: 'leo-room',
    template: `
<div class="row">
    <div class="col-12">
        <div class="alert alert-success" role="alert">
            Selected rooms: {{values}}
        </div>
    </div>
    <div class="col-12">
        <div class="form-group row">
            <label for="item-category" class="col-3 col-form-label">Item category</label>
            <div class="col-9">
                <leo-dropdown-treeview [config]="config" [items]="items" (selectedChange)="values = $event">
                </leo-dropdown-treeview>
            </div>
        </div>
    </div>
</div>
`, providers: [
        RoomService
    ]
})
export class RoomComponent implements OnInit {
    items: TreeviewItem[];
    values: any[];
    config: TreeviewConfig = {
        isShowAllCheckBox: true,
        isShowFilter: true,
        isShowCollapseExpand: false,
        maxHeight: 500
    };

    constructor(
        private service: RoomService
    ) { }

    ngOnInit() {
        this.items = this.service.getRooms();
    }
}
import { Injectable } from '@angular/core';
import { TreeviewItem } from 'ng2-dropdown-treeview';

@Injectable()
export class RoomService {
    getRooms(): TreeviewItem[] {
        const items: TreeviewItem[] = [];
        for (let i = 0; i < 1000; i++) {
            const value: any = i === 0 ? { name: `${i}` } : i;
            const checked = i % 100 === 0;
            const item = new TreeviewItem({ text: `Room ${i}`, value: value, checked: checked });
            items.push(item);
        };
        return items;
    }
}
import { Component, OnInit } from '@angular/core';
import { TreeviewItem, TreeviewConfig } from 'ng2-dropdown-treeview';
import { BookService } from './book.service';

@Component({
    selector: 'leo-book',
    template: `
<div class="row">
    <div class="col-12">
        <div class="alert alert-success" role="alert">
            Selected books: {{values}}
        </div>
    </div>
    <div class="col-12">
        <div class="form-check">
            <label class="form-check-label">
                <input class="form-check-input" type="checkbox" [(ngModel)]="enableButton">
                Check/uncheck to enable/disable dropdown button
            </label>
        </div>
        <div class="form-group row">
            <label for="book-category" class="col-3 col-form-label">Book category</label>
            <div class="col-9">
                <div class="d-inline-block">
                    <leo-dropdown-treeview [config]="config" [items]="items" (selectedChange)="values = $event"
                        [disabled]="!enableButton" [leoDisabledOnSelector]="'button.dropdown-toggle'">
                    </leo-dropdown-treeview>
                </div>
            </div>
        </div>
    </div>
</div>
`, providers: [
        BookService
    ]
})
export class BookComponent implements OnInit {
    enableButton = true;
    items: TreeviewItem[];
    values: number[];
    config: TreeviewConfig = {
        isShowAllCheckBox: true,
        isShowFilter: true,
        isShowCollapseExpand: true,
        maxHeight: 500
    };

    constructor(
        private service: BookService
    ) { }

    ngOnInit() {
        this.items = this.service.getBooks();
    }
}
import { Injectable } from '@angular/core';
import { TreeviewItem } from 'ng2-dropdown-treeview';

export class BookService {
    getBooks(): TreeviewItem[] {
        const childrenCategory = new TreeviewItem({
            text: 'Children', value: 1, collapsed: true, children: [
                { text: 'Baby 3-5', value: 11 },
                { text: 'Baby 6-8', value: 12 },
                { text: 'Baby 9-12', value: 13 }
            ]
        });
        const itCategory = new TreeviewItem({
            text: 'IT', value: 9, children: [
                {
                    text: 'Programming', value: 91, children: [{
                        text: 'Frontend', value: 911, children: [
                            { text: 'Angular 1', value: 9111 },
                            { text: 'Angular 2', value: 9112 },
                            { text: 'ReactJS', value: 9113 }
                        ]
                    }, {
                        text: 'Backend', value: 912, children: [
                            { text: 'C#', value: 9121 },
                            { text: 'Java', value: 9122 },
                            { text: 'Python', value: 9123, checked: false }
                        ]
                    }]
                },
                {
                    text: 'Networking', value: 92, children: [
                        { text: 'Internet', value: 921 },
                        { text: 'Security', value: 922 }
                    ]
                }
            ]
        });
        const teenCategory = new TreeviewItem({
            text: 'Teen', value: 2, collapsed: true, disabled: true, children: [
                { text: 'Adventure', value: 21 },
                { text: 'Science', value: 22 }
            ]
        });
        const othersCategory = new TreeviewItem({ text: 'Others', value: 3, collapsed: true, disabled: true });
        return [childrenCategory, itCategory, teenCategory, othersCategory];
    }
}