Skip to content

Commit cd616c0

Browse files
committed
Updates to select component
Fixed ngModel not working when initially setting a value, added support for maximum number of selected values for multi-select
1 parent 3694714 commit cd616c0

File tree

4 files changed

+82
-36
lines changed

4 files changed

+82
-36
lines changed

components/search/search.component.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,15 @@ export class Search extends Dropdown implements AfterViewInit {
9494
this._service.itemSelectedClass = "active";
9595
}
9696

97-
protected search(callback:Function):void {
97+
protected search(callback?:Function):void {
9898
this._loading = true;
9999
if (this._optionsLookup) {
100100
if (this._resultsCache[this._query]) {
101101
this._results = this._resultsCache[this._query];
102102
this._loading = false;
103-
callback();
103+
if (callback) {
104+
callback();
105+
}
104106
return;
105107
}
106108

@@ -110,9 +112,11 @@ export class Search extends Dropdown implements AfterViewInit {
110112
});
111113
return;
112114
}
113-
this._results = this.options.filter((o:string) => this.deepValue(o, this.optionsField).slice(0, this.query.length).toLowerCase() == this.query.toLowerCase());
115+
this._results = this.options.filter((o:string) => this.deepValue(o, this.optionsField).toString().slice(0, this.query.length).toLowerCase() == this.query.toLowerCase());
114116
this._loading = false;
115-
callback();
117+
if (callback) {
118+
callback();
119+
}
116120
}
117121

118122
private result(i:number):any {
Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,34 @@
1-
import {Component, Input, HostBinding, ElementRef, HostListener} from 'angular2/core';
1+
import {Component, Input, HostBinding, ElementRef, HostListener, AfterContentInit} from 'angular2/core';
22
import {Select} from '../select';
33

44
@Component({
55
selector: 'sui-select-option',
66
template: `<ng-content></ng-content>`
77
})
8-
export class SelectOption {
8+
export class SelectOption implements AfterContentInit {
99
@HostBinding('class.item') itemClass = true;
1010

1111
@Input()
1212
public value:any;
1313

14+
public HTML:string;
15+
1416
constructor(private host:Select, private el:ElementRef) { }
1517

18+
public ngAfterContentInit():void {
19+
this.HTML = this.el.nativeElement.innerHTML;
20+
var existingIndex = this.host.renderedOptions.findIndex((rO:SelectOption) => rO.value == this.value);
21+
if (existingIndex != -1) {
22+
this.host.renderedOptions.splice(existingIndex, 1);
23+
}
24+
this.host.renderedOptions.push(this);
25+
}
26+
1627
@HostListener('click', ['$event'])
17-
public click(event):boolean {
28+
public click(event:MouseEvent):boolean {
1829
event.stopPropagation();
19-
this.host.selectOption(this.value, this.el.nativeElement.innerHTML);
30+
this.HTML = this.el.nativeElement.innerHTML;
31+
this.host.selectOption(this);
2032
return false;
2133
}
2234
}

components/select/select.component.ts

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
import {Component, Directive, Provider, ViewChild, HostBinding, ElementRef, HostListener, forwardRef} from 'angular2/core';
1+
import {Component, Directive, Provider, ViewChild, HostBinding, ElementRef, HostListener, OnInit, forwardRef} from 'angular2/core';
22
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from 'angular2/common';
33
import {Search, SearchValueAccessor} from '../search';
4+
import {SelectOption} from './select-option.component';
45
import {DropdownMenu} from '../dropdown';
56
import {KEYCODE} from '../dropdown/dropdown.service';
67

78
@Component({
89
selector: 'sui-select',
910
directives: [DropdownMenu],
10-
inputs: ['placeholder', 'options', 'optionsField', 'isSearchable', 'searchDelay', 'isDisabled', 'allowMultiple'],
11+
inputs: ['placeholder', 'options', 'optionsField', 'isSearchable', 'searchDelay', 'isDisabled', 'allowMultiple', 'maxSelected'],
1112
outputs: ['selectedOptionChange'],
1213
host: {
1314
'[class.visible]': 'isOpen',
@@ -28,12 +29,13 @@ import {KEYCODE} from '../dropdown/dropdown.service';
2829
<!-- Select dropdown menu -->
2930
<div class="menu" suiDropdownMenu>
3031
<ng-content></ng-content>
31-
<div *ngIf="!results.length" class="message">No Results</div>
32+
<div *ngIf="!results.length && !maxSelectedReached" class="message">No Results</div>
33+
<div *ngIf="!results.length && maxSelectedReached" class="message">Max {{ maxSelected }} selections</div>
3234
</div>
3335
`,
34-
styles: [`:host input.search { width: 12em !important; }`]
36+
styles: [`:host input.search { width: 12em !important; } .selected-results { display: none; }`]
3537
})
36-
export class Select extends Search {
38+
export class Select extends Search implements OnInit {
3739
@ViewChild(DropdownMenu) protected _menu:DropdownMenu;
3840

3941
@HostBinding('class.ui')
@@ -44,14 +46,18 @@ export class Select extends Search {
4446
public isSearchable:boolean = false;
4547
@HostBinding('class.multiple')
4648
public allowMultiple:boolean = false;
47-
protected searchDelay:number = 0;
49+
public searchDelay:number = 0;
4850
@HostBinding('class.loading')
4951
protected _loading:boolean = false;
5052
public placeholder:string = "Select one";
5153
public selectedOptionHTML:string;
5254

5355
public selectedOptions:any = [];
5456
public selectedOptionsHTML:Array<string> = [];
57+
public maxSelected:number;
58+
private maxSelectedReached:boolean = false;
59+
60+
public renderedOptions:Array<SelectOption> = [];
5561

5662
@HostBinding('class.active')
5763
public get isOpen():boolean {
@@ -63,12 +69,17 @@ export class Select extends Search {
6369
}
6470

6571
protected get results():Array<any> {
72+
this.maxSelectedReached = false;
6673
var results = this.options;
6774
if (this.isSearchable || this._optionsLookup) {
6875
results = this._results;
6976
}
7077
if (this.allowMultiple) {
71-
results = results.filter(r => (this.selectedOptions || []).indexOf(r) == -1);
78+
results = results.filter((r:any) => (this.selectedOptions || []).indexOf(r) == -1);
79+
if (this.selectedOptions && this.maxSelected == this.selectedOptions.length) {
80+
this.maxSelectedReached = true;
81+
results = [];
82+
}
7283
}
7384
return results;
7485
}
@@ -100,26 +111,33 @@ export class Select extends Search {
100111
});
101112
}
102113

103-
public selectOption(option:any, valueHTML:string):void {
114+
public ngOnInit():void {
115+
if (this.isSearchable) {
116+
//Initialise initial results
117+
this.search();
118+
}
119+
}
120+
121+
public selectOption(selectOption:SelectOption):void {
104122
if (!this.allowMultiple) {
105-
super.select(option);
106-
this.selectedOptionHTML = valueHTML;
123+
super.select(selectOption.value);
124+
this.selectedOptionHTML = selectOption.HTML;
107125
}
108126
else {
109127
this.selectedOptions = this.selectedOptions || [];
110-
this.selectedOptions.push(option);
111-
this.selectedOptionsHTML.push(valueHTML);
128+
this.selectedOptions.push(selectOption.value);
129+
this.selectedOptionsHTML.push(selectOption.HTML);
112130

113131
this.selectedOptionChange.emit(this.selectedOptions);
114-
this.onItemSelected.emit(option);
132+
this.onItemSelected.emit(selectOption.value);
115133
}
116134
if (this.isSearchable) {
117135
this.focusFirstItem();
118136
this.focusSearch();
119137
}
120138
this._query = "";
121139
if (this.isSearchable) {
122-
this.search(() => {});
140+
this.search();
123141
}
124142
}
125143

@@ -135,7 +153,7 @@ export class Select extends Search {
135153
}
136154

137155
//noinspection JSMethodCanBeStatic
138-
private selectedOptionClick(event) {
156+
private selectedOptionClick(event:MouseEvent) {
139157
event.stopPropagation();
140158
}
141159

@@ -149,15 +167,24 @@ export class Select extends Search {
149167
setTimeout(() => {
150168
this._service.selectedItem = null;
151169
this._service.selectNextItem();
152-
})
170+
});
153171
}
154172

155173
public writeValue(value:any) {
156174
if (this.allowMultiple) {
157-
this.selectedOptions = value;
175+
//This allows all of the possible results to load in first, so we can set the innerHTML correctly without using a template.
176+
setTimeout(() => {
177+
this.selectedOptions = value;
178+
(this.selectedOptions || []).forEach((v:any, i:number) => {
179+
this.selectedOptionsHTML[i] = this.renderedOptions.find((rO:SelectOption) => rO.value == v).HTML
180+
});
181+
});
158182
return;
159183
}
160184
this.selectedOption = value;
185+
if (value) {
186+
setTimeout(() => this.selectedOptionHTML = this.renderedOptions.find((rO:SelectOption) => rO.value == value).HTML);
187+
}
161188
}
162189

163190
@HostListener('click', ['$event'])
@@ -179,7 +206,7 @@ export class Select extends Search {
179206
return false;
180207
}
181208

182-
public searchKeyDown(event) {
209+
public searchKeyDown(event:KeyboardEvent) {
183210
if (event.which == KEYCODE.BACKSPACE && !this._query) {
184211
var selectedOptions = this.selectedOptions || [];
185212
var lastSelectedOption = selectedOptions[selectedOptions.length - 1];

demo/app/components/test.page.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,17 @@ import {TEMPLATE_DIRECTIVES} from "../../../components/template";
1919
<div class="ui main container">
2020
<div class="ui dividing right rail"></div>
2121
<h2 class="ui dividing header">Examples</h2>
22+
<!--<div class="ui segment">-->
23+
<!--<sui-select [placeholder]="placeholder" [options]="testOptions" [(ngModel)]="selected" [isSearchable]="true" #select>-->
24+
<!--<sui-select-option *ngFor="#result of select.results" [value]="result"><i class="af flag"></i>{{ result }}</sui-select-option>-->
25+
<!--</sui-select>-->
26+
<!--</div>-->
27+
<!--<div class="ui segment">-->
28+
<!--<p>Selected option: {{ selected | json }}</p>-->
29+
<!--</div>-->
2230
<div class="ui segment">
23-
<sui-select [placeholder]="placeholder" [options]="options" optionsField="test" [(ngModel)]="selected" [isSearchable]="true" #select>
24-
<sui-select-option *ngFor="#result of select.results" [value]="result"><i class="af flag"></i>{{ result.test }}</sui-select-option>
25-
</sui-select>
26-
</div>
27-
<div class="ui segment">
28-
<p>Selected option: {{ selected | json }}</p>
29-
</div>
30-
<div class="ui segment">
31-
<sui-select class="fluid" [options]="optionsSearch" optionsField="test" [(ngModel)]="selectedItems" [isSearchable]="true" [allowMultiple]="true" #multiSelect>
32-
<sui-select-option *ngFor="#result of multiSelect.results" [value]="result"><i class="af flag"></i>{{ result.test }}</sui-select-option>
31+
<sui-select class="fluid" [options]="testOptions" [(ngModel)]="selectedItems" [isSearchable]="true" [allowMultiple]="true" [maxSelected]="3" #multiSelect>
32+
<sui-select-option *ngFor="#result of multiSelect.results" [value]="result"><i class="af flag"></i>{{ result }}</sui-select-option>
3333
</sui-select>
3434
</div>
3535
<div class="ui segment">
@@ -41,6 +41,9 @@ import {TEMPLATE_DIRECTIVES} from "../../../components/template";
4141
export class TestComponentPage {
4242
public options:Array<any> = [{ test: "Example"}, { test: "Test"}, { test: "What"}, { test: "No"}, { test: "Benefit"}, { test: "Oranges"}, { test: "Artemis"}, { test: "Teeeest"}];
4343
public placeholder:string = "Select Weirdness";
44+
public testOptions = [1, 2, 3, 4, 5, 6, 7, 8, 9];
45+
public selected = 6;
46+
public selectedItems = [3, 6];
4447
public optionsSearch(query:string):Promise<Array<any>> {
4548
var options = [{ test: "Example"}, { test: "Test"}, { test: "What"}, { test: "No"}, { test: "Benefit"}, { test: "Oranges"}, { test: "Artemis"}, { test: "Teeeest"}];
4649
return new Promise((resolve, reject) => {

0 commit comments

Comments
 (0)