mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-21 04:53:58 +08:00
fix(select): update text on ngModel change
This commit is contained in:
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
@ -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() {
|
||||||
|
Reference in New Issue
Block a user