Skip to content

Commit

Permalink
feat(tags): support promise while creating new tag (#273)
Browse files Browse the repository at this point in the history
* feat(tags): support promise while creating new tag
closes #220
  • Loading branch information
varnastadeus committed Feb 19, 2018
1 parent 8396c2e commit d0295fa
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 31 deletions.
40 changes: 20 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,27 +119,27 @@ map: {
## API
| Input | Type | Default | Required | Description |
| ------------- | ------------- | ------------- | ------------- | ------------- |
| [items] | Array<NgOption> | `[]` | yes | Items array |
| bindLabel | string | `label` | no | Object property to use for label. Default `label` |
| bindValue | string | `-` | no | Object property to use for selected model. By default binds to whole object. |
| [clearable] | boolean | `true` | no | Allow to clear selected value. Default `true`|
| [markFirst] | boolean | `true` | no | Marks first item as focused when opening/filtering. Default `true`|
| [searchable] | boolean | `true` | no | Allow to search for value. Default `true`|
| multiple | boolean | `false` | no | Allows to select multiple items. |
| maxSelectedItems | number | none | no | When multiple = true, allows to set a limit number of selection. |
| [addTag] | Function or boolean | `false` | no | Allows to create custom options. Using boolean simply adds tag with value as bindLabel. If you want custom properties add function which returns object. |
| placeholder | string | `-` | no | Placeholder text. |
| notFoundText | string | `No items found` | no | Set custom text when filter returns empty result |
| typeToSearchText | string | `Type to search` | no | Set custom text when using Typeahead |
| clearAllText | string | `Clear all` | no | Set custom text for clear all icon title |
| addTagText | string | `Add item` | no | Set custom text when using tagging |
| loadingText | string | `Loading...` | no | Set custom text when for loading items |
| [typeahead] | Subject | `-` | no | Custom autocomplete or filter. |
| [disableVirtualScroll] | boolean | false | no | Disable virtual scroll |
| [items] | `Array<NgOption>` | `[]` | yes | Items array |
| bindLabel | `string` | `label` | no | Object property to use for label. Default `label` |
| bindValue | `string` | `-` | no | Object property to use for selected model. By default binds to whole object. |
| [clearable] | `boolean` | `true` | no | Allow to clear selected value. Default `true`|
| [markFirst] | `boolean` | `true` | no | Marks first item as focused when opening/filtering. Default `true`|
| [searchable] | `boolean` | `true` | no | Allow to search for value. Default `true`|
| multiple | `boolean` | `false` | no | Allows to select multiple items. |
| maxSelectedItems | `number` | none | no | When multiple = true, allows to set a limit number of selection. |
| [addTag] | `boolean | ((term: string) => any | Promise<any>)` | `false` | no | Allows to create custom options. Using boolean simply adds tag with value as bindLabel. If you want custom properties add function which returns object or Promise. |
| placeholder | `string` | `-` | no | Placeholder text. |
| notFoundText | `string` | `No items found` | no | Set custom text when filter returns empty result |
| typeToSearchText | `string` | `Type to search` | no | Set custom text when using Typeahead |
| clearAllText | `string` | `Clear all` | no | Set custom text for clear all icon title |
| addTagText | `string` | `Add item` | no | Set custom text when using tagging |
| loadingText | `string` | `Loading...` | no | Set custom text when for loading items |
| [typeahead] | `Subject` | `-` | no | Custom autocomplete or filter. |
| [disableVirtualScroll] | `boolean` | false | no | Disable virtual scroll |
| dropdownPosition | `bottom`,`top`,`auto` | `bottom` | no | Set the dropdown position on open |
| appendTo | string | null | no | Append drodown to body or any other element using css selector |
| loading | boolean | `-` | no | you can set the loading state from the outside (e.g. async items loading) |
| closeOnSelect | boolean | true | no | whether to close the menu when a value is selected |
| appendTo | `string` | null | no | Append drodown to body or any other element using css selector |
| loading | `boolean` | `-` | no | you can set the loading state from the outside (e.g. async items loading) |
| closeOnSelect | `boolean` | true | no | whether to close the menu when a value is selected |

| Output | Description |
| ------------- | ------------- |
Expand Down
28 changes: 24 additions & 4 deletions demo/app/examples/tags.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,24 @@ import { Component, ChangeDetectionStrategy } from '@angular/core';
Selected value: {{selectedCompanyCustom | json}}
</p>
<hr>
<label>Server side tags</label>
---html,true
<ng-select [items]="companies"
[addTag]="addTagPromise"
multiple="true"
bindLabel="name"
[loading]="loading"
[(ngModel)]="selectedCompanyCustomPromise">
</ng-select>
---
`
})
export class SelectTagsComponent {

companies: any[] = [];

loading = false;
companiesNames = ['Miškas', 'Žalias', 'Flexigen'];

ngOnInit() {
Expand All @@ -44,9 +56,17 @@ export class SelectTagsComponent {
});
}

addTag = (name) => {
addTag(name) {
return { name: name, tag: true };
}
}


addTagPromise(name) {
return new Promise((resolve) => {
this.loading = true;
setTimeout(() => {
resolve({ id: 5, name: name, valid: true });
this.loading = false;
}, 1000);
})
}
}
25 changes: 25 additions & 0 deletions src/ng-select/ng-select.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,26 @@ describe('NgSelectComponent', function () {
id: 'custom tag', name: 'custom tag', custom: true
}));
}));

it('should select custom tag with promise', fakeAsync(() => {
let fixture = createTestingModule(
NgSelectBasicTestCmp,
`<ng-select [items]="cities"
bindLabel="name"
[addTag]="tagFuncPromise"
placeholder="select value"
[(ngModel)]="selectedCity">
</ng-select>`);

tickAndDetectChanges(fixture);
fixture.componentInstance.select.onFilter('server side tag');
tickAndDetectChanges(fixture);
triggerKeyDownEvent(getNgSelectElement(fixture), KeyCode.Enter);
tick();
expect(<any>fixture.componentInstance.selectedCity).toEqual(jasmine.objectContaining({
id: 5, name: 'server side tag', valid: true
}));
}));
});

describe('Placeholder', () => {
Expand Down Expand Up @@ -1514,6 +1534,11 @@ class NgSelectBasicTestCmp {
tagFunc(term) {
return { id: term, name: term, custom: true }
}
tagFuncPromise(term) {
return Promise.resolve({
id: 5, name: term, valid: true
});
};
}

@Component({
Expand Down
18 changes: 11 additions & 7 deletions src/ng-select/ng-select.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export class NgSelectComponent implements OnInit, OnDestroy, OnChanges, AfterVie
@Input() maxSelectedItems: number;
@Input() @HostBinding('class.typeahead') typeahead: Subject<string>;
@Input() @HostBinding('class.ng-multiple') multiple = false;
@Input() @HostBinding('class.taggable') addTag: boolean | ((term: string) => NgOption) = false;
@Input() @HostBinding('class.taggable') addTag: boolean | ((term: string) => any | Promise<any>) = false;
@Input() @HostBinding('class.searchable') searchable = true;

// output events
Expand Down Expand Up @@ -132,7 +132,7 @@ export class NgSelectComponent implements OnInit, OnDestroy, OnChanges, AfterVie
this.unselect(option);
};

constructor( @Inject(NG_SELECT_DEFAULT_CONFIG) config: NgSelectConfig,
constructor(@Inject(NG_SELECT_DEFAULT_CONFIG) config: NgSelectConfig,
private changeDetectorRef: ChangeDetectorRef,
private elementRef: ElementRef,
private renderer: Renderer2
Expand Down Expand Up @@ -335,15 +335,19 @@ export class NgSelectComponent implements OnInit, OnDestroy, OnChanges, AfterVie
}

selectTag() {
let tag = {};
let tag;
if (this.addTag instanceof Function) {
tag = this.addTag(this.filterValue)
tag = this.addTag(this.filterValue);
} else {
tag[this.bindLabel] = this.filterValue;
tag = { [this.bindLabel]: this.filterValue };
}

const item = this.itemsList.addItem(tag);
this.select(item);
if (tag instanceof Promise) {
tag.then(newTag => this.select(this.itemsList.addItem(newTag)))
.catch(() => { });
} else if (tag) {
this.select(this.itemsList.addItem(tag));
}
}

showClear() {
Expand Down

0 comments on commit d0295fa

Please sign in to comment.