Skip to content

Commit acdf5c2

Browse files
nareshpingaleanjmao
authored andcommitted
feat(template): loading, not found, type to search state template options. (#341)
closes #217, #201
1 parent d5a9568 commit acdf5c2

File tree

6 files changed

+198
-14
lines changed

6 files changed

+198
-14
lines changed

demo/app/examples/custom-templates.component.ts

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
import { Component, ChangeDetectionStrategy } from '@angular/core';
1+
import { Component, ChangeDetectionStrategy, EventEmitter, ChangeDetectorRef } from '@angular/core';
22
import { DataService } from '../shared/data.service';
3+
import { distinctUntilChanged, debounceTime, switchMap } from 'rxjs/operators'
4+
35

46
@Component({
57
selector: 'select-with-templates',
@@ -85,9 +87,43 @@ import { DataService } from '../shared/data.service';
8587
</ng-template>
8688
</ng-select>
8789
---
90+
<p>
91+
Selected people: {{selectedPeople}}
92+
</p
93+
94+
<label>Custom not found , type to search and loading </label>
95+
---html,true
96+
<ng-select
97+
[multiple]="true"
98+
[items]="serverSideFilterItems"
99+
[(ngModel)]="selectedPeople"
100+
placeholder="Select people"
101+
bindLabel="name"
102+
bindValue="name"
103+
[typeahead]="peopleTypeahead">
104+
<ng-template ng-typetosearch-tmp>
105+
<div class="ng-option disabled">
106+
Start typing...
107+
</div>
108+
</ng-template>
109+
<ng-template ng-notfound-tmp let-searchTerm="searchTerm">
110+
111+
<div class="ng-option disabled">
112+
No data found for "{{searchTerm}}"
113+
</div>
114+
</ng-template>
115+
<ng-template ng-loadingtext-tmp let-searchTerm="searchTerm">
116+
117+
<div class="ng-option disabled">
118+
Fetching Data for "{{searchTerm}}"
119+
</div>
120+
</ng-template>
121+
</ng-select>
122+
---
88123
<p>
89124
Selected people: {{selectedPeople}}
90125
</p>
126+
91127
<hr />
92128
93129
<label>Custom search</label>
@@ -119,13 +155,17 @@ export class SelectWithTemplatesComponent {
119155

120156
people = [];
121157
selectedPeople = [];
158+
serverSideFilterItems = [];
159+
160+
peopleTypeahead = new EventEmitter<string>();
122161

123-
constructor(private dataService: DataService) {}
162+
constructor(private dataService: DataService, private cd: ChangeDetectorRef) {}
124163

125164
ngOnInit() {
126165
this.dataService.getPeople().subscribe(items => {
127166
this.people = items;
128167
});
168+
this.serverSideSearch();
129169
}
130170

131171
selectAll() {
@@ -135,5 +175,19 @@ export class SelectWithTemplatesComponent {
135175
unselectAll() {
136176
this.selectedPeople = [];
137177
}
178+
179+
private serverSideSearch() {
180+
this.peopleTypeahead.pipe(
181+
distinctUntilChanged(),
182+
debounceTime(300),
183+
switchMap(term => this.dataService.getPeople(term))
184+
).subscribe(x => {
185+
this.cd.markForCheck();
186+
this.serverSideFilterItems = x;
187+
}, (err) => {
188+
console.log(err);
189+
this.serverSideFilterItems = [];
190+
});
191+
}
138192
}
139193

src/ng-select/ng-select.component.html

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -85,15 +85,37 @@
8585
</div>
8686
</ng-container>
8787

88-
<div class="ng-option disabled" *ngIf="showNoItemsFound()">
89-
{{notFoundText}}
90-
</div>
88+
<ng-container *ngIf="showNoItemsFound()">
89+
<ng-template #defaultNotfoundTemplate>
90+
<div class="ng-option disabled" [innerHTML]="notFoundText" *ngIf="showNoItemsFound()"></div>
91+
</ng-template>
92+
93+
<ng-template
94+
[ngTemplateOutlet]="notFoundTemplate || defaultNotfoundTemplate"
95+
[ngTemplateOutletContext]="{searchTerm: filterValue }">
96+
</ng-template>
97+
</ng-container>
9198

92-
<div class="ng-option disabled" *ngIf="showTypeToSearch()">
93-
{{typeToSearchText}}
94-
</div>
99+
<ng-container *ngIf="showTypeToSearch()">
100+
<ng-template #defaultTypeToSearchTemplate>
101+
<div class="ng-option disabled" [innerHTML]="typeToSearchText"></div>
102+
</ng-template>
103+
104+
<ng-template
105+
[ngTemplateOutlet]="typeToSearchTemplate || defaultTypeToSearchTemplate"
106+
[ngTemplateOutletContext]="{ searchTerm: filterValue }">
107+
</ng-template>
108+
</ng-container>
109+
110+
<ng-container *ngIf="isLoading && itemsList.filteredItems.length === 0">
111+
<ng-template #defaultLoadingTextTemplate>
112+
<div class="ng-option disabled" [innerHTML]="loadingText" ></div>
113+
</ng-template>
114+
115+
<ng-template
116+
[ngTemplateOutlet]="loadingTextTemplate || defaultLoadingTextTemplate"
117+
[ngTemplateOutletContext]="{ isearchTerm: filterValue }">
118+
</ng-template>
119+
</ng-container>
95120

96-
<div class="ng-option disabled" *ngIf="isLoading && itemsList.filteredItems.length === 0">
97-
{{loadingText}}
98-
</div>
99121
</ng-dropdown-panel>

src/ng-select/ng-select.component.spec.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,6 +1139,78 @@ describe('NgSelectComponent', function () {
11391139
});
11401140
}));
11411141

1142+
1143+
it('should display custom loading and no data found template', fakeAsync(() => {
1144+
const fixture = createTestingModule(
1145+
NgSelectFilterTestCmp,
1146+
`<ng-select [items]="cities"
1147+
[loading]="citiesLoading"
1148+
[(ngModel)]="selectedCity">
1149+
1150+
<ng-template ng-notfound-tmp let-searchTerm="searchTerm">
1151+
<div class="custom-notfound">
1152+
No data found for "{{searchTerm}}"
1153+
</div>
1154+
</ng-template>
1155+
<ng-template ng-loadingtext-tmp let-searchTerm="searchTerm">
1156+
<div class="custom-loading">
1157+
Fetching Data for "{{searchTerm}}"
1158+
</div>
1159+
</ng-template>
1160+
</ng-select>`);
1161+
1162+
1163+
1164+
fixture.whenStable().then(() => {
1165+
fixture.componentInstance.cities = [];
1166+
fixture.componentInstance.citiesLoading = true;
1167+
tickAndDetectChanges(fixture);
1168+
triggerKeyDownEvent(getNgSelectElement(fixture), KeyCode.Space);
1169+
tickAndDetectChanges(fixture);
1170+
const loadingOption = fixture.debugElement.queryAll(By.css('.custom-loading'));
1171+
expect(loadingOption.length).toBe(1);
1172+
1173+
1174+
fixture.componentInstance.citiesLoading = false;
1175+
tickAndDetectChanges(fixture);
1176+
triggerKeyDownEvent(getNgSelectElement(fixture), KeyCode.Space);
1177+
tickAndDetectChanges(fixture);
1178+
const notFoundOptions = fixture.debugElement.queryAll(By.css('.custom-notfound'));
1179+
expect(notFoundOptions.length).toBe(1);
1180+
1181+
});
1182+
}));
1183+
1184+
it('should display custom type for search template', fakeAsync(() => {
1185+
const fixture = createTestingModule(
1186+
NgSelectFilterTestCmp,
1187+
`<ng-select [items]="cities"
1188+
[typeahead]="customFilter"
1189+
[(ngModel)]="selectedCity">
1190+
<ng-template ng-typetosearch-tmp>
1191+
<div class="custom-typeforsearch">
1192+
Start typing...
1193+
</div>
1194+
</ng-template>
1195+
1196+
</ng-select>`);
1197+
1198+
1199+
1200+
fixture.whenStable().then(() => {
1201+
fixture.componentInstance.cities = [];
1202+
fixture.componentInstance.select.open();
1203+
fixture.componentInstance.customFilter.subscribe();
1204+
tickAndDetectChanges(fixture);
1205+
triggerKeyDownEvent(getNgSelectElement(fixture), KeyCode.Space);
1206+
tickAndDetectChanges(fixture);
1207+
const loadingOption = fixture.debugElement.queryAll(By.css('.custom-typeforsearch'));
1208+
expect(loadingOption.length).toBe(1);
1209+
1210+
});
1211+
1212+
}));
1213+
11421214
it('should create items from ng-option', fakeAsync(() => {
11431215
const fixture = createTestingModule(
11441216
NgSelectBasicTestCmp,
@@ -2182,6 +2254,7 @@ class NgSelectModelChangesTestCmp {
21822254
})
21832255
class NgSelectFilterTestCmp {
21842256
@ViewChild(NgSelectComponent) select: NgSelectComponent;
2257+
citiesLoading = false;
21852258
selectedCity: { id: number; name: string };
21862259
cities = [
21872260
{ id: 1, name: 'Vilnius' },

src/ng-select/ng-select.component.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ import {
3232
NgLabelTemplateDirective,
3333
NgHeaderTemplateDirective,
3434
NgFooterTemplateDirective,
35-
NgOptgroupTemplateDirective
35+
NgOptgroupTemplateDirective,
36+
NgNotFoundTemplateDirective,
37+
NgTypeToSearchTemplateDirective,
38+
NgLoadingTextTemplateDirective
3639
} from './ng-templates.directive';
3740

3841
import { NgOption, KeyCode, NgSelectConfig } from './ng-select.types';
@@ -110,6 +113,9 @@ export class NgSelectComponent implements OnDestroy, OnChanges, AfterViewInit, C
110113
@ContentChild(NgLabelTemplateDirective, { read: TemplateRef }) labelTemplate: TemplateRef<any>;
111114
@ContentChild(NgHeaderTemplateDirective, { read: TemplateRef }) headerTemplate: TemplateRef<any>;
112115
@ContentChild(NgFooterTemplateDirective, { read: TemplateRef }) footerTemplate: TemplateRef<any>;
116+
@ContentChild(NgNotFoundTemplateDirective, { read: TemplateRef }) notFoundTemplate: TemplateRef<any>;
117+
@ContentChild(NgTypeToSearchTemplateDirective, { read: TemplateRef }) typeToSearchTemplate: TemplateRef<any>;
118+
@ContentChild(NgLoadingTextTemplateDirective, { read: TemplateRef }) loadingTextTemplate: TemplateRef<any>;
113119

114120
@ViewChild(forwardRef(() => NgDropdownPanelComponent)) dropdownPanel: NgDropdownPanelComponent;
115121
@ContentChildren(NgOptionComponent, { descendants: true }) ngOptions: QueryList<NgOptionComponent>;

src/ng-select/ng-select.module.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ import {
66
NgLabelTemplateDirective,
77
NgHeaderTemplateDirective,
88
NgFooterTemplateDirective,
9-
NgOptgroupTemplateDirective
9+
NgOptgroupTemplateDirective,
10+
NgNotFoundTemplateDirective,
11+
NgTypeToSearchTemplateDirective,
12+
NgLoadingTextTemplateDirective
1013
} from './ng-templates.directive';
1114
import { NgOptionComponent } from './ng-option.component';
1215
import { NgOptionHighlightDirective } from './ng-option-highlight.directive' ;
@@ -25,6 +28,9 @@ import { VirtualScrollService } from './virtual-scroll.service';
2528
NgLabelTemplateDirective,
2629
NgHeaderTemplateDirective,
2730
NgFooterTemplateDirective,
31+
NgNotFoundTemplateDirective,
32+
NgTypeToSearchTemplateDirective,
33+
NgLoadingTextTemplateDirective
2834
],
2935
imports: [
3036
CommonModule
@@ -37,7 +43,10 @@ import { VirtualScrollService } from './virtual-scroll.service';
3743
NgOptionTemplateDirective,
3844
NgLabelTemplateDirective,
3945
NgHeaderTemplateDirective,
40-
NgFooterTemplateDirective
46+
NgFooterTemplateDirective,
47+
NgNotFoundTemplateDirective,
48+
NgTypeToSearchTemplateDirective,
49+
NgLoadingTextTemplateDirective
4150
],
4251
providers: [
4352
WindowService,

src/ng-select/ng-templates.directive.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,23 @@ export class NgFooterTemplateDirective {
2929
constructor(public template: TemplateRef<any>) {
3030
}
3131
}
32+
33+
@Directive({ selector: '[ng-notfound-tmp]' })
34+
export class NgNotFoundTemplateDirective {
35+
constructor(public template: TemplateRef<any>) {
36+
}
37+
}
38+
39+
40+
@Directive({ selector: '[ng-typetosearch-tmp]' })
41+
export class NgTypeToSearchTemplateDirective {
42+
constructor(public template: TemplateRef<any>) {
43+
}
44+
}
45+
46+
47+
@Directive({ selector: '[ng-loadingtext-tmp]' })
48+
export class NgLoadingTextTemplateDirective {
49+
constructor(public template: TemplateRef<any>) {
50+
}
51+
}

0 commit comments

Comments
 (0)