mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-20 12:29:55 +08:00
feat(select): ion-select using alert radio/checkbox options
Closes #890 Closes #826 Closes #886
This commit is contained in:
@ -22,6 +22,7 @@
|
||||
"components/radio/radio.ios",
|
||||
"components/searchbar/searchbar.ios",
|
||||
"components/segment/segment.ios",
|
||||
"components/select/select.ios",
|
||||
"components/tabs/tabs.ios",
|
||||
"components/toggle/toggle.ios",
|
||||
"components/toolbar/toolbar.ios";
|
||||
|
@ -23,6 +23,7 @@
|
||||
"components/tap-click/ripple",
|
||||
"components/searchbar/searchbar.md",
|
||||
"components/segment/segment.md",
|
||||
"components/select/select.md",
|
||||
"components/tabs/tabs.md",
|
||||
"components/toggle/toggle.md",
|
||||
"components/toolbar/toolbar.md";
|
||||
|
@ -24,6 +24,7 @@ export * from './components/nav/view-controller'
|
||||
export * from './components/nav/nav-push'
|
||||
export * from './components/nav/nav-router'
|
||||
export * from './components/navbar/navbar'
|
||||
export * from './components/option/option'
|
||||
export * from './components/overlay/overlay'
|
||||
export * from './components/slides/slides'
|
||||
export * from './components/radio/radio'
|
||||
@ -31,6 +32,7 @@ export * from './components/scroll/scroll'
|
||||
export * from './components/scroll/pull-to-refresh'
|
||||
export * from './components/searchbar/searchbar'
|
||||
export * from './components/segment/segment'
|
||||
export * from './components/select/select'
|
||||
export * from './components/tabs/tabs'
|
||||
export * from './components/tabs/tab'
|
||||
export * from './components/tap-click/tap-click'
|
||||
|
@ -48,6 +48,10 @@ ion-alert {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
// iOS Alert Header
|
||||
// --------------------------------------------------
|
||||
|
||||
.alert-head {
|
||||
padding: $alert-ios-head-padding;
|
||||
text-align: $alert-ios-head-text-align;
|
||||
@ -64,66 +68,62 @@ ion-alert {
|
||||
color: $alert-ios-sub-title-text-color;
|
||||
}
|
||||
|
||||
|
||||
// iOS Alert Message
|
||||
// --------------------------------------------------
|
||||
|
||||
.alert-message,
|
||||
.alert-inputs {
|
||||
.alert-input-group {
|
||||
padding: $alert-ios-message-padding;
|
||||
font-size: $alert-ios-message-font-size;
|
||||
color: $alert-ios-message-text-color;
|
||||
text-align: $alert-ios-message-text-align;
|
||||
font-size: $alert-ios-message-font-size;
|
||||
}
|
||||
|
||||
.alert-input {
|
||||
padding: $alert-ios-input-padding;
|
||||
margin-top: $alert-ios-input-margin-top;
|
||||
|
||||
background-color: $alert-ios-input-background-color;
|
||||
// iOS Alert Input
|
||||
// --------------------------------------------------
|
||||
|
||||
.alert-input {
|
||||
margin-top: $alert-ios-input-margin-top;
|
||||
padding: $alert-ios-input-padding;
|
||||
border: $alert-ios-input-border;
|
||||
border-radius: $alert-ios-input-border-radius;
|
||||
background-color: $alert-ios-input-background-color;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
.alert-radio-group {
|
||||
|
||||
// iOS Alert Radio/Checkbox Group
|
||||
// --------------------------------------------------
|
||||
|
||||
.alert-radio-group,
|
||||
.alert-checkbox-group {
|
||||
max-height: 240px;
|
||||
border-top: 1px solid $alert-ios-button-border-color;
|
||||
max-height: 200px;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.alert-radio {
|
||||
.alert-tappable {
|
||||
display: flex;
|
||||
min-height: $alert-ios-button-min-height;
|
||||
border-top: 1px solid $alert-ios-button-border-color;
|
||||
cursor: pointer;
|
||||
|
||||
&[aria-checked=true] {
|
||||
color: $alert-ios-button-text-color;
|
||||
|
||||
.alert-radio-icon:after {
|
||||
position: absolute;
|
||||
border-width: 2px;
|
||||
border-style: solid;
|
||||
border-color: $alert-ios-button-text-color;
|
||||
top: 13px;
|
||||
left: 7px;
|
||||
width: 4px;
|
||||
height: 10px;
|
||||
border-left: none;
|
||||
border-top: none;
|
||||
content: '';
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// iOS Alert Radio
|
||||
// --------------------------------------------------
|
||||
|
||||
.alert-radio-label {
|
||||
flex: 1;
|
||||
order: 0;
|
||||
text-align: auto;
|
||||
padding: 13px;
|
||||
text-align: auto;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
@ -135,6 +135,43 @@ ion-alert {
|
||||
min-width: 30px;
|
||||
}
|
||||
|
||||
.alert-radio[aria-checked=true] {
|
||||
color: $alert-ios-button-text-color;
|
||||
|
||||
.alert-radio-icon:after {
|
||||
position: absolute;
|
||||
border-width: 2px;
|
||||
border-style: solid;
|
||||
border-color: $alert-ios-button-text-color;
|
||||
top: 13px;
|
||||
left: 7px;
|
||||
width: 4px;
|
||||
height: 10px;
|
||||
border-left: none;
|
||||
border-top: none;
|
||||
content: '';
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// iOS Alert Checkbox
|
||||
// --------------------------------------------------
|
||||
|
||||
.alert-checkbox-label {
|
||||
flex: 1;
|
||||
order: 0;
|
||||
padding: 13px;
|
||||
text-align: auto;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
||||
// iOS Alert Button
|
||||
// --------------------------------------------------
|
||||
|
||||
.alert-button {
|
||||
margin: 0;
|
||||
flex: 1;
|
||||
|
@ -38,6 +38,9 @@ $alert-md-buttons-justify-content: flex-end !default;
|
||||
box-shadow: $alert-md-box-shadow;
|
||||
}
|
||||
|
||||
// Material Design Alert Header
|
||||
// --------------------------------------------------
|
||||
|
||||
.alert-head {
|
||||
text-align: $alert-md-head-text-align;
|
||||
padding: $alert-md-head-padding;
|
||||
@ -51,12 +54,20 @@ $alert-md-buttons-justify-content: flex-end !default;
|
||||
font-size: $alert-md-sub-title-font-size;
|
||||
}
|
||||
|
||||
|
||||
// Material Design Alert Message
|
||||
// --------------------------------------------------
|
||||
|
||||
.alert-message,
|
||||
.alert-inputs {
|
||||
.alert-input-group {
|
||||
padding: $alert-md-message-padding;
|
||||
color: $alert-md-message-text-color;
|
||||
}
|
||||
|
||||
|
||||
// Material Design Alert Input
|
||||
// --------------------------------------------------
|
||||
|
||||
.alert-input {
|
||||
border-bottom: 1px solid $alert-md-input-border-color;
|
||||
color: $alert-md-input-text-color;
|
||||
@ -68,15 +79,20 @@ $alert-md-buttons-justify-content: flex-end !default;
|
||||
}
|
||||
}
|
||||
|
||||
.alert-radio-group {
|
||||
|
||||
// Material Design Alert Radio/Checkbox Group
|
||||
// --------------------------------------------------
|
||||
|
||||
.alert-radio-group,
|
||||
.alert-checkbox-group {
|
||||
position: relative;
|
||||
border-top: 1px solid $alert-md-input-border-color;
|
||||
border-bottom: 1px solid $alert-md-input-border-color;
|
||||
max-height: 200px;
|
||||
overflow: scroll;
|
||||
max-height: 240px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.alert-radio {
|
||||
.alert-tappable {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
@ -84,24 +100,15 @@ $alert-md-buttons-justify-content: flex-end !default;
|
||||
border-top: 1px solid $alert-md-input-border-color;
|
||||
cursor: pointer;
|
||||
|
||||
&[aria-checked=true] {
|
||||
color: $alert-md-button-text-color;
|
||||
|
||||
.alert-radio-icon {
|
||||
border-color: $alert-md-button-text-color;
|
||||
}
|
||||
|
||||
.alert-radio-icon:after {
|
||||
transform: scale3d(1, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Material Design Alert Radio
|
||||
// --------------------------------------------------
|
||||
|
||||
.alert-radio-label {
|
||||
flex: 1;
|
||||
text-align: auto;
|
||||
@ -138,7 +145,69 @@ $alert-md-buttons-justify-content: flex-end !default;
|
||||
}
|
||||
}
|
||||
|
||||
.alert-buttons {
|
||||
.alert-radio[aria-checked=true] {
|
||||
color: $alert-md-button-text-color;
|
||||
|
||||
.alert-radio-icon {
|
||||
border-color: $alert-md-button-text-color;
|
||||
}
|
||||
|
||||
.alert-radio-icon:after {
|
||||
transform: scale3d(1, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Material Design Alert Checkbox
|
||||
// --------------------------------------------------
|
||||
|
||||
.alert-checkbox-label {
|
||||
flex: 1;
|
||||
text-align: auto;
|
||||
padding: 13px 26px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.alert-checkbox-icon {
|
||||
position: relative;
|
||||
top: 13px;
|
||||
left: 13px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 2px;
|
||||
border-width: 2px;
|
||||
border-style: solid;
|
||||
border-color: darken($list-md-border-color, 40%);
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.alert-checkbox[aria-checked=true] .alert-checkbox-icon {
|
||||
background-color: $alert-md-button-text-color;
|
||||
border-color: $alert-md-button-text-color;
|
||||
|
||||
&:after {
|
||||
position: absolute;
|
||||
border-width: 2px;
|
||||
border-style: solid;
|
||||
border-color: $alert-md-background-color;
|
||||
top: 0;
|
||||
left: 3px;
|
||||
width: 4px;
|
||||
height: 8px;
|
||||
border-left: none;
|
||||
border-top: none;
|
||||
content: '';
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Material Design Alert Button
|
||||
// --------------------------------------------------
|
||||
|
||||
.alert-button-group {
|
||||
padding: $alert-md-buttons-padding;
|
||||
justify-content: $alert-md-buttons-justify-content;
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ ion-alert {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.alert-buttons {
|
||||
.alert-button-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
@ -171,6 +171,13 @@ export class Alert extends ViewController {
|
||||
this.data.buttons.push(button);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} className CSS class name to add to the alert's outer wrapper
|
||||
*/
|
||||
setCssClass(className) {
|
||||
this.data.cssClass = className;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} opts Alert options
|
||||
*/
|
||||
@ -197,7 +204,7 @@ export class Alert extends ViewController {
|
||||
|
||||
'<template ngSwitchWhen="radio">' +
|
||||
'<div class="alert-radio-group" role="radiogroup" [attr.aria-labelledby]="hdrId" [attr.aria-activedescendant]="activeId">' +
|
||||
'<div *ngFor="#i of d.inputs" (click)="rbClick(i)" [attr.aria-checked]="i.checked" [attr.id]="i.id" class="alert-radio" tappable role="radio">' +
|
||||
'<div *ngFor="#i of d.inputs" (click)="rbClick(i)" [attr.aria-checked]="i.checked" [attr.id]="i.id" class="alert-tappable alert-radio" tappable role="radio">' +
|
||||
'<div class="alert-radio-icon"></div>' +
|
||||
'<div class="alert-radio-label">' +
|
||||
'{{i.label}}' +
|
||||
@ -206,8 +213,19 @@ export class Alert extends ViewController {
|
||||
'</div>' +
|
||||
'</template>' +
|
||||
|
||||
'<template ngSwitchWhen="checkbox">' +
|
||||
'<div class="alert-checkbox-group">' +
|
||||
'<div *ngFor="#i of d.inputs" (click)="cbClick(i)" [attr.aria-checked]="i.checked" class="alert-tappable alert-checkbox" tappable role="checkbox">' +
|
||||
'<div class="alert-checkbox-icon"></div>' +
|
||||
'<div class="alert-checkbox-label">' +
|
||||
'{{i.label}}' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</template>' +
|
||||
|
||||
'<template ngSwitchDefault>' +
|
||||
'<div class="alert-inputs">' +
|
||||
'<div class="alert-input-group">' +
|
||||
'<div *ngFor="#i of d.inputs" class="alert-input-wrapper">' +
|
||||
'<input [placeholder]="i.placeholder" [(ngModel)]="i.value" [type]="i.type" class="alert-input">' +
|
||||
'</div>' +
|
||||
@ -215,7 +233,7 @@ export class Alert extends ViewController {
|
||||
'</template>' +
|
||||
|
||||
'</div>' +
|
||||
'<div class="alert-buttons">' +
|
||||
'<div class="alert-button-group">' +
|
||||
'<button *ngFor="#b of d.buttons" (click)="btnClick(b)" [ngClass]="b.cssClass" class="alert-button">' +
|
||||
'{{b.text}}' +
|
||||
'</button>' +
|
||||
@ -224,7 +242,8 @@ export class Alert extends ViewController {
|
||||
host: {
|
||||
'role': 'dialog',
|
||||
'[attr.aria-labelledby]': 'hdrId',
|
||||
'[attr.aria-describedby]': 'descId'
|
||||
'[attr.aria-describedby]': 'descId',
|
||||
'[class]': 'cssClass'
|
||||
},
|
||||
directives: [NgClass, NgSwitch, NgIf, NgFor]
|
||||
})
|
||||
@ -238,9 +257,7 @@ class AlertCmp {
|
||||
renderer: Renderer
|
||||
) {
|
||||
this.d = params.data;
|
||||
if (this.d.cssClass) {
|
||||
renderer.setElementClass(_elementRef, this.d.cssClass, true);
|
||||
}
|
||||
this.cssClass = this.d.cssClass || '';
|
||||
|
||||
this.id = (++alertIds);
|
||||
this.descId = '';
|
||||
@ -280,13 +297,10 @@ class AlertCmp {
|
||||
input.checked = (checkedInput === input);
|
||||
});
|
||||
this.activeId = checkedInput.id;
|
||||
}
|
||||
|
||||
if (!this.d.buttons.length) {
|
||||
// auto dismiss if no buttons
|
||||
setTimeout(() => {
|
||||
this.dismiss();
|
||||
}, this._config.get('pageTransitionDelay'));
|
||||
}
|
||||
cbClick(checkedInput) {
|
||||
checkedInput.checked = !checkedInput.checked;
|
||||
}
|
||||
|
||||
dismiss() {
|
||||
@ -295,16 +309,23 @@ class AlertCmp {
|
||||
|
||||
getValues() {
|
||||
if (this.inputType === 'radio') {
|
||||
// this is a radio button alert
|
||||
// return the one radio button value which is checked
|
||||
let checkedInput = this.d.inputs.find(input => input.checked);
|
||||
// this is an alert with radio buttons (single value select)
|
||||
// return the one value which is checked, otherwise undefined
|
||||
let checkedInput = this.d.inputs.find(i => i.checked);
|
||||
return checkedInput ? checkedInput.value : undefined;
|
||||
}
|
||||
|
||||
// return an object of all the values with the name as the key
|
||||
if (this.inputType === 'checkbox') {
|
||||
// this is an alert with checkboxes (multiple value select)
|
||||
// return an array of all the checked values
|
||||
return this.d.inputs.filter(i => i.checked).map(i => i.value);
|
||||
}
|
||||
|
||||
// this is an alert with text inputs
|
||||
// return an object of all the values with the input name as the key
|
||||
let values = {};
|
||||
this.d.inputs.forEach(input => {
|
||||
values[input.name] = input.value;
|
||||
this.d.inputs.forEach(i => {
|
||||
values[i.name] = i.value;
|
||||
});
|
||||
return values;
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ class E2EPage {
|
||||
let alert = Alert.create({
|
||||
title: 'Alert!',
|
||||
subTitle: 'Subtitle!!!',
|
||||
buttons: ['Ok']
|
||||
buttons: ['OK']
|
||||
});
|
||||
|
||||
this.nav.present(alert);
|
||||
|
@ -57,7 +57,7 @@ ion-checkbox {
|
||||
top: 0;
|
||||
left: 3px;
|
||||
width: 4px;
|
||||
height: 9px;
|
||||
height: 8px;
|
||||
border-left: none;
|
||||
border-top: none;
|
||||
content: '';
|
||||
|
@ -1,7 +1,6 @@
|
||||
import {Component, Directive, Optional, ElementRef, Input, Renderer, HostListener} from 'angular2/core';
|
||||
import {NgControl} from 'angular2/common';
|
||||
|
||||
import {Ion} from '../ion';
|
||||
import {Form} from '../../util/form';
|
||||
|
||||
/**
|
||||
|
@ -38,15 +38,19 @@ export class Label {
|
||||
private _renderer: Renderer
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ngOnInit() {
|
||||
if (!this.id) {
|
||||
this.id = 'lbl-' + this._form.nextId();
|
||||
}
|
||||
}
|
||||
|
||||
get text() {
|
||||
return this._elementRef.nativeElement.textContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
27
ionic/components/option/option.ts
Normal file
27
ionic/components/option/option.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import {Directive, ElementRef, Input} from 'angular2/core';
|
||||
|
||||
/**
|
||||
* @name Option
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'ion-option'
|
||||
})
|
||||
export class Option {
|
||||
constructor(private _elementRef: ElementRef) {
|
||||
this._checked = false;
|
||||
}
|
||||
|
||||
@Input() value: string;
|
||||
|
||||
get checked(): boolean {
|
||||
return this._checked;
|
||||
}
|
||||
@Input() checked;
|
||||
set checked(val:string) {
|
||||
this._checked = (val === 'true' || val === true || val === '');
|
||||
}
|
||||
|
||||
get text() {
|
||||
return this._elementRef.nativeElement.textContent;
|
||||
}
|
||||
}
|
5
ionic/components/select/select.ios.scss
Normal file
5
ionic/components/select/select.ios.scss
Normal file
@ -0,0 +1,5 @@
|
||||
@import "../../globals.ios";
|
||||
@import "./select";
|
||||
|
||||
// iOS Select
|
||||
// --------------------------------------------------
|
5
ionic/components/select/select.md.scss
Normal file
5
ionic/components/select/select.md.scss
Normal file
@ -0,0 +1,5 @@
|
||||
@import "../../globals.md";
|
||||
@import "./select";
|
||||
|
||||
// Material Design Select
|
||||
// --------------------------------------------------
|
35
ionic/components/select/select.scss
Normal file
35
ionic/components/select/select.scss
Normal file
@ -0,0 +1,35 @@
|
||||
@import "../../globals.core";
|
||||
|
||||
// Select
|
||||
// --------------------------------------------------
|
||||
|
||||
.select-icon {
|
||||
position: relative;
|
||||
min-width: 16px;
|
||||
}
|
||||
|
||||
.select-icon:after {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-top: -3px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 5px solid;
|
||||
border-right: 5px solid transparent;
|
||||
border-left: 5px solid transparent;
|
||||
color: #999;
|
||||
content: "";
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.select-text-value {
|
||||
max-width: 120px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
ion-select ion-label {
|
||||
margin: 0;
|
||||
}
|
249
ionic/components/select/select.ts
Normal file
249
ionic/components/select/select.ts
Normal file
@ -0,0 +1,249 @@
|
||||
import {Component, Directive, Optional, ElementRef, Input, Renderer, HostListener, ContentChild, ContentChildren} from 'angular2/core';
|
||||
import {NgControl} from 'angular2/common';
|
||||
|
||||
import {Alert} from '../alert/alert';
|
||||
import {Form} from '../../util/form';
|
||||
import {Label} from '../label/label';
|
||||
import {NavController} from '../nav/nav-controller';
|
||||
import {Option} from '../option/option';
|
||||
import {Form} from '../../util/form';
|
||||
import {merge} from '../../util/util';
|
||||
|
||||
/**
|
||||
* @usage
|
||||
* ```html
|
||||
* <ion-select [(ngModel)]="gender">
|
||||
* <ion-label>Gender</ion-label>
|
||||
* <ion-option value="f">Female</ion-option>
|
||||
* <ion-option value="m">Male</ion-option>
|
||||
* </ion-select>
|
||||
* ```
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ion-select',
|
||||
host: {
|
||||
'class': 'item',
|
||||
'tappable': '',
|
||||
'tabindex': 0,
|
||||
'[attr.aria-disabled]': 'disabled'
|
||||
},
|
||||
template:
|
||||
'<ng-content select="[item-left]"></ng-content>' +
|
||||
'<div class="item-inner">' +
|
||||
'<ion-item-content id="{{labelId}}">' +
|
||||
'<ng-content select="ion-label"></ng-content>' +
|
||||
'</ion-item-content>' +
|
||||
'<div class="select-text-value" item-right>{{selectedText}}</div>' +
|
||||
'<div class="select-icon" item-right></div>' +
|
||||
'</div>'
|
||||
})
|
||||
export class Select {
|
||||
@Input() public value: string = '';
|
||||
@Input() public alertOptions: any = {};
|
||||
@Input() public checked: any = false;
|
||||
@Input() disabled: boolean = false;
|
||||
@Input() id: string = '';
|
||||
@Input() multiple: string = '';
|
||||
@ContentChild(Label) label: Label;
|
||||
@ContentChildren(Option) options;
|
||||
|
||||
constructor(
|
||||
private _form: Form,
|
||||
private _elementRef: ElementRef,
|
||||
private _renderer: Renderer,
|
||||
@Optional() private _navCtrl: NavController,
|
||||
@Optional() ngControl: NgControl
|
||||
) {
|
||||
_form.register(this);
|
||||
this.selectedText = '';
|
||||
|
||||
if (ngControl) {
|
||||
ngControl.valueAccessor = this;
|
||||
}
|
||||
|
||||
if (!_navCtrl) {
|
||||
console.error('parent <ion-nav> required for <ion-select>');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ngOnInit() {
|
||||
if (!this.id) {
|
||||
this.id = 'sel-' + this._form.nextId();
|
||||
this._renderer.setElementAttribute(this._elementRef, 'id', this.id);
|
||||
}
|
||||
|
||||
this.labelId = 'lbl-' + this.id;
|
||||
this._renderer.setElementAttribute(this._elementRef, 'aria-labelledby', this.labelId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ngAfterContentInit() {
|
||||
let selectedOption = this.options.toArray().find(o => o.checked);
|
||||
if (selectedOption) {
|
||||
this.value = selectedOption.value;
|
||||
this.selectedText = selectedOption.text;
|
||||
setTimeout(()=> {
|
||||
this.onChange(this.value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@HostListener('click')
|
||||
_click() {
|
||||
let cancelText = 'Cancel';
|
||||
let submitText = 'OK';
|
||||
|
||||
let isMulti = (this.multiple === true || this.multiple === 'true');
|
||||
|
||||
// the user may have assigned some options specifically for the alert
|
||||
let alertOptions = merge({}, this.alertOptions);
|
||||
|
||||
// user can provide buttons, but only two of them, and only as text
|
||||
// index 0 becomes the cancel text, index 1 becomes the submit (ok) text
|
||||
if (alertOptions.buttons && alertOptions.buttons.length === 2) {
|
||||
cancelText = alertOptions.buttons[0];
|
||||
submitText = alertOptions.buttons[1];
|
||||
}
|
||||
|
||||
// make sure their buttons array is removed from the options
|
||||
// and we create a new array for the alert's two buttons
|
||||
alertOptions.buttons = [cancelText];
|
||||
|
||||
// if the alertOptions didn't provide an title then use the label's text
|
||||
if (!alertOptions.title) {
|
||||
alertOptions.title = this.label.text;
|
||||
}
|
||||
|
||||
// user cannot provide inputs from alertOptions
|
||||
// alert inputs must be created by ionic from ion-options
|
||||
alertOptions.inputs = this.options.toArray().map(input => {
|
||||
return {
|
||||
type: (isMulti ? 'checkbox' : 'radio'),
|
||||
label: input.text,
|
||||
value: input.value,
|
||||
checked: !!input.checked
|
||||
}
|
||||
});
|
||||
|
||||
// create the alert instance from our built up alertOptions
|
||||
let alert = Alert.create(alertOptions);
|
||||
|
||||
if (isMulti) {
|
||||
// use checkboxes
|
||||
alert.setCssClass('select-alert multiple-select-alert');
|
||||
|
||||
alert.addButton({
|
||||
text: submitText,
|
||||
handler: selectedValues => {
|
||||
// passed an array of all the values which were checked
|
||||
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;
|
||||
selectedTexts.push(option.text);
|
||||
|
||||
} else {
|
||||
// this option was not checked
|
||||
option.checked = false;
|
||||
}
|
||||
});
|
||||
|
||||
this.selectedText = selectedTexts.join(', ');
|
||||
|
||||
this.onChange(selectedValues);
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
// use radio buttons
|
||||
alert.setCssClass('select-alert single-select-alert');
|
||||
|
||||
alert.addButton({
|
||||
text: submitText,
|
||||
handler: selectedValue => {
|
||||
// passed the single value that was checked
|
||||
// or undefined if nothing was checked
|
||||
this.value = selectedValue;
|
||||
|
||||
this.selectedText = '';
|
||||
this.options.toArray().forEach(option => {
|
||||
if (option.value === selectedValue) {
|
||||
// this option was the one that was checked
|
||||
option.checked = true;
|
||||
this.selectedText = option.text;
|
||||
|
||||
} else {
|
||||
// this option was not checked
|
||||
option.checked = false;
|
||||
}
|
||||
});
|
||||
|
||||
this.onChange(selectedValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this._navCtrl.present(alert);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Angular2 Forms API method called by the model (Control) on change to update
|
||||
* the checked value.
|
||||
* https://github.com/angular/angular/blob/master/modules/angular2/src/forms/directives/shared.ts#L34
|
||||
*/
|
||||
writeValue(value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
onChange(val) {
|
||||
// TODO: figure the whys and the becauses
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
onTouched(val) {
|
||||
// TODO: figure the whys and the becauses
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Angular2 Forms API method called by the view (NgControl) to register the
|
||||
* onChange event handler that updates the model (Control).
|
||||
* https://github.com/angular/angular/blob/master/modules/angular2/src/forms/directives/shared.ts#L27
|
||||
* @param {Function} fn the onChange event handler.
|
||||
*/
|
||||
registerOnChange(fn) { this.onChange = fn; }
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Angular2 Forms API method called by the the view (NgControl) to register
|
||||
* the onTouched event handler that marks model (Control) as touched.
|
||||
* @param {Function} fn onTouched event handler.
|
||||
*/
|
||||
registerOnTouched(fn) { this.onTouched = fn; }
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ngOnDestroy() {
|
||||
this._form.deregister(this);
|
||||
}
|
||||
}
|
1
ionic/components/select/test/multiple-value/e2e.ts
Normal file
1
ionic/components/select/test/multiple-value/e2e.ts
Normal file
@ -0,0 +1 @@
|
||||
|
17
ionic/components/select/test/multiple-value/index.ts
Normal file
17
ionic/components/select/test/multiple-value/index.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import {App, Page} from 'ionic/ionic';
|
||||
|
||||
|
||||
@Page({
|
||||
templateUrl: 'main.html'
|
||||
})
|
||||
class E2EPage {}
|
||||
|
||||
|
||||
@App({
|
||||
template: '<ion-nav [root]="root"></ion-nav>'
|
||||
})
|
||||
class E2EApp {
|
||||
constructor() {
|
||||
this.root = E2EPage;
|
||||
}
|
||||
}
|
34
ionic/components/select/test/multiple-value/main.html
Normal file
34
ionic/components/select/test/multiple-value/main.html
Normal file
@ -0,0 +1,34 @@
|
||||
<ion-toolbar><ion-title>Select Item: Multiple Value</ion-title></ion-toolbar>
|
||||
|
||||
<ion-content class="outer-content">
|
||||
|
||||
<ion-select [(ngModel)]="topings" multiple="true">
|
||||
<ion-label>Topings</ion-label>
|
||||
<ion-option value="bacon">Bacon</ion-option>
|
||||
<ion-option value="olives">Black Olives</ion-option>
|
||||
<ion-option value="xcheese">Extra Cheese</ion-option>
|
||||
<ion-option value="peppers">Green Peppers</ion-option>
|
||||
<ion-option value="mushrooms">Mushrooms</ion-option>
|
||||
<ion-option value="onions">Onions</ion-option>
|
||||
<ion-option value="pepperoni">Pepperoni</ion-option>
|
||||
<ion-option value="pineapple">Pineapple</ion-option>
|
||||
<ion-option value="Sausage">Sausage</ion-option>
|
||||
<ion-option value="Spinach">Spinach</ion-option>
|
||||
</ion-select>
|
||||
|
||||
<ion-select [(ngModel)]="carOptions" multiple="true">
|
||||
<ion-label>Car Options</ion-label>
|
||||
<ion-option value="backupcamera">Backup Camera</ion-option>
|
||||
<ion-option value="heatedseats">Headted Seats</ion-option>
|
||||
<ion-option value="keyless">Keyless Entry</ion-option>
|
||||
<ion-option value="navigation">Navigation</ion-option>
|
||||
<ion-option value="parkingassist">Parking Assist</ion-option>
|
||||
<ion-option value="sunroof">Sun Roof</ion-option>
|
||||
</ion-select>
|
||||
|
||||
<p aria-hidden="true" padding>
|
||||
<code>topings: {{topings}}</code><br>
|
||||
<code>carOptions: {{carOptions}}</code><br>
|
||||
</p>
|
||||
|
||||
</ion-content>
|
1
ionic/components/select/test/single-value/e2e.ts
Normal file
1
ionic/components/select/test/single-value/e2e.ts
Normal file
@ -0,0 +1 @@
|
||||
|
17
ionic/components/select/test/single-value/index.ts
Normal file
17
ionic/components/select/test/single-value/index.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import {App, Page} from 'ionic/ionic';
|
||||
|
||||
|
||||
@Page({
|
||||
templateUrl: 'main.html'
|
||||
})
|
||||
class E2EPage {}
|
||||
|
||||
|
||||
@App({
|
||||
template: '<ion-nav [root]="root"></ion-nav>'
|
||||
})
|
||||
class E2EApp {
|
||||
constructor() {
|
||||
this.root = E2EPage;
|
||||
}
|
||||
}
|
52
ionic/components/select/test/single-value/main.html
Normal file
52
ionic/components/select/test/single-value/main.html
Normal file
@ -0,0 +1,52 @@
|
||||
<ion-toolbar><ion-title>Select Item: Single Value</ion-title></ion-toolbar>
|
||||
|
||||
<ion-content class="outer-content">
|
||||
|
||||
<ion-select [(ngModel)]="gender">
|
||||
<ion-label>Gender</ion-label>
|
||||
<ion-option value="f" checked="true">Female</ion-option>
|
||||
<ion-option value="m">Male</ion-option>
|
||||
</ion-select>
|
||||
|
||||
<ion-select [(ngModel)]="gaming">
|
||||
<ion-label>Gaming</ion-label>
|
||||
<ion-option value="nes">NES</ion-option>
|
||||
<ion-option value="n64">Nintendo64</ion-option>
|
||||
<ion-option value="ps">PlayStation</ion-option>
|
||||
<ion-option value="genesis">Sega Genesis</ion-option>
|
||||
<ion-option value="saturn">Sega Saturn</ion-option>
|
||||
<ion-option value="snes">SNES</ion-option>
|
||||
</ion-select>
|
||||
|
||||
<ion-select [(ngModel)]="os" submitText="Okay" cancelText="Nah">
|
||||
<ion-label>Operating System</ion-label>
|
||||
<ion-option value="dos">DOS</ion-option>
|
||||
<ion-option value="lunix">Linux</ion-option>
|
||||
<ion-option value="mac7">Mac OS 7</ion-option>
|
||||
<ion-option value="mac8">Mac OS 8</ion-option>
|
||||
<ion-option value="win3.1" checked>Windows 3.1</ion-option>
|
||||
<ion-option value="win95">Windows 95</ion-option>
|
||||
<ion-option value="win98">Windows 98</ion-option>
|
||||
</ion-select>
|
||||
|
||||
<ion-select [(ngModel)]="music">
|
||||
<ion-label>Music</ion-label>
|
||||
<ion-option value="aliceinchains">Alice in Chains</ion-option>
|
||||
<ion-option value="greenday">Green Day</ion-option>
|
||||
<ion-option value="hole">Hole</ion-option>
|
||||
<ion-option value="korn">Korn</ion-option>
|
||||
<ion-option value="nirvana">Nirvana</ion-option>
|
||||
<ion-option value="pearljam">Pearl Jam</ion-option>
|
||||
<ion-option value="smashingpumpkins">Smashing Pumpkins</ion-option>
|
||||
<ion-option value="soundgarden">Soundgarden</ion-option>
|
||||
<ion-option value="stp">Stone Temple Pilots</ion-option>
|
||||
</ion-select>
|
||||
|
||||
<p aria-hidden="true" padding>
|
||||
<code>gender: {{gender}}</code><br>
|
||||
<code>gaming: {{gaming}}</code><br>
|
||||
<code>os: {{os}}</code><br>
|
||||
<code>music: {{music}}</code><br>
|
||||
</p>
|
||||
|
||||
</ion-content>
|
@ -20,6 +20,8 @@ import {ItemSliding} from '../components/item/item-sliding';
|
||||
import {Toolbar, ToolbarTitle, ToolbarItem} from '../components/toolbar/toolbar';
|
||||
import {Icon} from '../components/icon/icon';
|
||||
import {Checkbox} from '../components/checkbox/checkbox';
|
||||
import {Select} from '../components/select/select';
|
||||
import {Option} from '../components/option/option';
|
||||
import {Toggle} from '../components/toggle/toggle';
|
||||
import {TextInput} from '../components/text-input/text-input';
|
||||
import {Label} from '../components/label/label';
|
||||
@ -85,6 +87,8 @@ import {ShowWhen, HideWhen} from '../components/show-hide-when/show-hide-when';
|
||||
* - Checkbox
|
||||
* - RadioGroup
|
||||
* - RadioButton
|
||||
* - Select
|
||||
* - Option
|
||||
* - Toggle
|
||||
* - ItemInput
|
||||
* - TextInput
|
||||
@ -150,6 +154,8 @@ export const IONIC_DIRECTIVES = [
|
||||
Checkbox,
|
||||
RadioGroup,
|
||||
RadioButton,
|
||||
Select,
|
||||
Option,
|
||||
Toggle,
|
||||
ItemInput,
|
||||
TextInput,
|
||||
|
Reference in New Issue
Block a user