Kendo TreeView Поиск с подсветкой
У меня есть KendoTreeview со спрайт-классом. Я хочу выделить узлы (корневые и дочерние узлы) с помощью моего поискового запроса. Я реализовал функцию поиска. Но проблема, когда я ищу это, выдвигает на первый план термин в узлах, но пропускает SpriteClass в узлах после первого поиска. Любая идея?
$('#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);
});
}
}