From cb5fa23a8043f75aa259fad2ecdf4991a803009f Mon Sep 17 00:00:00 2001 From: Tim Lancina Date: Fri, 17 Jul 2015 15:41:01 -0500 Subject: [PATCH] checkbox and radio --- ionic/components/checkbox/checkbox.ts | 40 ++--- ionic/components/checkbox/test/basic/index.ts | 7 +- ionic/components/form/tap-input.ts | 16 +- ionic/components/radio/radio.ts | 165 ++++++++---------- ionic/components/radio/test/basic/index.ts | 44 ++++- ionic/components/radio/test/basic/main.html | 37 ++-- ionic/config/annotations.ts | 6 +- 7 files changed, 157 insertions(+), 158 deletions(-) diff --git a/ionic/components/checkbox/checkbox.ts b/ionic/components/checkbox/checkbox.ts index e54fd584aa..0a7745c62f 100644 --- a/ionic/components/checkbox/checkbox.ts +++ b/ionic/components/checkbox/checkbox.ts @@ -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: - '
' + + '
' + '' + '' + '
' + @@ -36,42 +37,31 @@ import {Icon} from '../icon/icon'; '
' }) 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 found inside of '; - } - } + onClick(ev) { + // toggling with spacebar fires mouse event + if (ev.target.tagName === "INPUT") return; - registerInput(checkboxDir) { - if (this._checkbox != null) { - throw 'Only one is allowed per ' - } - 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; + } } diff --git a/ionic/components/checkbox/test/basic/index.ts b/ionic/components/checkbox/test/basic/index.ts index cce5ee7155..8648475d06 100644 --- a/ionic/components/checkbox/test/basic/index.ts +++ b/ionic/components/checkbox/test/basic/index.ts @@ -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' diff --git a/ionic/components/form/tap-input.ts b/ionic/components/form/tap-input.ts index b8712c35f4..e040237f4b 100644 --- a/ionic/components/form/tap-input.ts +++ b/ionic/components/form/tap-input.ts @@ -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); + } + } diff --git a/ionic/components/radio/radio.ts b/ionic/components/radio/radio.ts index 08714bc98e..28d352231c 100644 --- a/ionic/components/radio/radio.ts +++ b/ionic/components/radio/radio.ts @@ -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 = []; + 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: ` -
-
- -
-
- - -
-
- ` + template: + '
' + + '' + + '' + + '
' + + '
' + + '' + + '
' }) -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); + } } diff --git a/ionic/components/radio/test/basic/index.ts b/ionic/components/radio/test/basic/index.ts index 9f19697df8..a411aece0d 100644 --- a/ionic/components/radio/test/basic/index.ts +++ b/ionic/components/radio/test/basic/index.ts @@ -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(); } } diff --git a/ionic/components/radio/test/basic/main.html b/ionic/components/radio/test/basic/main.html index 1376fcb08b..f6c4df4efb 100644 --- a/ionic/components/radio/test/basic/main.html +++ b/ionic/components/radio/test/basic/main.html @@ -3,33 +3,28 @@ -
+
- Radio Group + Fruit Group 1
- - - - McIntosh - - - - Granny Smith - - - - Gala - - - - Fuji - - + + + + appleCtrl.dirty: {{fruitsForm.controls.fruitGroup1.controls.appleCtrl.dirty}}
+ +
+ Fruit Group 2 +
+ + + + + + - Current apple: {{form.controls.preferredApple.value}}
diff --git a/ionic/config/annotations.ts b/ionic/config/annotations.ts index 4721d42e06..038941fa5d 100644 --- a/ionic/config/annotations.ts +++ b/ionic/config/annotations.ts @@ -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),