Skip to content

Commit

Permalink
feat(typeahead): allow veto of an item selection
Browse files Browse the repository at this point in the history
Closes #699

BREAKING CHANGE: payload of the typeahead's `selectItem` event was changed - now it is
an object implementing the `NgbTypeaheadSelectItemEvent`. You can easily migrate your existing
code by changing:

`<input [ngbTypeahead]="find" (selectItem)="onSelect($event)"/>`

to

`<input [ngbTypeahead]="find" (selectItem)="onSelect($event.item)"/>`

Closes #733
  • Loading branch information
pkozlowski-opensource committed Sep 14, 2016
1 parent 567e349 commit e793944
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 9 deletions.
1 change: 1 addition & 0 deletions demo/src/app/components/typeahead/typeahead.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {DEMO_SNIPPETS} from './demos';
template: `
<ngbd-content-wrapper component="Typeahead">
<ngbd-api-docs directive="NgbTypeahead"></ngbd-api-docs>
<ngbd-api-docs-class type="NgbTypeaheadSelectItemEvent"></ngbd-api-docs-class>
<ngbd-api-docs-class type="ResultTemplateContext"></ngbd-api-docs-class>
<ngbd-api-docs-config type="NgbTypeaheadConfig"></ngbd-api-docs-config>
<ngbd-example-box demoTitle="Simple Typeahead" [htmlSnippet]="snippets.basic.markup" [tsSnippet]="snippets.basic.code">
Expand Down
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {NgbRatingModule} from './rating/rating.module';
import {NgbTabsetModule, NgbTabChangeEvent} from './tabset/tabset.module';
import {NgbTimepickerModule} from './timepicker/timepicker.module';
import {NgbTooltipModule} from './tooltip/tooltip.module';
import {NgbTypeaheadModule} from './typeahead/typeahead.module';
import {NgbTypeaheadModule, NgbTypeaheadSelectItemEvent} from './typeahead/typeahead.module';

export {NgbPanelChangeEvent, NgbAccordionConfig} from './accordion/accordion.module';
export {NgbModal, NgbModalOptions, NgbModalRef, ModalDismissReasons} from './modal/modal.module';
Expand All @@ -31,7 +31,7 @@ export {NgbRatingConfig} from './rating/rating.module';
export {NgbTimepickerConfig} from './timepicker/timepicker.module';
export {NgbTabsetConfig} from './tabset/tabset.module';
export {NgbTooltipConfig} from './tooltip/tooltip.module';
export {NgbTypeaheadConfig} from './typeahead/typeahead.module';
export {NgbTypeaheadConfig, NgbTypeaheadSelectItemEvent} from './typeahead/typeahead.module';

@NgModule({
exports: [
Expand Down
3 changes: 2 additions & 1 deletion src/typeahead/typeahead.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import {CommonModule} from '@angular/common';

import {NgbHighlight} from './highlight';
import {NgbTypeaheadWindow} from './typeahead-window';
import {NgbTypeahead} from './typeahead';
import {NgbTypeahead, NgbTypeaheadSelectItemEvent} from './typeahead';
import {NgbTypeaheadConfig} from './typeahead-config';

export {NgbTypeaheadConfig} from './typeahead-config';
export {NgbTypeaheadSelectItemEvent} from './typeahead';

@NgModule({
declarations: [NgbTypeahead, NgbHighlight, NgbTypeaheadWindow],
Expand Down
15 changes: 14 additions & 1 deletion src/typeahead/typeahead.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ describe('ngb-typeahead', () => {
describe('select event', () => {

it('should raise select event when a result is selected', () => {
const fixture = createTestComponent('<input type="text" [ngbTypeahead]="find" (selectItem)="onSelect($event)"/>');
const fixture = createTestComponent('<input [ngbTypeahead]="find" (selectItem)="onSelect($event.item)"/>');
const input = getNativeInput(fixture.nativeElement);

// clicking selected
Expand All @@ -395,6 +395,19 @@ describe('ngb-typeahead', () => {

expect(fixture.componentInstance.selectEventValue).toBe('one');
});

it('should not propagate model when preventDefault() is called on selectEvent', async(() => {
const fixture = createTestComponent(
'<input [(ngModel)]="model" [ngbTypeahead]="find" (selectItem)="$event.preventDefault()"/>');
const input = getNativeInput(fixture.nativeElement);

// clicking selected
changeInput(fixture.nativeElement, 'o');
fixture.detectChanges();
getWindowLinks(fixture.debugElement)[0].triggerEventHandler('click', {});
fixture.detectChanges();
fixture.whenStable().then(() => { expect(fixture.componentInstance.model).toBe('o'); });
}));
});

describe('auto attributes', () => {
Expand Down
30 changes: 25 additions & 5 deletions src/typeahead/typeahead.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,21 @@ const NGB_TYPEAHEAD_VALUE_ACCESSOR = {
multi: true
};

/**
* Payload of the selectItem event.
*/
export interface NgbTypeaheadSelectItemEvent {
/**
* An item about to be selected
*/
item: any;

/**
* Function that will prevent item selection if called
*/
preventDefault: () => void;
}

/**
* NgbTypeahead directive provides a simple way of creating powerful typeaheads from any text input
*/
Expand Down Expand Up @@ -93,9 +108,9 @@ export class NgbTypeahead implements OnInit,
@Input() showHint: boolean;

/**
* An event emitted when a match is selected. Event payload is equal to the selected item.
* An event emitted when a match is selected. Event payload is of type NgbTypeaheadSelectItemEvent.
*/
@Output() selectItem = new EventEmitter();
@Output() selectItem = new EventEmitter<NgbTypeaheadSelectItemEvent>();

onChange = (value) => {
this._onChangeNoEmit(value);
Expand Down Expand Up @@ -215,9 +230,14 @@ export class NgbTypeahead implements OnInit,
}

private _selectResult(result: any) {
this.writeValue(result);
this._onChangeNoEmit(result);
this.selectItem.emit(result);
let defaultPrevented = false;
this.selectItem.emit({item: result, preventDefault: () => { defaultPrevented = true; }});

if (!defaultPrevented) {
this.writeValue(result);
this._onChangeNoEmit(result);
}

this._closePopup();
}

Expand Down

0 comments on commit e793944

Please sign in to comment.