mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
fix(radio): use value accessor provider
This commit is contained in:
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
*
|
||||
* <ion-item>
|
||||
* <ion-label>Duesenberg</ion-label>
|
||||
* <ion-radio value="duesenberg" checked="true"></ion-radio>
|
||||
* <ion-radio value="duesenberg"></ion-radio>
|
||||
* </ion-item>
|
||||
*
|
||||
* <ion-item>
|
||||
@@ -50,6 +53,7 @@ import {isDefined} from '../../util/util';
|
||||
*
|
||||
* </ion-list>
|
||||
* ```
|
||||
*
|
||||
* @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<RadioButton> = [];
|
||||
private _btns: Array<RadioButton> = [];
|
||||
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<RadioGroup> = 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() {}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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<string>;
|
||||
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) {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Apple</ion-label>
|
||||
<ion-radio value="apple" checked="true"></ion-radio>
|
||||
<ion-radio value="apple"></ion-radio>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
@@ -56,7 +56,7 @@
|
||||
</ion-list-header>
|
||||
<ion-item *ngFor="#currency of currencies">
|
||||
<ion-label>{{currency}}</ion-label>
|
||||
<ion-radio [checked]="currency==selectedCurrency" [value]="currency"></ion-radio>
|
||||
<ion-radio [value]="currency"></ion-radio>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</form>
|
||||
@@ -82,7 +82,7 @@
|
||||
|
||||
<div radio-group [(ngModel)]="pet" (change)="petChange($event)">
|
||||
<p>
|
||||
<ion-radio checked (select)="dogSelect($event)"></ion-radio>
|
||||
<ion-radio (select)="dogSelect($event)"></ion-radio>
|
||||
Dogs
|
||||
</p>
|
||||
<p>
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user