Kendo TreeView Поиск с подсветкой

У меня есть KendoTreeview со спрайт-классом. Я хочу выделить узлы (корневые и дочерние узлы) с помощью моего поискового запроса. Я реализовал функцию поиска. Но проблема, когда я ищу это, выдвигает на первый план термин в узлах, но пропускает SpriteClass в узлах после первого поиска. Любая идея?

код jsFiddle

$('#search-term').on('keyup', function () {
    $('span.k-in > span.highlight').each(function () {
        $(this).parent().text($(this).parent().text());
    });

    // ignore if no search term
    if ($.trim($(this).val()) == '') {
        return;
    }

    var term = this.value.toUpperCase();
    var tlen = term.length;

    $('#treeview-sprites span.k-in').each(function (index) {
        var text = $(this).text();
        var html = '';
        var q = 0;
        while ((p = text.toUpperCase().indexOf(term, q)) >= 0) {
            html += text.substring(q, p) + '<span class="highlight">' + text.substr(p, tlen) + '</span>';
            q = p + tlen;
        }

        if (q > 0) {
            html += text.substring(q);
            $(this).html(html);

            $(this).parentsUntil('.k-treeview').filter('.k-item').each(

            function (index, element) {
                $('#treeview-sprites').data('kendoTreeView').expand($(this));
                $(this).data('search-term', term);
            });
        }
    });

$("#treeview-sprites").kendoTreeView({
    dataSource: [{
        text: "My Documents",
        expanded: true,
        spriteCssClass: "rootfolder",
        items: [{
            text: "Kendo UI Project",
            expanded: true,
            spriteCssClass: "folder",
            items: [{
                text: "about.html",
                spriteCssClass: "html"
            }, {
                text: "index.html",
                spriteCssClass: "html"
            }, {
                text: "logo.png",
                spriteCssClass: "image"
            }]
        }, {
            text: "New Web Site",
            expanded: true,
            spriteCssClass: "folder",
            items: [{
                text: "mockup.jpg",
                spriteCssClass: "image"
            }, {
                text: "Research.pdf",
                spriteCssClass: "pdf"
            }, ]
        }, {
            text: "Reports",
            expanded: true,
            spriteCssClass: "folder",
            items: [{
                text: "February.pdf",
                spriteCssClass: "pdf"
            }, {
                text: "March.pdf",
                spriteCssClass: "pdf"
            }, {
                text: "April.pdf",
                spriteCssClass: "pdf"
            }]
        }]
    }]
})

;

4 ответа

Решение

Виджет в виде дерева Kendo не понравится, если вы будете копаться в его HTML, поэтому я предлагаю вместо этого изменить источник данных (для этого потребуется encoded опция для всех предметов в дс).

В обработчике ключей вы сбрасываете DS каждый раз, когда выполняете поиск, чтобы очистить подсветку, а затем вместо прямой замены HTML-элемента, вы устанавливаете свойство text модели:

$('#search-term').on('keyup', function () {
    var treeView = $("#treeview-sprites").getKendoTreeView();
    treeView.dataSource.data(pristine);

    // ignore if no search term
    if ($.trim($(this).val()) == '') {
        return;
    }

    var term = this.value.toUpperCase();
    var tlen = term.length;

    $('#treeview-sprites span.k-in').each(function (index) {
        var text = $(this).text();
        var html = '';
        var q = 0;
        while ((p = text.toUpperCase().indexOf(term, q)) >= 0) {
            html += text.substring(q, p) + '<span class="highlight">' + text.substr(p, tlen) + '</span>';
            q = p + tlen;
        }

        if (q > 0) {
            html += text.substring(q);

            var dataItem = treeView.dataItem($(this));
            dataItem.set("text", html);

            $(this).parentsUntil('.k-treeview').filter('.k-item').each(

            function (index, element) {
                $('#treeview-sprites').data('kendoTreeView').expand($(this));
                $(this).data('search-term', term);
            });
        }
    });

    $('#treeview-sprites .k-item').each(function () {
        if ($(this).data('search-term') != term) {
            $('#treeview-sprites').data('kendoTreeView').collapse($(this));
        }
    });
});

Определение дерева нуждается в encoded Вариант для этого на работу:

var pristine = [{
    encoded: false,
    text: "Kendo UI Project",
    expanded: true,
    spriteCssClass: "folder",
    items: [{
        encoded: false,
        text: "about.html",
        spriteCssClass: "html"
    }, {
        encoded: false,
        text: "index.html",
        spriteCssClass: "html"
    }, {
        encoded: false,
        text: "logo.png",
        spriteCssClass: "image"
    }]
}, {
    encoded: false,
    text: "New Web Site",
    expanded: true,
    spriteCssClass: "folder",
    items: [{
        encoded: false,
        text: "mockup.jpg",
        spriteCssClass: "image"
    }, {
        encoded: false,
        text: "Research.pdf",
        spriteCssClass: "pdf"
    }, ]
}, {
    encoded: false,
    text: "Reports",
    expanded: true,
    spriteCssClass: "folder",
    items: [{
        encoded: false,
        text: "February.pdf",
        spriteCssClass: "pdf"
    }, {
        encoded: false,
        text: "March.pdf",
        spriteCssClass: "pdf"
    }, {
        encoded: false,
        text: "April.pdf",
        spriteCssClass: "pdf"
    }]
}];

$("#treeview-sprites").kendoTreeView({
    dataSource: [{
        text: "My Documents",
        expanded: true,
        spriteCssClass: "rootfolder",
        items: pristine
    }]
});

( демо)

Хорошая работа, ребята, как раз то, что мне нужно!

Используя ваш код, я сделал небольшую настройку (фактически добавил всего две строки фильтрации jquery), так что теперь при поиске по ключевому слову древовидное представление показывает только ветви, которые содержат выделенный текст. Очень просто!:)

Другие ветки скрыты, если они не содержат выделенный текст. Просто как тот.

Это означает, что теперь у нас есть поиск в виде дерева в стиле VisualStudio (см. Поиск и фильтр в Visual Studio Solution Explorer: http://goo.gl/qr7yVb).

Вот мой код и демо на jsfiddle: http://jsfiddle.net/ComboFusion/d0qespaz/2/

HTML:

<input id="treeViewSearchInput"></input>
<ul id="treeview">
    <li data-expanded="true">My Web Site
        <ul>
            <li data-expanded="true">images
                <ul>
                    <li>logo.png</li>
                    <li>body-back.png</li>
                    <li>my-photo.jpg</li>
                </ul>
            </li>
            <li data-expanded="true">resources
                <ul>
                    <li data-expanded="true">pdf
                        <ul>
                            <li>brochure.pdf</li>
                            <li>prices.pdf</li>
                        </ul>
                    </li>
                    <li>zip</li>
                </ul>
            </li>
            <li>about.html</li>
            <li>contacts.html</li>
            <li>index.html</li>
            <li>portfolio.html</li>
        </ul>
    </li>
    <li>Another Root</li>
</ul>

CSS

span.k-in > span.highlight {
    background: #7EA700;
    color: #ffffff;
    border: 1px solid green;
    padding: 1px;
}

JAVASCRIPT

function InitSearch(treeViewId, searchInputId) {

    var tv = $(treeViewId).data('kendoTreeView');

    $(searchInputId).on('keyup', function () {

        $(treeViewId + ' li.k-item').show();

        $('span.k-in > span.highlight').each(function () {
            $(this).parent().text($(this).parent().text());
        });

        // ignore if no search term
        if ($.trim($(this).val()) === '') {
            return;
        }

        var term = this.value.toUpperCase();
        var tlen = term.length;

        $(treeViewId + ' span.k-in').each(function (index) {
            var text = $(this).text();
            var html = '';
            var q = 0;
            var p;

            while ((p = text.toUpperCase().indexOf(term, q)) >= 0) {
                html += text.substring(q, p) + '<span class="highlight">' + text.substr(p, tlen) + '</span>';
                q = p + tlen;
            }

            if (q > 0) {
                html += text.substring(q);
                $(this).html(html);

                $(this).parentsUntil('.k-treeview').filter('.k-item').each(function (index, element) {
                    tv.expand($(this));
                    $(this).data('SearchTerm', term);
                });
            }
        });

        $(treeViewId + ' li.k-item:not(:has(".highlight"))').hide();

        $(treeViewId + ' li.k-item').expand(".k-item");
    });
}

var $tv = $("#treeview").kendoTreeView();

InitSearch("#treeview", "#treeViewSearchInput");
   $("#textBox").on("input", function () {

    var query = this.value.toLowerCase();
    var dataSource = $("#Treeview").data("kendoTreeView").dataSource;

    filter(dataSource, query);
   });


  function filter(dataSource, query) {
        var uidData = [];
        var data = dataSource instanceof kendo.data.DataSource && dataSource.data();
        for (var i = 0; i < data.length; i++) {
            var item = data[i];
            var text = item.text.toLowerCase();
            var isChecked = item.checked;
            var itemVisible =
                query === true 
                || query === "" 
                || text.indexOf(query) >= 0; 

            uidData.push({ UID: item.uid, Visible: itemVisible });

        }

        if (query != "") {
            $.each(uidData, function (index, datavalue) {
                if (datavalue.Visible) {
                    $("li[data-uid='" + datavalue.UID + "']").addClass("highlight");
                }
                else {
                    $("li[data-uid='" + datavalue.UID + "']").removeClass("highlight");
                }

            });
        }
        else {
            $.each(uidData, function (index, datavalue) {
                $("li[data-uid='" + datavalue.UID + "']").removeClass("highlight");

            });
        }
    }

CSS:

.highlight {
    background:#0fa1ba;
    color:white;
}

Для Angular 2+ вам нужно создать канал для этой функции.

import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { NGXLogger } from 'ngx-logger';

@Pipe({
    name: 'highlight'
})

export class TypeaheadHighlight implements PipeTransform {
    constructor(private readonly _sanitizer: DomSanitizer, private readonly logger: NGXLogger) { }
    transform(matchItem: any, query: any): string {
        let matchedItem: any;
        if (matchItem) {
            matchedItem = matchItem.toString();
        }
        if (this.containsHtml(matchedItem)) {
            this.logger.warn('Unsafe use of typeahead please use ngSanitize');
        }
        matchedItem = query ? ('' + matchedItem).replace(new RegExp(this.escapeRegexp(query), 'gi'), '<strong>$&</strong>') : matchedItem; // Replaces the capture string with a the same string inside of a "strong" tag

        if (!this._sanitizer) {
            matchedItem = this._sanitizer.bypassSecurityTrustHtml(matchedItem);
        }

        return matchedItem;
    }

    escapeRegexp = (queryToEscape) => queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');

    containsHtml = (matchItem) => /<.*>/g.test(matchItem);

} 

Использование этой трубы в шаблоне html....

            <input name="searchTerm" type="text" [(ngModel)]="searchTerm" (keyup)='onkeyup(searchTerm)'
             />
        </div>

        <div style="height: 70vh;overflow: auto">
            <kendo-treeview style="margin-top: 50px" id="availableColumns" [nodes]="availableColumns"
                textField="displayName" kendoTreeViewExpandable kendoTreeViewFlatDataBinding idField="id"
                parentIdField="parentId">
                <ng-template kendoTreeViewNodeTemplate let-dataItem>
                    <span [tooltip]="dataItem.columnDescription"
                        [innerHtml]="dataItem.displayName | highlight:searchTerm "></span>
                </ng-template>
            </kendo-treeview>
        </div> 

Еще один твик от меня:)

Что я сделал, так это изменил код подсветки, чтобы сохранить все остальное, что может существовать в узле html (например, спрайт span).

Я также реализовал это как оболочку класса TypeScript вокруг TreeView.

Если вы не хотите использовать TypeScript, просто скопируйте код, и он будет работать нормально:)

export class SearchableTreeView {
    TreeView: kendo.ui.TreeView;
    emphasisClass: string;

    constructor(treeView: kendo.ui.TreeView) {
        this.TreeView = treeView;
        this.emphasisClass = "bg-warning";
    }

    search(term: string): void {
        var treeElement: JQuery = this.TreeView.element;
        var tv: kendo.ui.TreeView = this.TreeView;
        var emphClass = this.emphasisClass;

        this.resetHighlights();

        // ignore if no search term
        if ($.trim(term) === '') { return; }

        var term = term.toUpperCase();
        var tlen = term.length;

        $('span.k-in', treeElement).each(function (index) {
            // find all corresponding nodes
            var node = $(this);
            var htmlContent = node.html();
            var text = node.text();

            var searchPosition = text.toUpperCase().indexOf(term);
            if (searchPosition === -1) {
                // continue
                return true;
            }

            var generatedHtml = '<span class="highlight-container">' + text.substr(0, searchPosition) + '<span class="' + emphClass + '">' + text.substr(searchPosition, tlen) + '</span>' + text.substr(searchPosition + tlen) + '</span>';

            htmlContent = htmlContent.replace(text, generatedHtml);
            node.html(htmlContent);

            node.parentsUntil('.k-treeview').filter('.k-item').each(
                function (index, element) {
                    tv.expand($(this));
                    $(this).data('search-term', term);
                }
            );

        });

        $('.k-item', treeElement).each(function () {
            if ($(this).data('search-term') != term) {
                tv.collapse($(this));
            }
        });
    }

    resetHighlights(): void {
        this.TreeView.element.find("span.k-in:has('." + this.emphasisClass + "')")
            .each(function () {
                var node = $(this);
                var text = node.text();
                $(".highlight-container", node).remove();
                node.append(text);
            });
    }
}
Другие вопросы по тегам