Skip to content

Commit

Permalink
feat: allow to set max selected items using multiple (#239)
Browse files Browse the repository at this point in the history
closes #181
  • Loading branch information
benjaminlefevre authored and anjmao committed Feb 8, 2018
1 parent f15fafc commit f500654
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 6 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ map: {
| [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 |
Expand Down
35 changes: 31 additions & 4 deletions demo/app/examples/multi.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,33 @@ import { DataService } from '../shared/data.service';
</div>
<hr/>
<label>Disabled multiple elements</label>
<label>Select multiple elements with a limit number of selections (e.g 3)</label>
---html,true
<ng-select
[items]="people$2 | async"
[multiple]="true"
[maxSelectedItems]="3"
bindLabel="name"
[(ngModel)]="selectedPeople2">
</ng-select>
---
<div class="mt-3">
Selected value: <br />
<ul>
<li *ngFor="let item of selectedPeople1">{{item.name}}</li>
</ul>
<button (click)="clearModel()" class="btn btn-secondary btn-sm">Clear model</button>
</div>
<hr/>
<label>Disabled multiple elements</label>
---html,true
<ng-select
[items]="people$3 | async"
bindLabel="name"
[multiple]="true"
[disabled]="disable"
[(ngModel)]="selectedPeople2">
[(ngModel)]="selectedPeople3">
</ng-select>
---
<br>
Expand Down Expand Up @@ -64,6 +83,9 @@ export class SelectMultiComponent {

people$2: Observable<any[]>;
selectedPeople2 = [];

people$3: Observable<any[]>;
selectedPeople3 = [];
disable = true;

githubUsers$: Observable<any[]>;
Expand All @@ -74,18 +96,23 @@ export class SelectMultiComponent {
ngOnInit() {
this.people$1 = this.dataService.getPeople();
this.people$2 = this.dataService.getPeople();
this.people$3 = this.dataService.getPeople();
this.githubUsers$ = this.dataService.getGithubAccounts('anjm');

this.selectedPeople2 = [
this.selectedPeople3 = [
{ id: '5a15b13c2340978ec3d2c0ea', name: 'Rochelle Estes' },
{ id: '5a15b13c728cd3f43cc0fe8a', name: 'Marquez Nolan' }
];
}

clearModel() {
clearModel1() {
this.selectedPeople1 = [];
}

clearModel2() {
this.selectedPeople2 = [];
}

}


6 changes: 5 additions & 1 deletion src/ng-select/items-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class ItemsList {
}

select(item: NgOption) {
if (item.selected) {
if (item.selected || this.maxItemsSelected()) {
return;
}
if (!this._ngSelect.multiple) {
Expand All @@ -47,6 +47,10 @@ export class ItemsList {
item.selected = true;
}

maxItemsSelected(): boolean {
return this._ngSelect.multiple && this._ngSelect.maxSelectedItems <= this._selected.length;
}

findItem(value: any): NgOption {
if (this._ngSelect.bindValue) {
return this._items.find(item => this.resolveNested(item.value, this._ngSelect.bindValue) === value);
Expand Down
39 changes: 39 additions & 0 deletions src/ng-select/ng-select.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,45 @@ describe('NgSelectComponent', function () {
});
});

describe('Multiple with max limit of selection', () => {
let fixture: ComponentFixture<NgSelectBasicTestCmp>;
let arrowIcon: DebugElement = null;

beforeEach(() => {
fixture = createTestingModule(
NgSelectBasicTestCmp,
`<ng-select [items]="cities"
bindLabel="name"
bindValue="this"
placeholder="select value"
[(ngModel)]="selectedCity"
[multiple]="true"
[maxSelectedItems]="2">
</ng-select>`);
arrowIcon = fixture.debugElement.query(By.css('.ng-arrow-zone'));
});

it('should be able to select only two elements', fakeAsync(() => {
selectOption(fixture, KeyCode.ArrowDown, 0);
selectOption(fixture, KeyCode.ArrowDown, 1);
selectOption(fixture, KeyCode.ArrowDown, 1);
tickAndDetectChanges(fixture);
expect((<NgOption[]>fixture.componentInstance.select.selectedItems).length).toBe(2);
}));

it('should click on arrow be disabled when maximum of items is reached', fakeAsync (() => {
const clickArrow = () => arrowIcon.triggerEventHandler('click', { stopPropagation: () => { } });
selectOption(fixture, KeyCode.ArrowDown, 0);
selectOption(fixture, KeyCode.ArrowDown, 1);
tickAndDetectChanges(fixture);
// open
clickArrow();
tickAndDetectChanges(fixture);
expect(fixture.componentInstance.select.isOpen).toBe(false);
expect((<NgOption[]>fixture.componentInstance.select.selectedItems).length).toBe(2);
}));
});

describe('Tagging', () => {
it('should select default tag', fakeAsync(() => {
let fixture = createTestingModule(
Expand Down
3 changes: 2 additions & 1 deletion src/ng-select/ng-select.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export class NgSelectComponent implements OnInit, OnDestroy, OnChanges, AfterVie
@Input() appendTo: string;
@Input() loading = false;
@Input() closeOnSelect = true;
@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;
Expand Down Expand Up @@ -273,7 +274,7 @@ export class NgSelectComponent implements OnInit, OnDestroy, OnChanges, AfterVie
}

open() {
if (this.isDisabled || this.isOpen) {
if (this.isDisabled || this.isOpen || this.itemsList.maxItemsSelected()) {
return;
}
this.isOpen = true;
Expand Down

0 comments on commit f500654

Please sign in to comment.