Available in the Full Version

Tree Grid - KnockoutJS Binding

This sample features remote loading of more than 10,000 records and demonstrates editing grid rows with KnockoutJS binding.
Show
records
PictureNamePositionHire DateAnnual SalaryReports To

This sample is designed for a larger screen size.

On mobile, try rotating your screen, view full size, or email to another device.

Custom function is used to render the paging breadcrumb to display the picture and name of the parent employee. Other features enabled in the Tree Grid are selection and remote paging. Note that saving employee is done only in the local data source, not pushed back to the server.

Code View

Copy to Clipboard
@using Infragistics.Web.Mvc
@using IgniteUI.SamplesBrowser.Models.Northwind
@using System.Data

<!DOCTYPE html>

<html>
<head>
    <title></title>

    <!-- Ignite UI for jQuery Required Combined CSS Files -->
    <link href="http://cdn-na.infragistics.com/igniteui/2024.2/latest/css/themes/infragistics/infragistics.theme.css" rel="stylesheet" />
    <link href="http://cdn-na.infragistics.com/igniteui/2024.2/latest/css/structure/infragistics.css" rel="stylesheet" />

    <script src="http://ajax.aspnetcdn.com/ajax/modernizr/modernizr-2.8.3.js"></script>
    <script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
    <script src="http://code.jquery.com/ui/1.11.1/jquery-ui.min.js"></script>

    <script src="http://igniteui.com/js/external/knockout-latest.js"></script>
    <script src="http://igniteui.com/js/external/knockout.mapping-latest.js"></script>
    <script src="http://cdn-na.infragistics.com/igniteui/2024.2/latest/js/extensions/infragistics.ui.editors.knockout-extensions.js"></script>
    <script src="http://cdn-na.infragistics.com/igniteui/2024.2/latest/js/extensions/infragistics.ui.combo.knockout-extensions.js"></script>

    <!-- Ignite UI for jQuery Required Combined JavaScript Files -->
    <script src="http://cdn-na.infragistics.com/igniteui/2024.2/latest/js/infragistics.core.js"></script>
    <script src="http://cdn-na.infragistics.com/igniteui/2024.2/latest/js/infragistics.lob.js"></script>

    <script src="http://igniteui.com/data-files/org-chart-available-positions.js"></script>

    <style type="text/css">
        #sampleContainer fieldset {
            margin:0;
        }
        #editForm .ui-dialog-title{
            text-transform:uppercase;
        }
        .employee-table {
            width:100%;
            overflow:hidden;
            border-bottom: 1px solid #ccc;
            border-top: 1px solid #ccc;
            margin-bottom:10px;
            padding-bottom:6px;
            padding-top:20px;
        }
        .employee-data {
            float:left;
            width:80%;
        }
        .employee-pic {
            float:right;
            width:20%;
        }
        .form-row {
            width:100%;
            overflow:hidden;
            margin-bottom:14px;
        }
        #sampleContainer fieldset .row-edit-dialog-container-head {
             font-size:16px;
         }
        #sampleContainer fieldset .row-edit-dialog-container-head label {
            display: inline !important;
        }
        .row-edit-dialog-container-head #idEditor {
            line-height:32px;
            display:inline;
        }  
        .form-row label {
            float:left;
            margin:0 10px 0 0 !important;
            line-height:32px;
            width:30%;
            text-align: right;
        }
        .emp-pic {
            width: 50px;
            height: 50px;
            border-radius: 25px;
            -webkit-border-radius: 25px;
            -moz-border-radius: 25px;
			display: inline-block;
        }

        .emp-pic-big {
            width: 100px;
            height: 100px;
            border-radius: 50px;
            -webkit-border-radius: 50px;
            -moz-border-radius: 50px;
            float:right;
        }

        .emp-pic-breadcrumb {
            margin-right: 5px;
            float: left;
            width: 16px;
            height: 16px;
            border-radius: 8px;
            -webkit-border-radius: 8px;
            -moz-border-radius: 8px;
        }

        .breadcrumb-label {
            margin-right: 5px;
            float: left;
        }

        .breadcrumb-container {
            display: inline-block;
        }

        #orgChartGrid_container {
            float: left;
        }

        #org-chart-edit {
            float: left;
        }

            #org-chart-edit.label {
                display: block;
                margin-top: 5px;
            }

        .ui-dialog-buttonset {
                float: right;
        }
        .ui-dialog .ui-dialog-titlebar {
            padding:0.7em 1em;
        }
        .ui-dialog .ui-dialog-titlebar.ui-state-focus, .ui-dialog.ui-igdialog {
            border-color:#888 !important;
        }
    </style>
</head>
<body>
    <script type="text/javascript">
        var contextRowFunc = function (dataRow, $textArea, parents, mode) {
            var contextRowText = "<div class=\"breadcrumb-container\">";

            // Default text when there are no parents
            if (!parents || parents.length == 1) {
                contextRowText += "Organizational Chart";
            }
            else {
                $(parents).slice(0, -1).each(function (index) {
                    contextRowText += "<div class=\"emp-pic-breadcrumb\" style=\"background: url(" + this.row["Picture"].replace(/50/g, "16") + ") no-repeat;\"></div><div class=\"breadcrumb-label\">" + this.row["FirstName"] + " " + this.row["LastName"] + " > </div>";
                });

            }
            contextRowText += "</div>";

            return contextRowText;
        }

        function formatReportsTo(val, record) {
            var manager = ko.utils.arrayFirst(orgChartVM.directors(), function (item) {
                return item.EmployeeID() === record.ReportsTo;
            });

            if (manager) {
                return manager.FullName();
            }
            else {
                return '';
            }
        }

        var Employee = function (EmployeeID, FirstName, LastName, ReportsTo, Picture, Position, HireDate, AnnualSalary) {
            var self = this;
            self.EmployeeID = ko.observable(EmployeeID);
            self.FirstName = ko.observable(FirstName);
            self.LastName = ko.observable(LastName);
            self.ReportsTo = ko.observableArray([ReportsTo]);
            self.Picture = ko.observable(Picture);
            self.BigPicture = self.Picture() ? self.Picture().replace(/50/g, "100") : '';
            self.Position = ko.observableArray([Position]);
            self.HireDate = ko.observable(HireDate);
            self.AnnualSalary = ko.observable(AnnualSalary);
            self.FullName = ko.computed(function () {
                return self.FirstName() + ' ' + self.LastName();
            });
        }

        function EmployeesViewModel() {
            var self = this;
            self.selectedEmployee = ko.observable();

            self.employeeSelectionChanged = function (evt, ui) {
                if (!ui.row) { return; }

                var employee = $("#orgChartGrid").igTreeGrid("findRecordByKey", ui.row.id);

                self.selectedEmployee(new Employee(
                                 employee.EmployeeID,
                                 employee.FirstName,
                                 employee.LastName,
                                 employee.ReportsTo,
                                 employee.Picture,
                                 employee.Position,
                                 employee.HireDate,
                                 employee.AnnualSalary));

                $('#editForm').igDialog("open");
            };

            self.availablePositions = orgChartAvailablePositions;

            self.directors = ko.observableArray();
            $.get('@Url.Action("GetDirectors")', function (data) {
                 $(data).each(function () {
                     self.directors.push(new Employee(this.EmployeeID, this.FirstName, this.LastName, null, null, null, null, null));
                 });
             });

             self.save = function () {
                 var empRecord = $("#orgChartGrid").igTreeGrid("findRecordByKey", self.selectedEmployee().EmployeeID());

                 empRecord.FirstName = self.selectedEmployee().FirstName();
                 empRecord.LastName = self.selectedEmployee().LastName();
                 empRecord.ReportsTo = self.selectedEmployee().ReportsTo()[0];
                 empRecord.Picture = self.selectedEmployee().Picture();
                 empRecord.Position = self.selectedEmployee().Position()[0];
                 empRecord.HireDate = self.selectedEmployee().HireDate();
                 empRecord.AnnualSalary = self.selectedEmployee().AnnualSalary();

                 $("#orgChartGrid").igTreeGrid("commit");
                 $("#editForm").igDialog("close");
             };

             self.cancel = function () {
                 $("#editForm").igDialog("close");
             };
         }
         orgChartVM = new EmployeesViewModel();

         $(function () {
             ko.applyBindings(orgChartVM);

             $("#editForm").igDialog({
                 headerText: "Edit Employee",
                 state: "closed",
                 modal: true,
                 draggable: false,
                 resizable: false,
                 height: "460px",
                 width: "600px"
             });
         });
    </script>
    @(Html.Infragistics().TreeGrid<IgniteUI.SamplesBrowser.Models.OrgChartEmployee>()
            .ID("orgChartGrid")
            .Width("815px")
            .Height("600px")
            .DataSourceUrl(Url.Action("editing-knockout"))
            .AutoGenerateColumns(false)
            .PrimaryKey("EmployeeID")
            .ChildDataKey("Subs")
            .Columns(c =>
            {
                c.For(x => x.EmployeeID).HeaderText("Employee ID").DataType("number").Hidden(true);
                c.For(x => x.Picture).HeaderText("Picture").DataType("string").Width("100px").Template("<div class=\"emp-pic\" style=\"background: url(${Picture}) no-repeat;\"></div>");
                c.For(x => x.FirstName).HeaderText("Name").DataType("string").Template("${FirstName} ${LastName}");
                c.For(x => x.Position).HeaderText("Position").DataType("string");
                c.For(x => x.HireDate).HeaderText("Hire Date").DataType("date").Format("yyyy-MM-dd");
                c.For(x => x.AnnualSalary).HeaderText("Annual Salary").DataType("number").Format("currency");
                c.For(x => x.ReportsTo).Hidden(true);
                c.For(x => x.LastName).HeaderText("Reports To").DataType("string").FormatterFunction("formatReportsTo");
            })
            .DefaultColumnWidth("100px")
            .InitialExpandDepth(0)
            .InitialIndentationLevel(2)
            .RenderExpansionIndicatorColumn(true)
            .Features(f =>
            {
                f.Selection()
                    .Mode(SelectionMode.Row)
                    .ClientEvents(new Dictionary<string, string>() {
                        { GridSelectionClientEvents.RowSelectionChanged,  "orgChartVM.employeeSelectionChanged(evt,ui);" }
                    });
                f.Paging()
                    .Type(OpType.Remote)
                    .Mode(TreeGridPagingMode.AllLevels)
                    .ContextRowMode(TreeGridPagingContextRowMode.Breadcrumb)
                    .RenderContextRowFunc("contextRowFunc")
                    .PageSize(100);
            })
            .Render()
    )

    <div id="editForm">
        <fieldset class="org-chart-edit" data-bind="visible: selectedEmployee(), with: selectedEmployee">            
            <div class="row-edit-dialog-container-head">
                <label for="idEditor">Employee ID:</label>
                <div id="idEditor" data-bind="text: EmployeeID"></div>
            </div>
            <div class="employee-table">
                <div class="employee-data">
                    <div class="form-row">
                        <label for="firstNameEditor">First Name:</label>
                        <input id="firstNameEditor" type="text" data-bind="igTextEditor: { value: FirstName, selectionOnFocus: 'atEnd' }" />
                    </div>
                    <div class="form-row">
                        <label for="lastNameEditor">Last Name:</label>
                        <input id="lastNameEditor" type="text" data-bind="igTextEditor: { value: LastName, selectionOnFocus: 'atEnd' }" />
                    </div>
                    <div class="form-row">
                        <label for="positionEditor">Position:</label>
                        <select id="positionEditor" data-bind="igCombo: { dataSource: $root.availablePositions, selectedItems: Position, enableClearButton: false, mode: 'dropdown' }"></select>
                    </div>
                    <div class="form-row">
                        <label for="hireDateEditor">Hire Date:</label>
                        <input id="hireDateEditor" data-bind="igDateEditor: { value: HireDate }" />
                    </div>
                    <div class="form-row">
                        <label for="reportsToEditor">Reports To:</label>
                        <select id="reportsToEditor" data-bind="igCombo: { dataSource: $root.directors, valueKey: 'EmployeeID', textKey: 'FullName', selectedItems: ReportsTo, placeHolder: 'Choose manager...' }"></select>
                    </div>
                    <div class="form-row">
                        <label for="annualSalaryEditor">Annual Salary:</label>
                        <input id="annualSalaryEditor" type="number" step="10" data-bind="igCurrencyEditor: { value: AnnualSalary, selectionOnFocus: 'atStart' }" />
                    </div>
                </div>
                <div class="employee-pic">
                    <div class="emp-pic-big" data-bind="style: { background: 'url(\'' + BigPicture + '\') no-repeat' }"></div>
                </div>
            </div>
            <div class="ui-dialog-buttonset">
                <button class="ui-button-text-only ui-button ui-igbutton ui-widget ui-widget-content ui-corner-all ui-state-default" data-bind="click: $root.save"><span id="grid_updating_dialog_container_footer_buttonok_lbl" class="ui-button-text">Save Employee</span></button>
                <button class="ui-button-text-only ui-button ui-igbutton ui-widget ui-widget-content ui-corner-all ui-state-default" data-bind="click: $root.cancel"><span id="grid_updating_dialog_container_footer_buttoncancel_lbl" class="ui-button-text">Cancel</span></button>
            </div>
        </fieldset>
    </div>
</body>
</html>