diff --git a/ionic/components/radio/radio-button.ts b/ionic/components/radio/radio-button.ts index 93724af3ee..f45799de15 100644 --- a/ionic/components/radio/radio-button.ts +++ b/ionic/components/radio/radio-button.ts @@ -1,7 +1,7 @@ import {Component, Optional, Input, Output, HostListener, EventEmitter} from 'angular2/core'; import {Form} from '../../util/form'; -import {isTrueProperty, isDefined} from '../../util/util'; +import {isTrueProperty, isDefined, isBlank} from '../../util/util'; import {Item} from '../item/item'; import {ListHeader} from '../list/list'; import {RadioGroup} from './radio-group'; @@ -50,10 +50,10 @@ export class RadioButton { private _checked: any = false; private _disabled: any = false; private _labelId: string; + private _value = null; id: string; - @Input() value: string = ''; @Output() select: EventEmitter = new EventEmitter(); constructor( @@ -63,21 +63,28 @@ export class RadioButton { ) { _form.register(this); + if (_group) { + // register with the radiogroup + this.id = 'rb-' + _group.register(this); + } + if (_item) { + // register the input inside of the item + // reset to the item's id instead of the radiogroup id this.id = 'rb-' + _item.registerInput('radio'); this._labelId = 'lbl-' + _item.id; this._item.setCssClass('item-radio', true); } - - console.log(this.value); - - if (_group) { - _group.register(this); - } } - check() { - this.checked = true; + @Input() + get value() { + // if the value is not defined then use it's unique id + return isBlank(this._value) ? this.id : this._value; + } + + set value(val) { + this._value = val; } @Input() @@ -85,11 +92,28 @@ export class RadioButton { return this._checked; } - set checked(val) { + set checked(isChecked) { if (!this._disabled) { - this._checked = isTrueProperty(val); - this.select.emit(this); - this._item && this._item.setCssClass('item-radio-checked', this._checked); + // only check/uncheck if it's not disabled + + // emit the select event for the radiogroup to catch + this._checked = isTrueProperty(isChecked); + this.select.emit(this.value); + + // if it's a stand-alone radiobutton nothing else happens + // if it was within a radiogroup then updateAsChecked will + // get called again + this.updateAsChecked(this._checked); + } + } + + /** + * @private + */ + updateAsChecked(val: boolean) { + this._checked = val; + if (this._item) { + this._item.setCssClass('item-radio-checked', val); } } @@ -103,14 +127,6 @@ export class RadioButton { this._item && this._item.setCssClass('item-radio-disabled', this._disabled); } - /** - * @private - */ - setChecked(val: boolean) { - this._checked = isTrueProperty(val); - this._item && this._item.setCssClass('item-radio-checked', val); - } - /** * @private */ @@ -119,7 +135,7 @@ export class RadioButton { console.debug('radio, select', this.id); ev.preventDefault(); ev.stopPropagation(); - this.check(); + this.checked = true; } /** diff --git a/ionic/components/radio/radio-group.ts b/ionic/components/radio/radio-group.ts index a358a54702..a475b347f0 100644 --- a/ionic/components/radio/radio-group.ts +++ b/ionic/components/radio/radio-group.ts @@ -1,4 +1,4 @@ -import {Directive, ElementRef, Renderer, Optional, Input, Output, ContentChild, EventEmitter} from 'angular2/core'; +import {Directive, ElementRef, Renderer, Optional, Input, Output, HostListener, ContentChild, EventEmitter} from 'angular2/core'; import {NgControl} from 'angular2/common'; import {RadioButton} from './radio-button'; @@ -60,6 +60,8 @@ import {isDefined} from '../../util/util'; }) export class RadioGroup { private _buttons: Array = []; + private _ids: number = -1; + private _init: boolean = false; id; value; @@ -84,43 +86,67 @@ export class RadioGroup { * the checked value. * https://github.com/angular/angular/blob/master/modules/angular2/src/forms/directives/shared.ts#L34 */ - writeValue(value) { - this.value = isDefined(value) ? value : ''; - this._buttons.forEach(button => { - let isChecked = button.checked; - button.setChecked(isChecked); - if (isChecked) { - this._renderer.setElementAttribute(this._elementRef.nativeElement, 'aria-activedescendant', button.id); + writeValue(val) { + if (val !== null) { + let oldVal = this.value; + + // set the radiogroup's value + this.value = val || ''; + + this.updateValue(); + + // only emit change when it...changed + if (this.value !== oldVal && this._init) { + this.change.emit(this.value); } - }); - } - register(button: RadioButton) { - this._buttons.push(button); - - button.select.subscribe(() => { - this.writeValue(button.value); - this.onChange(button.value); - this.change.emit(this); - }); + this._init = true; + } } ngAfterContentInit() { - this._buttons.forEach(button => { - - if (isDefined(this.value)) { - let isChecked = (button.value === this.value) || button.checked; - button.setChecked(isChecked); - if (isChecked) { - this.writeValue(button.value); - //this.onChange(button.value); - this._renderer.setElementAttribute(this._elementRef.nativeElement, 'aria-activedescendant', button.id); - } - } - + // in a setTimeout to prevent + // Expression '_checked in RadioButton@0:24' has changed after + // it was checked. Previous value: 'true'. Current value: 'false' + // should be available in future versions of ng2 + setTimeout(() => { + this.updateValue(); }); } + private updateValue() { + if (isDefined(this.value)) { + // loop through each of the radiobuttons + this._buttons.forEach(radioButton => { + + // check this radiobutton if its value is + // the same as the radiogroups value + let isChecked = (radioButton.value === this.value); + + radioButton.updateAsChecked(isChecked); + + if (isChecked) { + // if this button is checked, then set it as + // the radiogroup's active descendant + this._renderer.setElementAttribute(this._elementRef.nativeElement, 'aria-activedescendant', radioButton.id); + } + }); + } + } + + register(button: RadioButton): string { + this._buttons.push(button); + + // listen for radiobutton select events + button.select.subscribe(() => { + // this radiobutton has been selected + this.writeValue(button.value); + this.onChange(button.value); + }); + + return this.id + '-' + (++this._ids); + } + @ContentChild(ListHeader) private set _header(header) { if (header) { @@ -134,16 +160,12 @@ export class RadioGroup { /** * @private */ - onChange(val) { - // TODO: figure the whys and the becauses - } + onChange(val) {} /** * @private */ - onTouched(val) { - // TODO: figure the whys and the becauses - } + onTouched(val) {} /** * @private diff --git a/ionic/components/radio/radio.ios.scss b/ionic/components/radio/radio.ios.scss index 2854b80f8d..3a252316d8 100644 --- a/ionic/components/radio/radio.ios.scss +++ b/ionic/components/radio/radio.ios.scss @@ -13,6 +13,12 @@ $radio-ios-icon-border-style: solid !default; $radio-ios-disabled-opacity: 0.4 !default; +ion-radio { + position: relative; + display: inline-block; +} + + // iOS Radio Circle: Unchecked // ----------------------------------------- @@ -56,6 +62,7 @@ $radio-ios-disabled-opacity: 0.4 !default; // ----------------------------------------- .item ion-radio { + position: static; display: block; margin: $item-ios-padding-media-top ($item-ios-padding-right / 2) $item-ios-padding-media-bottom ($item-ios-padding-left / 2); } diff --git a/ionic/components/radio/radio.md.scss b/ionic/components/radio/radio.md.scss index ff1d51139f..e2f0c42fff 100644 --- a/ionic/components/radio/radio.md.scss +++ b/ionic/components/radio/radio.md.scss @@ -18,6 +18,12 @@ $radio-md-transition-easing: cubic-bezier(.4, 0, .2, 1) !default; $radio-md-disabled-opacity: 0.4 !default; +ion-radio { + position: relative; + display: inline-block; +} + + // Material Design Radio Outer Circle: Unchecked // ----------------------------------------- @@ -82,6 +88,7 @@ $radio-md-disabled-opacity: 0.4 !default; // ----------------------------------------- .item ion-radio { + position: static; display: block; margin: $item-md-padding-media-top ($item-md-padding-right / 2) $item-md-padding-media-bottom 0; } diff --git a/ionic/components/radio/test/basic/index.ts b/ionic/components/radio/test/basic/index.ts index 566d380a24..87116f9695 100644 --- a/ionic/components/radio/test/basic/index.ts +++ b/ionic/components/radio/test/basic/index.ts @@ -32,7 +32,7 @@ class E2EApp { this.currencies = ['USD', 'EUR']; this.selectedCurrency = 'EUR'; - this.relationship = 'friends'; + this.relationship = 'enemies'; } @@ -52,4 +52,20 @@ class E2EApp { console.log('Submitting form', this.fruitsForm.value); event.preventDefault(); } + + petChange(ev) { + console.log('petChange', ev); + } + + dogSelect(ev) { + console.log('dogSelect', ev); + } + + catSelect(ev) { + console.log('catSelect', ev); + } + + turtleSelect(ev) { + console.log('turtleSelect', ev); + } } diff --git a/ionic/components/radio/test/basic/main.html b/ionic/components/radio/test/basic/main.html index 16f3fb2dcd..5000028ecb 100644 --- a/ionic/components/radio/test/basic/main.html +++ b/ionic/components/radio/test/basic/main.html @@ -1,5 +1,7 @@ -Radio Group + + Radio Group + @@ -78,20 +80,20 @@ relationship: {{relationship}} - - - Dogs - - - - Cats - - - - Turtles - - - +
+

+ + Dogs +

+

+ + Cats +

+

+ + Turtles +

+
pet: {{pet}}