mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-18 19:21:34 +08:00
checkbox and radio
This commit is contained in:
@ -17,8 +17,9 @@ import {Icon} from '../icon/icon';
|
||||
@IonicComponent({
|
||||
selector: 'ion-checkbox',
|
||||
host: {
|
||||
'class': 'item',
|
||||
'[attr.aria-checked]': 'checked'
|
||||
'[class.item]': 'item',
|
||||
'[attr.aria-checked]': 'input.checked',
|
||||
'(^click)': 'onClick($event)'
|
||||
},
|
||||
defaultProperties: {
|
||||
'iconOff': 'ion-ios-circle-outline',
|
||||
@ -27,7 +28,7 @@ import {Icon} from '../icon/icon';
|
||||
})
|
||||
@IonicView({
|
||||
template:
|
||||
'<div class="item-media media-checkbox" (click)="onClick($event)">' +
|
||||
'<div class="item-media media-checkbox">' +
|
||||
'<icon [name]="iconOff" class="checkbox-off"></icon>' +
|
||||
'<icon [name]="iconOn" class="checkbox-on"></icon>' +
|
||||
'</div>' +
|
||||
@ -36,42 +37,31 @@ import {Icon} from '../icon/icon';
|
||||
'</div>'
|
||||
})
|
||||
export class Checkbox extends IonInputItem {
|
||||
|
||||
_checkbox: CheckboxInput;
|
||||
|
||||
constructor(
|
||||
elementRef: ElementRef,
|
||||
config: IonicConfig
|
||||
) {
|
||||
super(elementRef, config);
|
||||
this.item = true;
|
||||
}
|
||||
|
||||
onAllChangesDone() {
|
||||
// Enforce that this directive actually contains a checkbox
|
||||
if (this._checkbox == null) {
|
||||
throw 'No <input type="checkbox"> found inside of <ion-checkbox>';
|
||||
}
|
||||
}
|
||||
onClick(ev) {
|
||||
// toggling with spacebar fires mouse event
|
||||
if (ev.target.tagName === "INPUT") return;
|
||||
|
||||
registerInput(checkboxDir) {
|
||||
if (this._checkbox != null) {
|
||||
throw 'Only one <input type="checkbox"> is allowed per <ion-checkbox>'
|
||||
}
|
||||
this._checkbox = checkboxDir.elementRef.nativeElement;
|
||||
this._checkboxDir = checkboxDir;
|
||||
}
|
||||
this.input.checked = !this.input.checked;
|
||||
|
||||
onClick(e) {
|
||||
let val = !this._checkbox.checked;
|
||||
this._checkbox.checked = val;
|
||||
this.checked = val;
|
||||
|
||||
//TODO figure out a way to trigger change on the actual input to trigger
|
||||
//TODO trigger change/mouse event on the actual input to trigger
|
||||
// form updates
|
||||
|
||||
// this._checkbox.dispatchEvent(e);
|
||||
//this._checkboxDir.control.valueAccessor.writeValue(val);
|
||||
}
|
||||
|
||||
onChangeEvent(input) {
|
||||
//TODO can we just assume this will always be true?
|
||||
this.input.checked = this.input.elementRef.nativeElement.checked;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,7 +1,4 @@
|
||||
import {Component, View} from 'angular2/angular2';
|
||||
|
||||
import {Query, QueryList, NgFor} from 'angular2/angular2';
|
||||
// import {FormBuilder, Validators, NgFormControl, Control, NgControlName, NgControlGroup, ControlContainer} from 'angular2/forms';
|
||||
import {App} from 'ionic/ionic';
|
||||
import {
|
||||
Control,
|
||||
ControlGroup,
|
||||
@ -14,8 +11,6 @@ import {
|
||||
NgFormModel,
|
||||
FormBuilder
|
||||
} from 'angular2/forms';
|
||||
import {App, Checkbox, Content, List} from 'ionic/ionic';
|
||||
//import {IONIC_DIRECTIVES} from 'ionic/ionic'
|
||||
|
||||
@App({
|
||||
templateUrl: 'main.html'
|
||||
|
@ -9,12 +9,18 @@ import {RadioButton} from '../radio/radio';
|
||||
|
||||
|
||||
@Directive({
|
||||
selector: 'input[type=checkbox],input[type=radio]'
|
||||
selector: 'input[type=checkbox],input[type=radio]',
|
||||
properties: [ 'checked', 'name' ],
|
||||
host: {
|
||||
'[checked]': 'checked',
|
||||
'[attr.name]': 'name',
|
||||
'(change)': 'onChangeEvent($event)'
|
||||
}
|
||||
})
|
||||
export class TapInput extends IonInput {
|
||||
constructor(
|
||||
@Optional() @Parent() checkboxContainer: Checkbox, //TODO have this be either Checkbox or Radio
|
||||
@Optional() @Parent() radioContainer : RadioButton,
|
||||
@Optional() @Parent() checkboxContainer: Checkbox,
|
||||
@Optional() @Parent() radioContainer: RadioButton,
|
||||
@Optional() @Ancestor() scrollView: Content,
|
||||
@Attribute('type') type: string,
|
||||
elementRef: ElementRef,
|
||||
@ -35,4 +41,8 @@ export class TapInput extends IonInput {
|
||||
this.tabIndex = this.tabIndex || '';
|
||||
}
|
||||
|
||||
onChangeEvent(ev) {
|
||||
this.container && this.container.onChangeEvent(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,134 +1,105 @@
|
||||
import {ElementRef, Ancestor} from 'angular2/angular2';
|
||||
|
||||
import {IonicDirective, IonicComponent, IonicView} from '../../config/annotations';
|
||||
import {IonicConfig} from '../../config/config';
|
||||
import {Ion} from '../ion';
|
||||
import {IonInputItem} from '../form/form';
|
||||
|
||||
let groupName = -1;
|
||||
|
||||
@IonicDirective({
|
||||
selector: 'ion-radio-group'
|
||||
selector: 'ion-radio-group',
|
||||
host: {
|
||||
'[class.list]': 'list'
|
||||
}
|
||||
})
|
||||
export class RadioGroup {
|
||||
export class RadioGroup extends Ion {
|
||||
|
||||
_name: number = ++groupName;
|
||||
buttons: Array<RadioButton> = [];
|
||||
|
||||
constructor(
|
||||
elementRef: ElementRef//,
|
||||
//cd:ControlDirective
|
||||
elementRef: ElementRef,
|
||||
ionicConfig: IonicConfig
|
||||
) {
|
||||
this.ele = elementRef.nativeElement
|
||||
// this.config = RadioGroup.config.invoke(this)
|
||||
// this.controlDirective = cd;
|
||||
// cd.valueAccessor = this; //ControlDirective should inject CheckboxControlDirective
|
||||
|
||||
this.ele.classList.add('list');
|
||||
|
||||
this.buttons = [];
|
||||
super(elementRef, ionicConfig);
|
||||
this.list = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Much like ngModel, this is called from our valueAccessor for the attached
|
||||
* ControlDirective to update the value internally.
|
||||
*/
|
||||
writeValue(value) {
|
||||
this.value = value;
|
||||
|
||||
setTimeout(() => {
|
||||
this.selectFromValue(value);
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by child SegmentButtons to bind themselves to
|
||||
* the Segment.
|
||||
*/
|
||||
register(radioButton) {
|
||||
this.buttons.push(radioButton);
|
||||
let inputEl = radioButton.input.elementRef.nativeElement;
|
||||
if (!inputEl.hasAttribute('name')) {
|
||||
radioButton.input.name = this._name;
|
||||
}
|
||||
// if (radioButton && !radioButton.hasAttribute('name')){
|
||||
// radioButton.setAttribute('name', this._name);
|
||||
// }
|
||||
}
|
||||
|
||||
// If we don't have a default value, and this is the
|
||||
// first button added, select it
|
||||
if(!this.value && this.buttons.length === 1) {
|
||||
setTimeout(() => {
|
||||
// We need to defer so the control directive can initialize
|
||||
this.selected(radioButton);
|
||||
})
|
||||
update(input) {
|
||||
for (let button of this.buttons) {
|
||||
button.input.checked = button.input.elementRef.nativeElement.checked;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the button with the given value.
|
||||
*/
|
||||
selectFromValue(value) {
|
||||
for(let button of this.buttons) {
|
||||
if(button.value === value) {
|
||||
this.selected(button);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Indicate a button should be selected.
|
||||
*/
|
||||
selected(radioButton) {
|
||||
for(let button of this.buttons) {
|
||||
button.setActive(false);
|
||||
}
|
||||
radioButton.setActive(true);
|
||||
|
||||
this.value = radioButton.value;
|
||||
// TODO: Better way to do this?
|
||||
this.controlDirective._control().updateValue(this.value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@IonicComponent({
|
||||
selector: 'ion-radio',
|
||||
properties: [
|
||||
'value'
|
||||
],
|
||||
host: {
|
||||
'(^click)': 'buttonClicked($event)'
|
||||
'[class.item]': 'item',
|
||||
'[class.active]': 'input.checked',
|
||||
'[attr.aria-checked]': 'input.checked',
|
||||
'(^click)': 'onClick($event)'
|
||||
},
|
||||
defaultProperties: {
|
||||
'iconOff': 'ion-ios-circle-outline',
|
||||
'iconOn': 'ion-ios-checkmark'
|
||||
}
|
||||
})
|
||||
@IonicView({
|
||||
template: `
|
||||
<div class="item-content">
|
||||
<div class="item-title">
|
||||
<content></content>
|
||||
</div>
|
||||
<div class="item-media media-radio">
|
||||
<icon class="radio-off"></icon>
|
||||
<icon class="ion-ios-checkmark-empty radio-on"></icon>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
template:
|
||||
'<div class="item-media media-radio">' +
|
||||
'<icon [name]="iconOff" class="radio-off"></icon>' +
|
||||
'<icon [name]="iconOn" class="radio-on"></icon>' +
|
||||
'</div>' +
|
||||
'<div class="item-content">' +
|
||||
'<content></content>' +
|
||||
'</div>'
|
||||
})
|
||||
export class RadioButton {
|
||||
export class RadioButton extends IonInputItem {
|
||||
constructor(
|
||||
@Ancestor() group: RadioGroup,
|
||||
elementRef: ElementRef
|
||||
elementRef: ElementRef,
|
||||
config: IonicConfig
|
||||
) {
|
||||
this.ele = elementRef.ele;
|
||||
|
||||
this.ele.classList.add('item')
|
||||
this.ele.setAttribute('aria-checked', true)
|
||||
|
||||
super(elementRef, config);
|
||||
this.item = true;
|
||||
this.group = group;
|
||||
|
||||
group.register(this);
|
||||
}
|
||||
|
||||
setActive(isActive) {
|
||||
// TODO: No ele
|
||||
if(isActive) {
|
||||
this.ele.classList.add('active');
|
||||
this.ele.setAttribute('aria-checked', true)
|
||||
} else {
|
||||
this.ele.classList.remove('active');
|
||||
this.ele.setAttribute('aria-checked', false)
|
||||
}
|
||||
registerInput(input) {
|
||||
this.input = input;
|
||||
this.group.register(this);
|
||||
}
|
||||
|
||||
buttonClicked(event) {
|
||||
this.group.selected(this, event);
|
||||
event.preventDefault();
|
||||
onClick(ev) {
|
||||
// switching between radio buttons with arrow keys fires a MouseEvent
|
||||
if (ev.target.tagName === "INPUT") return;
|
||||
this.input.checked = !this.input.checked;
|
||||
|
||||
//let bindings update first
|
||||
setTimeout(() => this.group.update(this.input));
|
||||
|
||||
//TODO figure out a way to trigger change on the actual input to trigger
|
||||
// form updates
|
||||
|
||||
// this._checkbox.dispatchEvent(e);
|
||||
//this._checkboxDir.control.valueAccessor.writeValue(val);
|
||||
}
|
||||
|
||||
onChangeEvent(input) {
|
||||
this.group.update(input);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,16 @@
|
||||
import {App} from 'ionic/ionic';
|
||||
import {
|
||||
Control,
|
||||
ControlGroup,
|
||||
NgForm,
|
||||
formDirectives,
|
||||
Validators,
|
||||
NgControl,
|
||||
ControlValueAccessor,
|
||||
NgControlName,
|
||||
NgFormModel,
|
||||
FormBuilder
|
||||
} from 'angular2/forms';
|
||||
|
||||
|
||||
@App({
|
||||
@ -6,9 +18,33 @@ import {App} from 'ionic/ionic';
|
||||
})
|
||||
class IonicApp {
|
||||
constructor() {
|
||||
// var fb = new FormBuilder();
|
||||
// this.form = fb.group({
|
||||
// preferredApple: ['mac', Validators.required],
|
||||
// });
|
||||
this.fruitsGroup1 = new ControlGroup({
|
||||
"appleCtrl": new Control("", isChecked),
|
||||
"bananaCtrl": new Control("", isChecked)
|
||||
});
|
||||
this.fruitsGroup2 = new ControlGroup({
|
||||
"grapeCtrl": new Control("", isChecked),
|
||||
"cherryCtrl": new Control("", isChecked)
|
||||
});
|
||||
|
||||
this.fruitsForm = new ControlGroup({
|
||||
"fruitGroup1": this.fruitsGroup1,
|
||||
"fruitGroup2": this.fruitsGroup2
|
||||
});
|
||||
|
||||
function isChecked(ctrl) {
|
||||
if (ctrl.checked) {
|
||||
return null;
|
||||
} else {
|
||||
return {
|
||||
'notChecked': true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doSubmit(event) {
|
||||
console.log('Submitting form', this.fruitsForm.value);
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
@ -3,33 +3,28 @@
|
||||
|
||||
<ion-content>
|
||||
|
||||
<form (^submit)="doSubmit($event)" [control-group]="form">
|
||||
<form (^submit)="doSubmit($event)" [ng-form-model]="fruitsForm">
|
||||
<div class="list-header">
|
||||
Radio Group
|
||||
Fruit Group 1
|
||||
</div>
|
||||
|
||||
<ion-radio-group control="preferredApple">
|
||||
|
||||
<ion-radio value="mac">
|
||||
McIntosh
|
||||
</ion-radio>
|
||||
|
||||
<ion-radio value="granny">
|
||||
Granny Smith
|
||||
</ion-radio>
|
||||
|
||||
<ion-radio value="gala">
|
||||
Gala
|
||||
</ion-radio>
|
||||
|
||||
<ion-radio value="fuji">
|
||||
Fuji
|
||||
</ion-radio>
|
||||
|
||||
<ion-radio-group ng-control-group="fruitGroup1">
|
||||
<ion-radio><label>APPLE</label><input #apple type="radio" ng-control="appleCtrl"></ion-radio>
|
||||
<ion-radio><label>BANANA</label><input #banana type="radio" ng-control="bananaCtrl"></ion-radio>
|
||||
</ion-radio-group>
|
||||
|
||||
appleCtrl.dirty: {{fruitsForm.controls.fruitGroup1.controls.appleCtrl.dirty}}<br>
|
||||
|
||||
<div class="list-header">
|
||||
Fruit Group 2
|
||||
</div>
|
||||
<ion-radio-group ng-control-group="fruitGroup2">
|
||||
<ion-radio><label>GRAPE</label><input #grape name="test" type="radio" ng-control="grapeCtrl"></ion-radio>
|
||||
<ion-radio><label>CHERRY</label><input #cherry name="test" type="radio" ng-control="cherryCtrl"></ion-radio>
|
||||
</ion-radio-group>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
Current apple: <b>{{form.controls.preferredApple.value}}</b>
|
||||
</ion-content>
|
||||
</ion-view>
|
||||
|
@ -57,8 +57,10 @@ export const IonicDirectives = [
|
||||
forwardRef(() => SegmentButton),
|
||||
forwardRef(() => SegmentControlValueAccessor),
|
||||
forwardRef(() => Checkbox),
|
||||
//Checkbox, Switch
|
||||
//RadioGroup, RadioButton, SearchBar,
|
||||
forwardRef(() => RadioGroup),
|
||||
forwardRef(() => RadioButton),
|
||||
//Switch
|
||||
//SearchBar,
|
||||
|
||||
// Input
|
||||
forwardRef(() => Input),
|
||||
|
Reference in New Issue
Block a user