From b85d7aa2692936c2799b286f24a0493a735276de Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Sat, 13 Feb 2016 23:18:39 -0600 Subject: [PATCH] fix(radio): use value accessor provider --- ionic/components/checkbox/checkbox.ts | 2 +- ionic/components/radio/radio-button.ts | 35 ++-- ionic/components/radio/radio-group.ts | 180 ++++++++++---------- ionic/components/radio/test/basic/index.ts | 43 +++-- ionic/components/radio/test/basic/main.html | 6 +- ionic/components/select/select.ts | 2 +- ionic/components/toggle/toggle.ts | 2 +- 7 files changed, 134 insertions(+), 136 deletions(-) diff --git a/ionic/components/checkbox/checkbox.ts b/ionic/components/checkbox/checkbox.ts index a30059e276..7c0fbde8af 100644 --- a/ionic/components/checkbox/checkbox.ts +++ b/ionic/components/checkbox/checkbox.ts @@ -1,5 +1,5 @@ import {Component, Optional, Input, HostListener, Provider, forwardRef} from 'angular2/core'; -import {NgControl, NG_VALUE_ACCESSOR} from 'angular2/common'; +import {NG_VALUE_ACCESSOR} from 'angular2/common'; import {Form} from '../../util/form'; import {Item} from '../item/item'; diff --git a/ionic/components/radio/radio-button.ts b/ionic/components/radio/radio-button.ts index abf1659e1f..539b51b938 100644 --- a/ionic/components/radio/radio-button.ts +++ b/ionic/components/radio/radio-button.ts @@ -71,7 +71,7 @@ export class RadioButton { if (_group) { // register with the radiogroup - this.id = 'rb-' + _group.register(this); + this.id = 'rb-' + _group.add(this); } if (_item) { @@ -105,27 +105,10 @@ export class RadioButton { } set checked(isChecked) { - if (!this._disabled) { - // only check/uncheck if it's not disabled + this._checked = isTrueProperty(isChecked); - // 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); + this._item.setCssClass('item-radio-checked', this._checked); } } @@ -150,7 +133,18 @@ export class RadioButton { console.debug('radio, select', this.id); ev.preventDefault(); ev.stopPropagation(); + this.checked = true; + this.select.emit(this.value); + } + + /** + * @private + */ + ngOnInit() { + if (this._group && isDefined(this._group.value) && this._group.value === this.value) { + this.checked = true; + } } /** @@ -158,5 +152,6 @@ export class RadioButton { */ ngOnDestroy() { this._form.deregister(this); + this._group.remove(this); } } diff --git a/ionic/components/radio/radio-group.ts b/ionic/components/radio/radio-group.ts index 230fb521ac..64c7d2659b 100644 --- a/ionic/components/radio/radio-group.ts +++ b/ionic/components/radio/radio-group.ts @@ -1,19 +1,22 @@ -import {Directive, ElementRef, Renderer, Optional, Input, Output, HostListener, ContentChild, EventEmitter} from 'angular2/core'; -import {NgControl} from 'angular2/common'; +import {Directive, ElementRef, Renderer, Optional, Input, Output, Provider, forwardRef, HostListener, ContentChild, EventEmitter} from 'angular2/core'; +import {NG_VALUE_ACCESSOR} from 'angular2/common'; import {RadioButton} from './radio-button'; import {ListHeader} from '../list/list'; import {isDefined} from '../../util/util'; +const RADIO_VALUE_ACCESSOR = new Provider( + NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => RadioGroup), multi: true}); /** * @name RadioGroup * @description - * A radio group is a group of radio components, and its value comes - * from the selected radio button's value. Selecting a radio button - * in the group unselects all others in the group. + * A radio group is a group of radio button components, and its value + * comes from the checked radio button's value. Selecting a radio + * button in the group unchecks all others in the group. * - * See the [Angular 2 Docs](https://angular.io/docs/ts/latest/guide/forms.html) for more info on forms and input. + * See the [Angular 2 Docs](https://angular.io/docs/ts/latest/guide/forms.html) + * for more info on forms and inputs. * * @usage * ```html @@ -30,7 +33,7 @@ import {isDefined} from '../../util/util'; * * * Duesenberg - * + * * * * @@ -50,6 +53,7 @@ import {isDefined} from '../../util/util'; * * * ``` + * * @demo /docs/v2/demos/radio/ * @see {@link /docs/v2/components#radio Radio Component Docs} */ @@ -58,22 +62,24 @@ import {isDefined} from '../../util/util'; host: { '[attr.aria-activedescendant]': 'activeId', 'role': 'radiogroup' - } + }, + providers: [RADIO_VALUE_ACCESSOR] }) export class RadioGroup { - private _buttons: Array = []; + private _btns: Array = []; + private _fn: Function; private _ids: number = -1; private _init: boolean = false; /** * @private */ - id; + value: any; /** * @private */ - value; + id: number; /** * @output {any} expression to be evaluated when selection has been changed @@ -81,93 +87,112 @@ export class RadioGroup { @Output() change: EventEmitter = new EventEmitter(); constructor( - @Optional() ngControl: NgControl, private _renderer: Renderer, private _elementRef: ElementRef ) { this.id = ++radioGroupIds; - - if (ngControl) { - ngControl.valueAccessor = this; - } } /** * @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(val) { - if (val !== null) { - let oldVal = this.value; + writeValue(val: any) { + console.debug('radio group, writeValue', val); + this.value = val; - // set the radiogroup's value - this.value = isDefined(val) ? val : ''; - - this.updateValue(); - - // only emit change when it...changed - if (this.value !== oldVal && this._init) { - this.change.emit(this.value); - } - - this._init = true; + if (this._init) { + this._update(); + this.onTouched(); + this.change.emit(val); } + + this._init = true; } /** * @private */ ngAfterContentInit() { - // 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 - */ - 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); - } - }); + let activeButton = this._btns.find(b => b.checked); + if (activeButton) { + this._setActive(activeButton); } } /** * @private */ - register(button: RadioButton): string { - this._buttons.push(button); + registerOnChange(fn: Function): void { + this._fn = fn; + this.onChange = (val: any) => { + console.debug('radio group, onChange', val); + fn(val); + this.value = val; + this._update(); + this.onTouched(); + this.change.emit(val); + }; + } + + /** + * @private + */ + registerOnTouched(fn) { this.onTouched = fn; } + + /** + * @private + */ + private _update() { + // loop through each of the radiobuttons + this._btns.forEach(radioButton => { + + // check this radiobutton if its value is + // the same as the radiogroups value + radioButton.checked = (radioButton.value === this.value); + + if (radioButton.checked) { + // if this button is checked, then set it as + // the radiogroup's active descendant + this._setActive(radioButton); + } + }); + } + + private _setActive(radioButton: RadioButton) { + this._renderer.setElementAttribute(this._elementRef.nativeElement, 'aria-activedescendant', radioButton.id); + } + + /** + * @private + */ + add(button: RadioButton): string { + this._btns.push(button); // listen for radiobutton select events - button.select.subscribe(() => { + button.select.subscribe((val) => { // this radiobutton has been selected - this.writeValue(button.value); - this.onChange(button.value); + this.onChange(val); }); return this.id + '-' + (++this._ids); } + /** + * @private + */ + remove(button: RadioButton) { + let index = this._btns.indexOf(button); + if (index > -1) { + if (button.value === this.value) { + this.value = null; + } + this._btns.splice(index, 1); + } + } + + /** + * @private + */ @ContentChild(ListHeader) private set _header(header) { if (header) { @@ -181,29 +206,12 @@ export class RadioGroup { /** * @private */ - onChange(val) {} + onChange(_) {} /** * @private */ - onTouched(val) {} - - /** - * @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 the model (Control) as touched. - * @param {Function} fn onTouched event handler. - */ - registerOnTouched(fn) { this.onTouched = fn; } + onTouched() {} } diff --git a/ionic/components/radio/test/basic/index.ts b/ionic/components/radio/test/basic/index.ts index f17be3e155..8cf419fe03 100644 --- a/ionic/components/radio/test/basic/index.ts +++ b/ionic/components/radio/test/basic/index.ts @@ -1,40 +1,35 @@ import {App} from '../../../../../ionic/ionic'; -import { - Control, - ControlGroup, - NgForm, - Validators, - NgControl, - ControlValueAccessor, - NgControlName, - NgFormModel, - FormBuilder -} from 'angular2/common'; +import {Control, ControlGroup} from 'angular2/common'; @App({ templateUrl: 'main.html' }) class E2EApp { + fruits: Control; + fruitsForm: ControlGroup; + currenciesControl: Control; + currencyForm: ControlGroup; + currencies: Array; + items: Array<{description: string, value: any}>; + relationship: string; + pet: string; + constructor() { - this.fruits = new Control(""); + this.fruits = new Control('apple'); this.fruitsForm = new ControlGroup({ - "fruits": this.fruits - }); - - this.currenciesControl = new Control(""); - - this.currencyForm = new ControlGroup({ - "currenciesControl": this.currenciesControl + 'fruits': this.fruits }); this.currencies = ['USD', 'EUR']; - this.selectedCurrency = 'EUR'; + this.currenciesControl = new Control('EUR'); + this.currencyForm = new ControlGroup({ + 'currenciesControl': this.currenciesControl + }); this.relationship = 'enemies'; - this.items = [ { description: 'value undefined', value: undefined }, { description: 'value false string', value: 'false' }, @@ -44,15 +39,15 @@ class E2EApp { } setApple() { - this.fruits.updateValue("apple"); + this.fruits.updateValue('apple'); } setBanana() { - this.fruits.updateValue("banana"); + this.fruits.updateValue('banana'); } setCherry() { - this.fruits.updateValue("cherry"); + this.fruits.updateValue('cherry'); } doSubmit(event) { diff --git a/ionic/components/radio/test/basic/main.html b/ionic/components/radio/test/basic/main.html index 2fbfc97e49..773b533563 100644 --- a/ionic/components/radio/test/basic/main.html +++ b/ionic/components/radio/test/basic/main.html @@ -16,7 +16,7 @@ Apple - + @@ -56,7 +56,7 @@ {{currency}} - + @@ -82,7 +82,7 @@

- + Dogs

diff --git a/ionic/components/select/select.ts b/ionic/components/select/select.ts index c664952d17..b552f72ced 100644 --- a/ionic/components/select/select.ts +++ b/ionic/components/select/select.ts @@ -1,5 +1,5 @@ import {Component, Optional, ElementRef, Renderer, Input, Output, Provider, forwardRef, EventEmitter, HostListener, ContentChildren, QueryList} from 'angular2/core'; -import {NgControl, NG_VALUE_ACCESSOR} from 'angular2/common'; +import {NG_VALUE_ACCESSOR} from 'angular2/common'; import {Alert} from '../alert/alert'; import {Form} from '../../util/form'; diff --git a/ionic/components/toggle/toggle.ts b/ionic/components/toggle/toggle.ts index a50f42ef48..a7902964b1 100644 --- a/ionic/components/toggle/toggle.ts +++ b/ionic/components/toggle/toggle.ts @@ -1,5 +1,5 @@ import {Component, ElementRef, Renderer, Input, Optional, Provider, forwardRef} from 'angular2/core'; -import {NgControl, ControlValueAccessor, NG_VALUE_ACCESSOR} from 'angular2/common'; +import {ControlValueAccessor, NG_VALUE_ACCESSOR} from 'angular2/common'; import {Form} from '../../util/form'; import {isTrueProperty} from '../../util/util';