Skip to content

Commit

Permalink
feat(template): loading, not found, type to search state template opt…
Browse files Browse the repository at this point in the history
…ions. (#341)

closes #217, #201
  • Loading branch information
nareshpingale authored and anjmao committed Mar 13, 2018
1 parent d5a9568 commit acdf5c2
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 14 deletions.
58 changes: 56 additions & 2 deletions demo/app/examples/custom-templates.component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { Component, ChangeDetectionStrategy, EventEmitter, ChangeDetectorRef } from '@angular/core';
import { DataService } from '../shared/data.service';
import { distinctUntilChanged, debounceTime, switchMap } from 'rxjs/operators'


@Component({
selector: 'select-with-templates',
Expand Down Expand Up @@ -85,9 +87,43 @@ import { DataService } from '../shared/data.service';
</ng-template>
</ng-select>
---
<p>
Selected people: {{selectedPeople}}
</p
<label>Custom not found , type to search and loading </label>
---html,true
<ng-select
[multiple]="true"
[items]="serverSideFilterItems"
[(ngModel)]="selectedPeople"
placeholder="Select people"
bindLabel="name"
bindValue="name"
[typeahead]="peopleTypeahead">
<ng-template ng-typetosearch-tmp>
<div class="ng-option disabled">
Start typing...
</div>
</ng-template>
<ng-template ng-notfound-tmp let-searchTerm="searchTerm">
<div class="ng-option disabled">
No data found for "{{searchTerm}}"
</div>
</ng-template>
<ng-template ng-loadingtext-tmp let-searchTerm="searchTerm">
<div class="ng-option disabled">
Fetching Data for "{{searchTerm}}"
</div>
</ng-template>
</ng-select>
---
<p>
Selected people: {{selectedPeople}}
</p>
<hr />
<label>Custom search</label>
Expand Down Expand Up @@ -119,13 +155,17 @@ export class SelectWithTemplatesComponent {

people = [];
selectedPeople = [];
serverSideFilterItems = [];

peopleTypeahead = new EventEmitter<string>();

constructor(private dataService: DataService) {}
constructor(private dataService: DataService, private cd: ChangeDetectorRef) {}

ngOnInit() {
this.dataService.getPeople().subscribe(items => {
this.people = items;
});
this.serverSideSearch();
}

selectAll() {
Expand All @@ -135,5 +175,19 @@ export class SelectWithTemplatesComponent {
unselectAll() {
this.selectedPeople = [];
}

private serverSideSearch() {
this.peopleTypeahead.pipe(
distinctUntilChanged(),
debounceTime(300),
switchMap(term => this.dataService.getPeople(term))
).subscribe(x => {
this.cd.markForCheck();
this.serverSideFilterItems = x;
}, (err) => {
console.log(err);
this.serverSideFilterItems = [];
});
}
}

40 changes: 31 additions & 9 deletions src/ng-select/ng-select.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,37 @@
</div>
</ng-container>

<div class="ng-option disabled" *ngIf="showNoItemsFound()">
{{notFoundText}}
</div>
<ng-container *ngIf="showNoItemsFound()">
<ng-template #defaultNotfoundTemplate>
<div class="ng-option disabled" [innerHTML]="notFoundText" *ngIf="showNoItemsFound()"></div>
</ng-template>

<ng-template
[ngTemplateOutlet]="notFoundTemplate || defaultNotfoundTemplate"
[ngTemplateOutletContext]="{searchTerm: filterValue }">
</ng-template>
</ng-container>

<div class="ng-option disabled" *ngIf="showTypeToSearch()">
{{typeToSearchText}}
</div>
<ng-container *ngIf="showTypeToSearch()">
<ng-template #defaultTypeToSearchTemplate>
<div class="ng-option disabled" [innerHTML]="typeToSearchText"></div>
</ng-template>

<ng-template
[ngTemplateOutlet]="typeToSearchTemplate || defaultTypeToSearchTemplate"
[ngTemplateOutletContext]="{ searchTerm: filterValue }">
</ng-template>
</ng-container>

<ng-container *ngIf="isLoading && itemsList.filteredItems.length === 0">
<ng-template #defaultLoadingTextTemplate>
<div class="ng-option disabled" [innerHTML]="loadingText" ></div>
</ng-template>

<ng-template
[ngTemplateOutlet]="loadingTextTemplate || defaultLoadingTextTemplate"
[ngTemplateOutletContext]="{ isearchTerm: filterValue }">
</ng-template>
</ng-container>

<div class="ng-option disabled" *ngIf="isLoading && itemsList.filteredItems.length === 0">
{{loadingText}}
</div>
</ng-dropdown-panel>
73 changes: 73 additions & 0 deletions src/ng-select/ng-select.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1139,6 +1139,78 @@ describe('NgSelectComponent', function () {
});
}));


it('should display custom loading and no data found template', fakeAsync(() => {
const fixture = createTestingModule(
NgSelectFilterTestCmp,
`<ng-select [items]="cities"
[loading]="citiesLoading"
[(ngModel)]="selectedCity">
<ng-template ng-notfound-tmp let-searchTerm="searchTerm">
<div class="custom-notfound">
No data found for "{{searchTerm}}"
</div>
</ng-template>
<ng-template ng-loadingtext-tmp let-searchTerm="searchTerm">
<div class="custom-loading">
Fetching Data for "{{searchTerm}}"
</div>
</ng-template>
</ng-select>`);



fixture.whenStable().then(() => {
fixture.componentInstance.cities = [];
fixture.componentInstance.citiesLoading = true;
tickAndDetectChanges(fixture);
triggerKeyDownEvent(getNgSelectElement(fixture), KeyCode.Space);
tickAndDetectChanges(fixture);
const loadingOption = fixture.debugElement.queryAll(By.css('.custom-loading'));
expect(loadingOption.length).toBe(1);


fixture.componentInstance.citiesLoading = false;
tickAndDetectChanges(fixture);
triggerKeyDownEvent(getNgSelectElement(fixture), KeyCode.Space);
tickAndDetectChanges(fixture);
const notFoundOptions = fixture.debugElement.queryAll(By.css('.custom-notfound'));
expect(notFoundOptions.length).toBe(1);

});
}));

it('should display custom type for search template', fakeAsync(() => {
const fixture = createTestingModule(
NgSelectFilterTestCmp,
`<ng-select [items]="cities"
[typeahead]="customFilter"
[(ngModel)]="selectedCity">
<ng-template ng-typetosearch-tmp>
<div class="custom-typeforsearch">
Start typing...
</div>
</ng-template>
</ng-select>`);



fixture.whenStable().then(() => {
fixture.componentInstance.cities = [];
fixture.componentInstance.select.open();
fixture.componentInstance.customFilter.subscribe();
tickAndDetectChanges(fixture);
triggerKeyDownEvent(getNgSelectElement(fixture), KeyCode.Space);
tickAndDetectChanges(fixture);
const loadingOption = fixture.debugElement.queryAll(By.css('.custom-typeforsearch'));
expect(loadingOption.length).toBe(1);

});

}));

it('should create items from ng-option', fakeAsync(() => {
const fixture = createTestingModule(
NgSelectBasicTestCmp,
Expand Down Expand Up @@ -2182,6 +2254,7 @@ class NgSelectModelChangesTestCmp {
})
class NgSelectFilterTestCmp {
@ViewChild(NgSelectComponent) select: NgSelectComponent;
citiesLoading = false;
selectedCity: { id: number; name: string };
cities = [
{ id: 1, name: 'Vilnius' },
Expand Down
8 changes: 7 additions & 1 deletion src/ng-select/ng-select.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ import {
NgLabelTemplateDirective,
NgHeaderTemplateDirective,
NgFooterTemplateDirective,
NgOptgroupTemplateDirective
NgOptgroupTemplateDirective,
NgNotFoundTemplateDirective,
NgTypeToSearchTemplateDirective,
NgLoadingTextTemplateDirective
} from './ng-templates.directive';

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

@ViewChild(forwardRef(() => NgDropdownPanelComponent)) dropdownPanel: NgDropdownPanelComponent;
@ContentChildren(NgOptionComponent, { descendants: true }) ngOptions: QueryList<NgOptionComponent>;
Expand Down
13 changes: 11 additions & 2 deletions src/ng-select/ng-select.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import {
NgLabelTemplateDirective,
NgHeaderTemplateDirective,
NgFooterTemplateDirective,
NgOptgroupTemplateDirective
NgOptgroupTemplateDirective,
NgNotFoundTemplateDirective,
NgTypeToSearchTemplateDirective,
NgLoadingTextTemplateDirective
} from './ng-templates.directive';
import { NgOptionComponent } from './ng-option.component';
import { NgOptionHighlightDirective } from './ng-option-highlight.directive' ;
Expand All @@ -25,6 +28,9 @@ import { VirtualScrollService } from './virtual-scroll.service';
NgLabelTemplateDirective,
NgHeaderTemplateDirective,
NgFooterTemplateDirective,
NgNotFoundTemplateDirective,
NgTypeToSearchTemplateDirective,
NgLoadingTextTemplateDirective
],
imports: [
CommonModule
Expand All @@ -37,7 +43,10 @@ import { VirtualScrollService } from './virtual-scroll.service';
NgOptionTemplateDirective,
NgLabelTemplateDirective,
NgHeaderTemplateDirective,
NgFooterTemplateDirective
NgFooterTemplateDirective,
NgNotFoundTemplateDirective,
NgTypeToSearchTemplateDirective,
NgLoadingTextTemplateDirective
],
providers: [
WindowService,
Expand Down
20 changes: 20 additions & 0 deletions src/ng-select/ng-templates.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,23 @@ export class NgFooterTemplateDirective {
constructor(public template: TemplateRef<any>) {
}
}

@Directive({ selector: '[ng-notfound-tmp]' })
export class NgNotFoundTemplateDirective {
constructor(public template: TemplateRef<any>) {
}
}


@Directive({ selector: '[ng-typetosearch-tmp]' })
export class NgTypeToSearchTemplateDirective {
constructor(public template: TemplateRef<any>) {
}
}


@Directive({ selector: '[ng-loadingtext-tmp]' })
export class NgLoadingTextTemplateDirective {
constructor(public template: TemplateRef<any>) {
}
}

0 comments on commit acdf5c2

Please sign in to comment.