I have a ng-select ponent whose drop-down items look like this image above.
In an attempt to filter the list, I type in characters into the input control and see this: "No Items Found" despite the fact they are in the list.
Here's what I've done to try to figure this out...
Within the ponent's own code, there is this filter function. I have found based on setting breakpoints, that there is no searchFn (according to code flow) so the defaultSearchFn is used but doesn't seem to work.
// Code within the ponent
filter(term) {
if (!term) {
this.resetFilteredItems();
return;
}
this._filteredItems = [];
term = this._ngSelect.searchFn ? term : stripSpecialChars(term).toLocaleLowerCase();
/** @type {?} */
const match = this._ngSelect.searchFn || this._defaultSearchFn;
/** @type {?} */
const hideSelected = this._ngSelect.hideSelected;
//Because there are no groups keys....
for (const key of Array.from(this._groups.keys())) {
/** @type {?} */
const matchedItems = [];
for (const item of this._groups.get(key)) {
if (hideSelected && (item.parent && item.parent.selected || item.selected)) {
continue;
}
/** @type {?} */
const searchItem = this._ngSelect.searchFn ? item.value : item;
//match is never called
if (match(term, searchItem)) {
matchedItems.push(item);
}
}
//code just skips to this point.
if (matchedItems.length > 0) {
const [last] = matchedItems.slice(-1);
if (last.parent) {
/** @type {?} */
const head = this._items.find(x => x === last.parent);
this._filteredItems.push(head);
}
this._filteredItems.push(...matchedItems);
}
}
}
So no items are found because the itemslist.filteredItems is always null.
<ng-select
#select
(change)="onSelectChanged(select)"
[(items)]="items"
[searchable]="true"
type="text"
>
<ng-template ng-label-tmp let-item="item">
<span class="dropdown">
{{ item.firstName + " " + item.lastName }}
</span>
</ng-template>
<ng-template
ng-option-tmp
let-item="item"
let-search="searchTerm"
let-index="index"
>
<span class="dropdown"
>{{
item.firstName +
" " +
item.lastName +
" " +
item.middleName +
" " +
"(" +
item.id +
")"
}}
</span>
</ng-template>
</ng-select>
Here's the HTML, have I forgot something to make the filtering work when characters are typed in?
I have a ng-select ponent whose drop-down items look like this image above.
In an attempt to filter the list, I type in characters into the input control and see this: "No Items Found" despite the fact they are in the list.
Here's what I've done to try to figure this out...
Within the ponent's own code, there is this filter function. I have found based on setting breakpoints, that there is no searchFn (according to code flow) so the defaultSearchFn is used but doesn't seem to work.
// Code within the ponent
filter(term) {
if (!term) {
this.resetFilteredItems();
return;
}
this._filteredItems = [];
term = this._ngSelect.searchFn ? term : stripSpecialChars(term).toLocaleLowerCase();
/** @type {?} */
const match = this._ngSelect.searchFn || this._defaultSearchFn;
/** @type {?} */
const hideSelected = this._ngSelect.hideSelected;
//Because there are no groups keys....
for (const key of Array.from(this._groups.keys())) {
/** @type {?} */
const matchedItems = [];
for (const item of this._groups.get(key)) {
if (hideSelected && (item.parent && item.parent.selected || item.selected)) {
continue;
}
/** @type {?} */
const searchItem = this._ngSelect.searchFn ? item.value : item;
//match is never called
if (match(term, searchItem)) {
matchedItems.push(item);
}
}
//code just skips to this point.
if (matchedItems.length > 0) {
const [last] = matchedItems.slice(-1);
if (last.parent) {
/** @type {?} */
const head = this._items.find(x => x === last.parent);
this._filteredItems.push(head);
}
this._filteredItems.push(...matchedItems);
}
}
}
So no items are found because the itemslist.filteredItems is always null.
<ng-select
#select
(change)="onSelectChanged(select)"
[(items)]="items"
[searchable]="true"
type="text"
>
<ng-template ng-label-tmp let-item="item">
<span class="dropdown">
{{ item.firstName + " " + item.lastName }}
</span>
</ng-template>
<ng-template
ng-option-tmp
let-item="item"
let-search="searchTerm"
let-index="index"
>
<span class="dropdown"
>{{
item.firstName +
" " +
item.lastName +
" " +
item.middleName +
" " +
"(" +
item.id +
")"
}}
</span>
</ng-template>
</ng-select>
Here's the HTML, have I forgot something to make the filtering work when characters are typed in?
Share Improve this question edited May 27, 2020 at 9:09 JWP asked May 26, 2020 at 17:13 JWPJWP 6,9753 gold badges57 silver badges77 bronze badges1 Answer
Reset to default 3Ok found out more about ng-select internals and a solution for proper dropdown filtering as one types.
The key to understanding this is this statement in the ponent's own filter function shown here.
const match = this._ngSelect.searchFn || this._defaultSearchFn;
This statement allows the user to inject a function, if it's not there; then the default searchFn is used.
To inject your own SearchFn, put this into the ng-select html. You will want this for specific plex searching.
<ng-select
[searchFn]="searchFunction" //inject your own function here.
[items]="filtered"
type="text">
// the itemsList is iterated and this is called for each item
// just like an array map function
searchFunction(term, item) {
// anything returning true makes it into the filtered list
return item.firstName.includes(term);
}
//any matches are pushed into the itemsList.filteredItems list which is read-only to the outside world.
//see this line up above in the filter method of the ng-select?
this._filteredItems.push(...matchedItems);
If you don't have a registered SearchFn method the defaultSearchFn is called. That function looks like this:
_defaultSearchFn(search, opt) {
/** @type {?} */
const label = stripSpecialChars(opt.label).toLocaleLowerCase();
return label.indexOf(search) > -1;
}
That function was failing to find anything in my situation because there was no opt.label. I believe this is a bug because I used the tmp-lbl template. In debug mode, the items on entry were all correct but no index was ever found.
Here was the ng-label-tmp template which should have set the opt.label value.
<ng-template ng-label-tmp let-item="item">
<span>{{ item.firstName + " " + item.lastName }}</span>
</ng-template>
[bindLabel] didn't work because it only binds to single fields.
If you have no Searchfn registered and nothing is found via the _defaultSearchFn, then one last chance is kicked out to the user, if you have registered an onSearch handler like this.
(search)="onSearchFunction($event, select)"
But you cannot access the itemsList.FilteredItems from here as it's read-only. The event sends in the term and items to work on.
It is also accessed from code behind like this:
@ViewChild(NgSelectComponent, { static: false }) select: NgSelectComponent;
this.select.searchEvent.subscribe((result) => {
let term = result.term;
result.items = this.filtered.find((item) =>
item.firstName.includes(term)
);
});
Note that this method requires you to maintain and bind you own filter list. Yuck...
Best solution is to use your own injected search function.