Skip to content

Commit

Permalink
feat: expose blur method
Browse files Browse the repository at this point in the history
BREAKING CHANGE: `filterValue` is now available as `searchTerm`

closes #1238, closes #1242
  • Loading branch information
varnastadeus committed Jul 5, 2019
1 parent 972af9a commit 643cf52
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 63 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ map: {
| open | Opens the select dropdown panel |
| close | Closes the select dropdown panel |
| focus | Focuses the select element |
| blur | Blurs the select element |

### Other
Name | Type | Description |
Expand Down
50 changes: 25 additions & 25 deletions src/ng-select/lib/ng-select.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
</ng-template>

<div class="ng-input">
<input #filterInput
<input #searchInput
type="text"
[attr.autocomplete]="labelForId ? 'off' : dropdownId"
[attr.id]="labelForId"
Expand All @@ -31,8 +31,8 @@
[attr.autocapitalize]="autoCapitalize"
[readOnly]="!searchable || itemsList.maxItemsSelected"
[disabled]="disabled"
[value]="filterValue ? filterValue : ''"
(input)="filter(filterInput.value)"
[value]="searchTerm ? searchTerm : ''"
(input)="filter(searchInput.value)"
(focus)="onInputFocus($event)"
(blur)="onInputBlur($event)"
(change)="$event.stopPropagation()"
Expand Down Expand Up @@ -63,23 +63,23 @@
</div>

<ng-dropdown-panel *ngIf="isOpen"
class="ng-dropdown-panel"
[virtualScroll]="virtualScroll"
[bufferAmount]="bufferAmount"
[appendTo]="appendTo"
[position]="dropdownPosition"
[headerTemplate]="headerTemplate"
[footerTemplate]="footerTemplate"
[filterValue]="filterValue"
[items]="itemsList.filteredItems"
[markedItem]="itemsList.markedItem"
(update)="viewPortItems = $event"
(scroll)="scroll.emit($event)"
(scrollToEnd)="scrollToEnd.emit($event)"
(outsideClick)="close()"
[class.ng-select-multiple]="multiple"
[ngClass]="appendTo ? classes : null"
[id]="dropdownId">
class="ng-dropdown-panel"
[virtualScroll]="virtualScroll"
[bufferAmount]="bufferAmount"
[appendTo]="appendTo"
[position]="dropdownPosition"
[headerTemplate]="headerTemplate"
[footerTemplate]="footerTemplate"
[filterValue]="searchTerm"
[items]="itemsList.filteredItems"
[markedItem]="itemsList.markedItem"
(update)="viewPortItems = $event"
(scroll)="scroll.emit($event)"
(scrollToEnd)="scrollToEnd.emit($event)"
(outsideClick)="close()"
[class.ng-select-multiple]="multiple"
[ngClass]="appendTo ? classes : null"
[id]="dropdownId">

<ng-container>
<div class="ng-option" [attr.role]="item.children ? 'group' : 'option'" (click)="toggleItem(item)" (mouseover)="onItemHover(item)"
Expand All @@ -99,18 +99,18 @@

<ng-template
[ngTemplateOutlet]="item.children ? (optgroupTemplate || defaultOptionTemplate) : (optionTemplate || defaultOptionTemplate)"
[ngTemplateOutletContext]="{ item: item.value, item$:item, index: item.index, searchTerm: filterValue }">
[ngTemplateOutletContext]="{ item: item.value, item$:item, index: item.index, searchTerm: searchTerm }">
</ng-template>
</div>

<div class="ng-option" [class.ng-option-marked]="!itemsList.markedItem" (mouseover)="itemsList.unmarkItem()" role="option" (click)="selectTag()" *ngIf="showAddTag">
<ng-template #defaultTagTemplate>
<span><span class="ng-tag-label">{{addTagText}}</span>"{{filterValue}}"</span>
<span><span class="ng-tag-label">{{addTagText}}</span>"{{searchTerm}}"</span>
</ng-template>

<ng-template
[ngTemplateOutlet]="tagTemplate || defaultTagTemplate"
[ngTemplateOutletContext]="{ searchTerm: filterValue }">
[ngTemplateOutletContext]="{ searchTerm: searchTerm }">
</ng-template>
</div>
</ng-container>
Expand All @@ -122,7 +122,7 @@

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

Expand All @@ -143,7 +143,7 @@

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

Expand Down
34 changes: 17 additions & 17 deletions src/ng-select/lib/ng-select.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1382,7 +1382,7 @@ describe('NgSelectComponent', () => {
}));

it('should not remove selected value if filter is set', fakeAsync(() => {
select.filterValue = 'a';
select.searchTerm = 'a';
fixture.componentInstance.selectedCity = fixture.componentInstance.cities[0];
tickAndDetectChanges(fixture);
triggerKeyDownEvent(getNgSelectElement(fixture), KeyCode.Backspace);
Expand Down Expand Up @@ -1758,7 +1758,7 @@ describe('NgSelectComponent', () => {
</ng-template>
</ng-select>`);

fixture.componentInstance.select.filterValue = 'tag';
fixture.componentInstance.select.searchTerm = 'tag';
fixture.componentInstance.select.open();
fixture.detectChanges();

Expand Down Expand Up @@ -2198,23 +2198,23 @@ describe('NgSelectComponent', () => {
});

it('should be false when there is no search term', () => {
select.filterValue = null;
select.searchTerm = null;
expect(select.showAddTag).toBeFalsy();
});

it('should be true when term not exists among items', () => {
select.filterValue = 'Vil';
select.searchTerm = 'Vil';
expect(select.showAddTag).toBeTruthy();
});

it('should be false when term exists among items', () => {
select.filterValue = 'Vilnius';
select.searchTerm = 'Vilnius';
expect(select.showAddTag).toBeFalsy();
});

it('should be false when term exists among selected items', fakeAsync(() => {
fixture.componentInstance.selectedCities = [{ name: 'Palanga', id: 9 }];
select.filterValue = 'Palanga';
select.searchTerm = 'Palanga';
select.hideSelected = true;
select.isOpen = true;
tickAndDetectChanges(fixture);
Expand All @@ -2223,7 +2223,7 @@ describe('NgSelectComponent', () => {

it('should be false when term exists among selected items and select is closed', fakeAsync(() => {
fixture.componentInstance.selectedCities = [{ name: 'Palanga', id: 9 }];
select.filterValue = 'Palanga';
select.searchTerm = 'Palanga';
select.hideSelected = false;
select.isOpen = false;
tickAndDetectChanges(fixture);
Expand Down Expand Up @@ -2364,7 +2364,7 @@ describe('NgSelectComponent', () => {
fixture.detectChanges();

const input: HTMLInputElement = select.element.querySelector('input');
expect(select.filterValue).toBeNull();
expect(select.searchTerm).toBeNull();
expect(input.readOnly).toBeTruthy();
}));

Expand Down Expand Up @@ -2451,10 +2451,10 @@ describe('NgSelectComponent', () => {

tickAndDetectChanges(fixture);

fixture.componentInstance.select.filterValue = 'Hey! Whats up!?';
fixture.componentInstance.select.searchTerm = 'Hey! Whats up!?';
selectOption(fixture, KeyCode.ArrowDown, 1);
tickAndDetectChanges(fixture);
expect(fixture.componentInstance.select.filterValue).toBe(null);
expect(fixture.componentInstance.select.searchTerm).toBe(null);
}));

it('should not reset items when selecting option', fakeAsync(() => {
Expand All @@ -2469,7 +2469,7 @@ describe('NgSelectComponent', () => {
const resetFilteredItemsSpy = spyOn(fixture.componentInstance.select.itemsList, 'resetFilteredItems');
tickAndDetectChanges(fixture);

fixture.componentInstance.select.filterValue = null;
fixture.componentInstance.select.searchTerm = null;
selectOption(fixture, KeyCode.ArrowDown, 1);
tickAndDetectChanges(fixture);
expect(resetFilteredItemsSpy).not.toHaveBeenCalled();
Expand Down Expand Up @@ -2618,10 +2618,10 @@ describe('NgSelectComponent', () => {
fixture.componentInstance.cities = [{ id: 4, name: 'New York' }];
tickAndDetectChanges(fixture);
expect(fixture.componentInstance.select.itemsList.filteredItems.length).toBe(1);
expect(fixture.componentInstance.select.filterValue).toBe('new');
expect(fixture.componentInstance.select.searchTerm).toBe('new');

fixture.componentInstance.select.select(fixture.componentInstance.select.viewPortItems[0]);
expect(fixture.componentInstance.select.filterValue).toBeNull();
expect(fixture.componentInstance.select.searchTerm).toBeNull();
}));

it('should not clear search term by default when closeOnSelect is false ', fakeAsync(() => {
Expand All @@ -2644,7 +2644,7 @@ describe('NgSelectComponent', () => {

fixture.componentInstance.select.select(fixture.componentInstance.select.viewPortItems[0]);
expect(fixture.componentInstance.select.itemsList.filteredItems.length).toBe(1);
expect(fixture.componentInstance.select.filterValue).toBe('new');
expect(fixture.componentInstance.select.searchTerm).toBe('new');
}));

it('should not clear search term when clearSearchOnAdd is false', fakeAsync(() => {
Expand All @@ -2667,7 +2667,7 @@ describe('NgSelectComponent', () => {
fixture.componentInstance.cities = [{ id: 4, name: 'New York' }, { id: 5, name: 'California' }];
tickAndDetectChanges(fixture);
fixture.componentInstance.select.select(fixture.componentInstance.select.viewPortItems[0]);
expect(fixture.componentInstance.select.filterValue).toBe('new');
expect(fixture.componentInstance.select.searchTerm).toBe('new');
}));
});
});
Expand Down Expand Up @@ -3036,12 +3036,12 @@ describe('NgSelectComponent', () => {

it('should clear only search text', fakeAsync(() => {
fixture.componentInstance.selectedCities = null;
fixture.componentInstance.select.filterValue = 'Hey! Whats up!?';
fixture.componentInstance.select.searchTerm = 'Hey! Whats up!?';
tickAndDetectChanges(fixture);
triggerMousedown();
tickAndDetectChanges(fixture);
expect(fixture.componentInstance.onChange).toHaveBeenCalledTimes(0);
expect(fixture.componentInstance.select.filterValue).toBe(null);
expect(fixture.componentInstance.select.searchTerm).toBe(null);
}));

it('should not open dropdown', fakeAsync(() => {
Expand Down
47 changes: 26 additions & 21 deletions src/ng-select/lib/ng-select.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,14 +167,15 @@ export class NgSelectComponent implements OnDestroy, OnChanges, AfterViewInit, C

@ViewChild(forwardRef(() => NgDropdownPanelComponent), { static: false }) dropdownPanel: NgDropdownPanelComponent;
@ContentChildren(NgOptionComponent, { descendants: true }) ngOptions: QueryList<NgOptionComponent>;
@ViewChild('filterInput', { static: true }) filterInput: ElementRef;
@ViewChild('searchInput', { static: true }) searchInput: ElementRef;

@HostBinding('class.ng-select-disabled') disabled = false;
@HostBinding('class.ng-select-filtered') get filtered() { return !!this.filterValue && this.searchable };

@HostBinding('class.ng-select-filtered') get filtered() { return !!this.searchTerm && this.searchable };

itemsList: ItemsList;
viewPortItems: NgOption[] = [];
filterValue: string = null;
searchTerm: string = null;
dropdownId = newId();
element: HTMLElement;
focused: boolean;
Expand Down Expand Up @@ -395,7 +396,7 @@ export class NgSelectComponent implements OnDestroy, OnChanges, AfterViewInit, C
this.isOpen = true;
this.itemsList.markSelectedOrDefault(this.markFirst);
this.openEvent.emit();
if (!this.filterValue) {
if (!this.searchTerm) {
this.focus();
}
this.detectChanges();
Expand Down Expand Up @@ -446,7 +447,11 @@ export class NgSelectComponent implements OnDestroy, OnChanges, AfterViewInit, C
}

focus() {
this.filterInput.nativeElement.focus();
this.searchInput.nativeElement.focus();
}

blur() {
this.searchInput.nativeElement.blur();
}

unselect(item: NgOption) {
Expand All @@ -463,9 +468,9 @@ export class NgSelectComponent implements OnDestroy, OnChanges, AfterViewInit, C
selectTag() {
let tag;
if (isFunction(this.addTag)) {
tag = (<AddTagFn>this.addTag)(this.filterValue);
tag = (<AddTagFn>this.addTag)(this.searchTerm);
} else {
tag = this._primitive ? this.filterValue : { [this.bindLabel]: this.filterValue };
tag = this._primitive ? this.searchTerm : { [this.bindLabel]: this.searchTerm };
}

const handleTag = (item) => this._isTypeahead || !this.isOpen ? this.itemsList.mapItem(item, null) : this.itemsList.addItem(item);
Expand All @@ -477,7 +482,7 @@ export class NgSelectComponent implements OnDestroy, OnChanges, AfterViewInit, C
}

showClear() {
return this.clearable && (this.hasValue || this.filterValue) && !this.disabled;
return this.clearable && (this.hasValue || this.searchTerm) && !this.disabled;
}

trackByOption = (_: number, item: NgOption) => {
Expand All @@ -489,11 +494,11 @@ export class NgSelectComponent implements OnDestroy, OnChanges, AfterViewInit, C
};

get showAddTag() {
if (!this.filterValue) {
if (!this.searchTerm) {
return false;
}

const term = this.filterValue.toLowerCase();
const term = this.searchTerm.toLowerCase();
return this.addTag &&
(!this.itemsList.filteredItems.some(x => x.label.toLowerCase() === term) &&
(!this.hideSelected && this.isOpen || !this.selectedItems.some(x => x.label.toLowerCase() === term))) &&
Expand All @@ -503,22 +508,22 @@ export class NgSelectComponent implements OnDestroy, OnChanges, AfterViewInit, C
showNoItemsFound() {
const empty = this.itemsList.filteredItems.length === 0;
return ((empty && !this._isTypeahead && !this.loading) ||
(empty && this._isTypeahead && this.filterValue && !this.loading)) &&
(empty && this._isTypeahead && this.searchTerm && !this.loading)) &&
!this.showAddTag;
}

showTypeToSearch() {
const empty = this.itemsList.filteredItems.length === 0;
return empty && this._isTypeahead && !this.filterValue && !this.loading;
return empty && this._isTypeahead && !this.searchTerm && !this.loading;
}

filter(term: string) {
this.filterValue = term;
this.searchTerm = term;

if (this._isTypeahead) {
this.typeahead.next(this.filterValue);
this.typeahead.next(this.searchTerm);
} else {
this.itemsList.filter(this.filterValue);
this.itemsList.filter(this.searchTerm);
if (this.isOpen) {
this.itemsList.markSelectedOrDefault(this.markFirst);
}
Expand Down Expand Up @@ -569,8 +574,8 @@ export class NgSelectComponent implements OnDestroy, OnChanges, AfterViewInit, C
if (items.length > 0 && this.hasValue) {
this.itemsList.mapSelectedItems();
}
if (this.isOpen && isDefined(this.filterValue) && !this._isTypeahead) {
this.itemsList.filter(this.filterValue);
if (this.isOpen && isDefined(this.searchTerm) && !this._isTypeahead) {
this.itemsList.filter(this.searchTerm);
}
if (this._isTypeahead || this.isOpen) {
this.itemsList.markSelectedOrDefault(this.markFirst);
Expand Down Expand Up @@ -721,11 +726,11 @@ export class NgSelectComponent implements OnDestroy, OnChanges, AfterViewInit, C
}

private _clearSearch() {
if (!this.filterValue) {
if (!this.searchTerm) {
return;
}

this.filterValue = null;
this.searchTerm = null;
this.itemsList.resetFilteredItems();
}

Expand Down Expand Up @@ -824,13 +829,13 @@ export class NgSelectComponent implements OnDestroy, OnChanges, AfterViewInit, C

private _nextItemIsTag(nextStep: number): boolean {
const nextIndex = this.itemsList.markedIndex + nextStep;
return this.addTag && this.filterValue
return this.addTag && this.searchTerm
&& this.itemsList.markedItem
&& (nextIndex < 0 || nextIndex === this.itemsList.filteredItems.length)
}

private _handleBackspace() {
if (this.filterValue || !this.clearable || !this.clearOnBackspace || !this.hasValue) {
if (this.searchTerm || !this.clearable || !this.clearOnBackspace || !this.hasValue) {
return;
}

Expand Down

0 comments on commit 643cf52

Please sign in to comment.