fix(select): update text on ngModel change

This commit is contained in:
Adam Bradley
2016-01-27 22:22:34 -06:00
parent 2d691b05d7
commit 0a04522d38
5 changed files with 108 additions and 92 deletions

View File

@ -6,12 +6,12 @@
ion-select { ion-select {
display: flex; display: flex;
overflow: hidden; overflow: hidden;
max-width: 45%;
} }
.select-text { .select-text {
flex: 1; flex: 1;
min-width: 16px; min-width: 16px;
max-width: 120px;
font-size: inherit; font-size: inherit;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;

View File

@ -4,7 +4,7 @@ import {NgControl} from 'angular2/common';
import {Alert} from '../alert/alert'; import {Alert} from '../alert/alert';
import {Form} from '../../util/form'; import {Form} from '../../util/form';
import {Item} from '../item/item'; import {Item} from '../item/item';
import {merge, isDefined, isTrueProperty} from '../../util/util'; import {merge, isTrueProperty, isBlank} from '../../util/util';
import {NavController} from '../nav/nav-controller'; import {NavController} from '../nav/nav-controller';
import {Option} from '../option/option'; import {Option} from '../option/option';
@ -97,7 +97,7 @@ import {Option} from '../option/option';
@Component({ @Component({
selector: 'ion-select', selector: 'ion-select',
template: template:
'<div class="select-text">{{text}}</div>' + '<div class="select-text">{{_text}}</div>' +
'<div class="select-icon">' + '<div class="select-icon">' +
'<div class="select-icon-inner"></div>' + '<div class="select-icon-inner"></div>' +
'</div>' + '</div>' +
@ -112,22 +112,25 @@ import {Option} from '../option/option';
export class Select { export class Select {
private _disabled: any = false; private _disabled: any = false;
private _labelId: string; private _labelId: string;
private _multi: boolean = false;
private _options: QueryList<Option>;
private _values: Array<string> = [];
private _texts: Array<string> = [];
private _text: string = '';
/**
* @private
*/
id: string; id: string;
text: string = '';
@Input() cancelText: string = 'Cancel'; @Input() cancelText: string = 'Cancel';
@Input() okText: string = 'OK'; @Input() okText: string = 'OK';
@Input() value: string = '';
@Input() alertOptions: any = {}; @Input() alertOptions: any = {};
@Input() checked: any = false; @Input() checked: any = false;
@Input() disabled: boolean = false; @Input() disabled: boolean = false;
@Input() multiple: string = '';
@Output() change: EventEmitter<any> = new EventEmitter(); @Output() change: EventEmitter<any> = new EventEmitter();
@ContentChildren(Option) options: QueryList<Option>;
constructor( constructor(
private _form: Form, private _form: Form,
private _elementRef: ElementRef, private _elementRef: ElementRef,
@ -153,29 +156,6 @@ export class Select {
} }
} }
/**
* @private
*/
ngAfterContentInit() {
let selectedValues = [];
let selectedTexts = [];
this.options.toArray().forEach(option => {
if (option.checked) {
selectedValues.push( isDefined(option.value) ? option.value : option.text );
selectedTexts.push(option.text);
option.select.emit(option.value);
}
});
this.value = selectedValues.join(',');
this.text = selectedTexts.join(', ');
setTimeout(()=> {
this.onChange(this.value);
});
}
/** /**
* @private * @private
*/ */
@ -185,8 +165,6 @@ export class Select {
ev.preventDefault(); ev.preventDefault();
ev.stopPropagation(); ev.stopPropagation();
let isMulti = this.multiple === 'true';
// the user may have assigned some options specifically for the alert // the user may have assigned some options specifically for the alert
let alertOptions = merge({}, this.alertOptions); let alertOptions = merge({}, this.alertOptions);
@ -201,9 +179,9 @@ export class Select {
// user cannot provide inputs from alertOptions // user cannot provide inputs from alertOptions
// alert inputs must be created by ionic from ion-options // alert inputs must be created by ionic from ion-options
alertOptions.inputs = this.options.toArray().map(input => { alertOptions.inputs = this._options.toArray().map(input => {
return { return {
type: (isMulti ? 'checkbox' : 'radio'), type: (this._multi ? 'checkbox' : 'radio'),
label: input.text, label: input.text,
value: input.value, value: input.value,
checked: input.checked checked: input.checked
@ -213,82 +191,98 @@ export class Select {
// create the alert instance from our built up alertOptions // create the alert instance from our built up alertOptions
let alert = Alert.create(alertOptions); let alert = Alert.create(alertOptions);
if (isMulti) { if (this._multi) {
// use checkboxes // use checkboxes
alert.setCssClass('select-alert multiple-select-alert'); alert.setCssClass('select-alert multiple-select-alert');
} else {
// use radio buttons
alert.setCssClass('select-alert single-select-alert');
}
alert.addButton({ alert.addButton({
text: this.okText, text: this.okText,
handler: selectedValues => { handler: selectedValues => {
// passed an array of all the values which were checked
this.value = selectedValues; this.value = selectedValues;
// keep a list of all the selected texts
let selectedTexts = [];
this.options.toArray().forEach(option => {
if (selectedValues.indexOf(option.value) > -1) {
// this option is one that was checked
option.checked = true;
option.select.emit(option.value);
selectedTexts.push(option.text);
} else {
// this option was not checked
option.checked = false;
}
});
this.text = selectedTexts.join(', ');
this.onChange(selectedValues); this.onChange(selectedValues);
this.change.emit(selectedValues); this.change.emit(selectedValues);
} }
}); });
} else {
// use radio buttons
alert.setCssClass('select-alert single-select-alert');
alert.addButton({
text: this.okText,
handler: selectedValue => {
// passed the single value that was checked
// or undefined if nothing was checked
this.value = selectedValue;
this.text = '';
this.options.toArray().forEach(option => {
if (option.value === selectedValue) {
// this option was the one that was checked
option.checked = true;
option.select.emit(option.value);
this.text = option.text;
} else {
// this option was not checked
option.checked = false;
}
});
this.onChange(selectedValue);
this.change.emit(selectedValue);
}
});
}
this._nav.present(alert, alertOptions); this._nav.present(alert, alertOptions);
} }
@Input()
get multiple() {
return this._multi;
}
set multiple(val) {
this._multi = isTrueProperty(val);
}
@Input()
get value(): any {
return (this._multi ? this._values : this._values.join());
}
set value(val: any) {
// passed in value could be either an array, undefined or a string
this._values = (Array.isArray(val) ? val : isBlank(val) ? [] : [val]);
this.updateOptions();
}
get text() {
return (this._multi ? this._texts : this._texts.join());
}
@ContentChildren(Option)
private set options(val: QueryList<Option>) {
this._options = val;
if (!this._values.length) {
// there are no values set at this point
// so check to see who should be checked
this._values = val.toArray().filter(o => o.checked).map(o => o.value);
}
this.updateOptions();
}
private updateOptions() {
this._texts = [];
if (this._options) {
this._options.toArray().forEach(option => {
// check this option if the option's value is in the values array
option.checked = (this._values.indexOf(option.value) > -1);
if (option.checked) {
this._texts.push(option.text);
}
});
}
this._text = this._texts.join(', ');
}
ngAfterContentInit() {
// using a setTimeout here to prevent
// "has changed after it was checked" error
// this will be fixed in future ng2 versions
setTimeout(()=> {
this.onChange(this._values);
});
}
/** /**
* @private * @private
* Angular2 Forms API method called by the model (Control) on change to update * Angular2 Forms API method called by the model (Control) on change to update
* the checked value. * the checked value.
* https://github.com/angular/angular/blob/master/modules/angular2/src/forms/directives/shared.ts#L34 * https://github.com/angular/angular/blob/master/modules/angular2/src/forms/directives/shared.ts#L34
*/ */
writeValue(value) { writeValue(val) {
if (value !== null) { if (!isBlank(val)) {
this.value = value; this.value = val;
} }
} }

View File

@ -6,6 +6,16 @@ import {App, Page} from 'ionic/ionic';
}) })
class E2EPage { class E2EPage {
constructor() {
this.petOptions = [
{ text: 'Bird', value: 'bird' },
{ text: 'Cat', value: 'cat', checked: true },
{ text: 'Dog', value: 'dog', checked: true },
{ text: 'Honey Badger', value: 'honeybadger' },
{ text: 'Pig', value: 'pig' },
];
}
carChange(selectedValues) { carChange(selectedValues) {
console.log('carChange', selectedValues); console.log('carChange', selectedValues);
} }

View File

@ -21,8 +21,8 @@
</ion-item> </ion-item>
<ion-item> <ion-item>
<ion-label>Car Options</ion-label> <ion-label>Car Features</ion-label>
<ion-select [(ngModel)]="carOptions" multiple="true" (change)="carChange($event)"> <ion-select [(ngModel)]="carFeatures" [multiple]="true" (change)="carChange($event)">
<ion-option value="backupcamera">Backup Camera</ion-option> <ion-option value="backupcamera">Backup Camera</ion-option>
<ion-option value="heatedseats">Headted Seats</ion-option> <ion-option value="heatedseats">Headted Seats</ion-option>
<ion-option value="keyless">Keyless Entry</ion-option> <ion-option value="keyless">Keyless Entry</ion-option>
@ -32,9 +32,17 @@
</ion-select> </ion-select>
</ion-item> </ion-item>
<ion-item>
<ion-label>Pets</ion-label>
<ion-select [(ngModel)]="pets" multiple>
<ion-option *ngFor="#o of petOptions" [value]="o.value" [checked]="o.checked">{{o.text}}</ion-option>
</ion-select>
</ion-item>
<p aria-hidden="true" padding> <p aria-hidden="true" padding>
<code>toppings: {{toppings}}</code><br> <code>toppings: {{toppings}}</code><br>
<code>carOptions: {{carOptions}}</code><br> <code>carFeatures: {{carFeatures}}</code><br>
<code>pets: {{pets}}</code><br>
</p> </p>
</ion-content> </ion-content>

View File

@ -10,6 +10,10 @@ class E2EPage {
title: '1994 Music', title: '1994 Music',
subTitle: 'Select your favorite' subTitle: 'Select your favorite'
}; };
setTimeout(() => {
this.gender = 'm';
}, 1500);
} }
stpSelect() { stpSelect() {