checkbox and radio

This commit is contained in:
Tim Lancina
2015-07-17 15:41:01 -05:00
parent 2ed18de699
commit cb5fa23a80
7 changed files with 157 additions and 158 deletions

View File

@ -17,8 +17,9 @@ import {Icon} from '../icon/icon';
@IonicComponent({ @IonicComponent({
selector: 'ion-checkbox', selector: 'ion-checkbox',
host: { host: {
'class': 'item', '[class.item]': 'item',
'[attr.aria-checked]': 'checked' '[attr.aria-checked]': 'input.checked',
'(^click)': 'onClick($event)'
}, },
defaultProperties: { defaultProperties: {
'iconOff': 'ion-ios-circle-outline', 'iconOff': 'ion-ios-circle-outline',
@ -27,7 +28,7 @@ import {Icon} from '../icon/icon';
}) })
@IonicView({ @IonicView({
template: 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]="iconOff" class="checkbox-off"></icon>' +
'<icon [name]="iconOn" class="checkbox-on"></icon>' + '<icon [name]="iconOn" class="checkbox-on"></icon>' +
'</div>' + '</div>' +
@ -36,42 +37,31 @@ import {Icon} from '../icon/icon';
'</div>' '</div>'
}) })
export class Checkbox extends IonInputItem { export class Checkbox extends IonInputItem {
_checkbox: CheckboxInput;
constructor( constructor(
elementRef: ElementRef, elementRef: ElementRef,
config: IonicConfig config: IonicConfig
) { ) {
super(elementRef, config); super(elementRef, config);
this.item = true;
} }
onAllChangesDone() { onClick(ev) {
// Enforce that this directive actually contains a checkbox // toggling with spacebar fires mouse event
if (this._checkbox == null) { if (ev.target.tagName === "INPUT") return;
throw 'No <input type="checkbox"> found inside of <ion-checkbox>';
}
}
registerInput(checkboxDir) { this.input.checked = !this.input.checked;
if (this._checkbox != null) {
throw 'Only one <input type="checkbox"> is allowed per <ion-checkbox>'
}
this._checkbox = checkboxDir.elementRef.nativeElement;
this._checkboxDir = checkboxDir;
}
onClick(e) { //TODO trigger change/mouse event on the actual input to trigger
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
// form updates // form updates
// this._checkbox.dispatchEvent(e); // this._checkbox.dispatchEvent(e);
//this._checkboxDir.control.valueAccessor.writeValue(val); //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;
}
} }

View File

@ -1,7 +1,4 @@
import {Component, View} from 'angular2/angular2'; import {App} from 'ionic/ionic';
import {Query, QueryList, NgFor} from 'angular2/angular2';
// import {FormBuilder, Validators, NgFormControl, Control, NgControlName, NgControlGroup, ControlContainer} from 'angular2/forms';
import { import {
Control, Control,
ControlGroup, ControlGroup,
@ -14,8 +11,6 @@ import {
NgFormModel, NgFormModel,
FormBuilder FormBuilder
} from 'angular2/forms'; } from 'angular2/forms';
import {App, Checkbox, Content, List} from 'ionic/ionic';
//import {IONIC_DIRECTIVES} from 'ionic/ionic'
@App({ @App({
templateUrl: 'main.html' templateUrl: 'main.html'

View File

@ -9,12 +9,18 @@ import {RadioButton} from '../radio/radio';
@Directive({ @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 { export class TapInput extends IonInput {
constructor( constructor(
@Optional() @Parent() checkboxContainer: Checkbox, //TODO have this be either Checkbox or Radio @Optional() @Parent() checkboxContainer: Checkbox,
@Optional() @Parent() radioContainer : RadioButton, @Optional() @Parent() radioContainer: RadioButton,
@Optional() @Ancestor() scrollView: Content, @Optional() @Ancestor() scrollView: Content,
@Attribute('type') type: string, @Attribute('type') type: string,
elementRef: ElementRef, elementRef: ElementRef,
@ -35,4 +41,8 @@ export class TapInput extends IonInput {
this.tabIndex = this.tabIndex || ''; this.tabIndex = this.tabIndex || '';
} }
onChangeEvent(ev) {
this.container && this.container.onChangeEvent(this);
}
} }

View File

@ -1,134 +1,105 @@
import {ElementRef, Ancestor} from 'angular2/angular2'; import {ElementRef, Ancestor} from 'angular2/angular2';
import {IonicDirective, IonicComponent, IonicView} from '../../config/annotations'; 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({ @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( constructor(
elementRef: ElementRef//, elementRef: ElementRef,
//cd:ControlDirective ionicConfig: IonicConfig
) { ) {
this.ele = elementRef.nativeElement super(elementRef, ionicConfig);
// this.config = RadioGroup.config.invoke(this) this.list = true;
// this.controlDirective = cd;
// cd.valueAccessor = this; //ControlDirective should inject CheckboxControlDirective
this.ele.classList.add('list');
this.buttons = [];
} }
/**
* 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) { register(radioButton) {
this.buttons.push(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 update(input) {
// first button added, select it for (let button of this.buttons) {
if(!this.value && this.buttons.length === 1) { button.input.checked = button.input.elementRef.nativeElement.checked;
setTimeout(() => {
// We need to defer so the control directive can initialize
this.selected(radioButton);
})
} }
} }
/**
* 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({ @IonicComponent({
selector: 'ion-radio', selector: 'ion-radio',
properties: [
'value'
],
host: { 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({ @IonicView({
template: ` template:
<div class="item-content"> '<div class="item-media media-radio">' +
<div class="item-title"> '<icon [name]="iconOff" class="radio-off"></icon>' +
<content></content> '<icon [name]="iconOn" class="radio-on"></icon>' +
</div> '</div>' +
<div class="item-media media-radio"> '<div class="item-content">' +
<icon class="radio-off"></icon> '<content></content>' +
<icon class="ion-ios-checkmark-empty radio-on"></icon> '</div>'
</div>
</div>
`
}) })
export class RadioButton { export class RadioButton extends IonInputItem {
constructor( constructor(
@Ancestor() group: RadioGroup, @Ancestor() group: RadioGroup,
elementRef: ElementRef elementRef: ElementRef,
config: IonicConfig
) { ) {
this.ele = elementRef.ele; super(elementRef, config);
this.item = true;
this.ele.classList.add('item')
this.ele.setAttribute('aria-checked', true)
this.group = group; this.group = group;
group.register(this);
} }
setActive(isActive) { registerInput(input) {
// TODO: No ele this.input = input;
if(isActive) { this.group.register(this);
this.ele.classList.add('active');
this.ele.setAttribute('aria-checked', true)
} else {
this.ele.classList.remove('active');
this.ele.setAttribute('aria-checked', false)
}
} }
buttonClicked(event) { onClick(ev) {
this.group.selected(this, event); // switching between radio buttons with arrow keys fires a MouseEvent
event.preventDefault(); 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);
}
} }

View File

@ -1,4 +1,16 @@
import {App} from 'ionic/ionic'; import {App} from 'ionic/ionic';
import {
Control,
ControlGroup,
NgForm,
formDirectives,
Validators,
NgControl,
ControlValueAccessor,
NgControlName,
NgFormModel,
FormBuilder
} from 'angular2/forms';
@App({ @App({
@ -6,9 +18,33 @@ import {App} from 'ionic/ionic';
}) })
class IonicApp { class IonicApp {
constructor() { constructor() {
// var fb = new FormBuilder(); this.fruitsGroup1 = new ControlGroup({
// this.form = fb.group({ "appleCtrl": new Control("", isChecked),
// preferredApple: ['mac', Validators.required], "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();
} }
} }

View File

@ -3,33 +3,28 @@
<ion-content> <ion-content>
<form (^submit)="doSubmit($event)" [control-group]="form"> <form (^submit)="doSubmit($event)" [ng-form-model]="fruitsForm">
<div class="list-header"> <div class="list-header">
Radio Group Fruit Group 1
</div> </div>
<ion-radio-group control="preferredApple"> <ion-radio-group ng-control-group="fruitGroup1">
<ion-radio><label>APPLE</label><input #apple type="radio" ng-control="appleCtrl"></ion-radio>
<ion-radio value="mac"> <ion-radio><label>BANANA</label><input #banana type="radio" ng-control="bananaCtrl"></ion-radio>
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> </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> </form>
Current apple: <b>{{form.controls.preferredApple.value}}</b>
</ion-content> </ion-content>
</ion-view> </ion-view>

View File

@ -57,8 +57,10 @@ export const IonicDirectives = [
forwardRef(() => SegmentButton), forwardRef(() => SegmentButton),
forwardRef(() => SegmentControlValueAccessor), forwardRef(() => SegmentControlValueAccessor),
forwardRef(() => Checkbox), forwardRef(() => Checkbox),
//Checkbox, Switch forwardRef(() => RadioGroup),
//RadioGroup, RadioButton, SearchBar, forwardRef(() => RadioButton),
//Switch
//SearchBar,
// Input // Input
forwardRef(() => Input), forwardRef(() => Input),