mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(radio): add required attribute to radio-group (#5751)
* feat(radio): add required attribute to radio-group * chore(radio): add sample using required attribute in demo-app * feat(radio): add required attribute to radio-button * refactor(radio): change let to const when possible * style(radio): remove trailing white-spaces * docs: revert radio.md
This commit is contained in:
committed by
Andrew Seguin
parent
36f708c8dd
commit
f06fe11103
@@ -21,10 +21,20 @@
|
||||
Disable buttons
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<span>isRequired: {{isRequired}}</span>
|
||||
<button md-raised-button (click)="isRequired=!isRequired" class="demo-button">
|
||||
Require buttons
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<span><md-checkbox [(ngModel)]="isAlignEnd">Align end</md-checkbox></span>
|
||||
</div>
|
||||
<md-radio-group name="my_options" [disabled]="isDisabled" [align]="isAlignEnd ? 'end' : 'start'">
|
||||
<md-radio-group
|
||||
name="my_options"
|
||||
[disabled]="isDisabled"
|
||||
[required]="isRequired"
|
||||
[align]="isAlignEnd ? 'end' : 'start'">
|
||||
<md-radio-button value="option_1">Option 1</md-radio-button>
|
||||
<md-radio-button value="option_2">Option 2</md-radio-button>
|
||||
<md-radio-button value="option_3">Option 3</md-radio-button>
|
||||
|
||||
@@ -8,8 +8,9 @@ import {Component} from '@angular/core';
|
||||
styleUrls: ['radio-demo.css'],
|
||||
})
|
||||
export class RadioDemo {
|
||||
isDisabled: boolean = false;
|
||||
isAlignEnd: boolean = false;
|
||||
isDisabled: boolean = false;
|
||||
isRequired: boolean = false;
|
||||
favoriteSeason: string = 'Autumn';
|
||||
seasonOptions = [
|
||||
'Winter',
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
[checked]="checked"
|
||||
[disabled]="disabled"
|
||||
[name]="name"
|
||||
[required]="required"
|
||||
[attr.aria-label]="ariaLabel"
|
||||
[attr.aria-labelledby]="ariaLabelledby"
|
||||
(change)="_onInputChange($event)"
|
||||
|
||||
+37
-18
@@ -62,7 +62,7 @@ describe('MdRadio', () => {
|
||||
|
||||
it('should set individual radio names based on the group name', () => {
|
||||
expect(groupInstance.name).toBeTruthy();
|
||||
for (let radio of radioInstances) {
|
||||
for (const radio of radioInstances) {
|
||||
expect(radio.name).toBe(groupInstance.name);
|
||||
}
|
||||
});
|
||||
@@ -92,14 +92,14 @@ describe('MdRadio', () => {
|
||||
testComponent.labelPos = 'before';
|
||||
fixture.detectChanges();
|
||||
|
||||
for (let radio of radioInstances) {
|
||||
for (const radio of radioInstances) {
|
||||
expect(radio.labelPosition).toBe('before');
|
||||
}
|
||||
|
||||
testComponent.labelPos = 'after';
|
||||
fixture.detectChanges();
|
||||
|
||||
for (let radio of radioInstances) {
|
||||
for (const radio of radioInstances) {
|
||||
expect(radio.labelPosition).toBe('after');
|
||||
}
|
||||
});
|
||||
@@ -108,11 +108,20 @@ describe('MdRadio', () => {
|
||||
testComponent.isGroupDisabled = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
for (let radio of radioInstances) {
|
||||
for (const radio of radioInstances) {
|
||||
expect(radio.disabled).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it('should set required to each radio button when the group is required', () => {
|
||||
testComponent.isGroupRequired = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
for (const radio of radioInstances) {
|
||||
expect(radio.required).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it('should update the group value when one of the radios changes', () => {
|
||||
expect(groupInstance.value).toBeFalsy();
|
||||
|
||||
@@ -155,7 +164,7 @@ describe('MdRadio', () => {
|
||||
it('should emit a change event from radio buttons', () => {
|
||||
expect(radioInstances[0].checked).toBe(false);
|
||||
|
||||
let spies = radioInstances
|
||||
const spies = radioInstances
|
||||
.map((radio, index) => jasmine.createSpy(`onChangeSpy ${index} for ${radio.name}`));
|
||||
|
||||
spies.forEach((spy, index) => radioInstances[index].change.subscribe(spy));
|
||||
@@ -178,7 +187,7 @@ describe('MdRadio', () => {
|
||||
programmatically`, () => {
|
||||
expect(groupInstance.value).toBeFalsy();
|
||||
|
||||
let changeSpy = jasmine.createSpy('radio-group change listener');
|
||||
const changeSpy = jasmine.createSpy('radio-group change listener');
|
||||
groupInstance.change.subscribe(changeSpy);
|
||||
|
||||
radioLabelElements[0].click();
|
||||
@@ -265,7 +274,7 @@ describe('MdRadio', () => {
|
||||
testComponent.disableRipple = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
for (let radioLabel of radioLabelElements) {
|
||||
for (const radioLabel of radioLabelElements) {
|
||||
dispatchFakeEvent(radioLabel, 'mousedown');
|
||||
dispatchFakeEvent(radioLabel, 'mouseup');
|
||||
|
||||
@@ -275,7 +284,7 @@ describe('MdRadio', () => {
|
||||
testComponent.disableRipple = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
for (let radioLabel of radioLabelElements) {
|
||||
for (const radioLabel of radioLabelElements) {
|
||||
dispatchFakeEvent(radioLabel, 'mousedown');
|
||||
dispatchFakeEvent(radioLabel, 'mouseup');
|
||||
|
||||
@@ -285,7 +294,7 @@ describe('MdRadio', () => {
|
||||
|
||||
it(`should update the group's selected radio to null when unchecking that radio
|
||||
programmatically`, () => {
|
||||
let changeSpy = jasmine.createSpy('radio-group change listener');
|
||||
const changeSpy = jasmine.createSpy('radio-group change listener');
|
||||
groupInstance.change.subscribe(changeSpy);
|
||||
radioInstances[0].checked = true;
|
||||
|
||||
@@ -305,7 +314,7 @@ describe('MdRadio', () => {
|
||||
});
|
||||
|
||||
it('should not fire a change event from the group when a radio checked state changes', () => {
|
||||
let changeSpy = jasmine.createSpy('radio-group change listener');
|
||||
const changeSpy = jasmine.createSpy('radio-group change listener');
|
||||
groupInstance.change.subscribe(changeSpy);
|
||||
radioInstances[0].checked = true;
|
||||
|
||||
@@ -324,7 +333,7 @@ describe('MdRadio', () => {
|
||||
});
|
||||
|
||||
it(`should update checked status if changed value to radio group's value`, () => {
|
||||
let changeSpy = jasmine.createSpy('radio-group change listener');
|
||||
const changeSpy = jasmine.createSpy('radio-group change listener');
|
||||
groupInstance.change.subscribe(changeSpy);
|
||||
groupInstance.value = 'apple';
|
||||
|
||||
@@ -403,25 +412,25 @@ describe('MdRadio', () => {
|
||||
|
||||
it('should set individual radio names based on the group name', () => {
|
||||
expect(groupInstance.name).toBeTruthy();
|
||||
for (let radio of radioInstances) {
|
||||
for (const radio of radioInstances) {
|
||||
expect(radio.name).toBe(groupInstance.name);
|
||||
}
|
||||
|
||||
groupInstance.name = 'new name';
|
||||
|
||||
for (let radio of radioInstances) {
|
||||
for (const radio of radioInstances) {
|
||||
expect(radio.name).toBe(groupInstance.name);
|
||||
}
|
||||
});
|
||||
|
||||
it('should check the corresponding radio button on group value change', () => {
|
||||
expect(groupInstance.value).toBeFalsy();
|
||||
for (let radio of radioInstances) {
|
||||
for (const radio of radioInstances) {
|
||||
expect(radio.checked).toBeFalsy();
|
||||
}
|
||||
|
||||
groupInstance.value = 'vanilla';
|
||||
for (let radio of radioInstances) {
|
||||
for (const radio of radioInstances) {
|
||||
expect(radio.checked).toBe(groupInstance.value === radio.value);
|
||||
}
|
||||
expect(groupInstance.selected!.value).toBe(groupInstance.value);
|
||||
@@ -539,12 +548,12 @@ describe('MdRadio', () => {
|
||||
.filter(debugEl => debugEl.componentInstance.name == 'fruit')
|
||||
.map(debugEl => debugEl.componentInstance);
|
||||
|
||||
let fruitRadioNativeElements = radioDebugElements
|
||||
const fruitRadioNativeElements = radioDebugElements
|
||||
.filter(debugEl => debugEl.componentInstance.name == 'fruit')
|
||||
.map(debugEl => debugEl.nativeElement);
|
||||
|
||||
fruitRadioNativeInputs = [];
|
||||
for (let element of fruitRadioNativeElements) {
|
||||
for (const element of fruitRadioNativeElements) {
|
||||
fruitRadioNativeInputs.push(<HTMLElement> element.querySelector('input'));
|
||||
}
|
||||
});
|
||||
@@ -579,6 +588,14 @@ describe('MdRadio', () => {
|
||||
expect(weatherRadioInstances[2].checked).toBe(true);
|
||||
});
|
||||
|
||||
it('should add required attribute to the underlying input element if defined', () => {
|
||||
const radioInstance = seasonRadioInstances[0];
|
||||
radioInstance.required = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(radioInstance.required).toBe(true);
|
||||
});
|
||||
|
||||
it('should add aria-label attribute to the underlying input element if defined', () => {
|
||||
expect(fruitRadioNativeInputs[0].getAttribute('aria-label')).toBe('Banana');
|
||||
});
|
||||
@@ -630,6 +647,7 @@ describe('MdRadio', () => {
|
||||
template: `
|
||||
<md-radio-group [disabled]="isGroupDisabled"
|
||||
[labelPosition]="labelPos"
|
||||
[required]="isGroupRequired"
|
||||
[value]="groupValue"
|
||||
name="test-name">
|
||||
<md-radio-button value="fire" [disableRipple]="disableRipple" [disabled]="isFirstDisabled"
|
||||
@@ -647,8 +665,9 @@ describe('MdRadio', () => {
|
||||
})
|
||||
class RadiosInsideRadioGroup {
|
||||
labelPos: 'before' | 'after';
|
||||
isGroupDisabled: boolean = false;
|
||||
isFirstDisabled: boolean = false;
|
||||
isGroupDisabled: boolean = false;
|
||||
isGroupRequired: boolean = false;
|
||||
groupValue: string | null = null;
|
||||
disableRipple: boolean = false;
|
||||
color: string | null;
|
||||
|
||||
+28
-5
@@ -105,6 +105,9 @@ export class MdRadioGroup extends _MdRadioGroupMixinBase
|
||||
/** Whether the radio group is disabled. */
|
||||
private _disabled: boolean = false;
|
||||
|
||||
/** Whether the radio group is required. */
|
||||
private _required: boolean = false;
|
||||
|
||||
/** The method to be called in order to update ngModel */
|
||||
_controlValueAccessorChangeFn: (value: any) => void = () => {};
|
||||
|
||||
@@ -189,12 +192,20 @@ export class MdRadioGroup extends _MdRadioGroupMixinBase
|
||||
|
||||
/** Whether the radio group is disabled */
|
||||
@Input()
|
||||
get disabled() { return this._disabled; }
|
||||
get disabled(): boolean { return this._disabled; }
|
||||
set disabled(value) {
|
||||
this._disabled = coerceBooleanProperty(value);
|
||||
this._markRadiosForCheck();
|
||||
}
|
||||
|
||||
/** Whether the radio group is required */
|
||||
@Input()
|
||||
get required(): boolean { return this._required; }
|
||||
set required(value: boolean) {
|
||||
this._required = coerceBooleanProperty(value);
|
||||
this._markRadiosForCheck();
|
||||
}
|
||||
|
||||
constructor(private _changeDetector: ChangeDetectorRef) {
|
||||
super();
|
||||
}
|
||||
@@ -231,7 +242,7 @@ export class MdRadioGroup extends _MdRadioGroupMixinBase
|
||||
/** Updates the `selected` radio button from the internal _value state. */
|
||||
private _updateSelectedRadioFromValue(): void {
|
||||
// If the value already matches the selected radio, do nothing.
|
||||
let isAlreadySelected = this._selected != null && this._selected.value == this._value;
|
||||
const isAlreadySelected = this._selected != null && this._selected.value == this._value;
|
||||
|
||||
if (this._radios != null && !isAlreadySelected) {
|
||||
this._selected = null;
|
||||
@@ -247,7 +258,7 @@ export class MdRadioGroup extends _MdRadioGroupMixinBase
|
||||
/** Dispatch change event with current selection and group value. */
|
||||
_emitChangeEvent(): void {
|
||||
if (this._isInitialized) {
|
||||
let event = new MdRadioChange();
|
||||
const event = new MdRadioChange();
|
||||
event.source = this._selected;
|
||||
event.value = this._value;
|
||||
this.change.emit(event);
|
||||
@@ -424,6 +435,15 @@ export class MdRadioButton extends _MdRadioButtonMixinBase
|
||||
this._disabled = coerceBooleanProperty(value);
|
||||
}
|
||||
|
||||
/** Whether the radio button is required. */
|
||||
@Input()
|
||||
get required(): boolean {
|
||||
return this._required || (this.radioGroup && this.radioGroup.required);
|
||||
}
|
||||
set required(value: boolean) {
|
||||
this._required = coerceBooleanProperty(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Event emitted when the checked state of this radio button changes.
|
||||
* Change events are only emitted when the value changes due to user interaction with
|
||||
@@ -443,6 +463,9 @@ export class MdRadioButton extends _MdRadioButtonMixinBase
|
||||
/** Whether this radio is disabled. */
|
||||
private _disabled: boolean;
|
||||
|
||||
/** Whether this radio is required. */
|
||||
private _required: boolean;
|
||||
|
||||
/** Value assigned to this radio.*/
|
||||
private _value: any = null;
|
||||
|
||||
@@ -516,7 +539,7 @@ export class MdRadioButton extends _MdRadioButtonMixinBase
|
||||
|
||||
/** Dispatch change event with current value. */
|
||||
private _emitChangeEvent(): void {
|
||||
let event = new MdRadioChange();
|
||||
const event = new MdRadioChange();
|
||||
event.source = this;
|
||||
event.value = this._value;
|
||||
this.change.emit(event);
|
||||
@@ -547,7 +570,7 @@ export class MdRadioButton extends _MdRadioButtonMixinBase
|
||||
// emit its event object to the `change` output.
|
||||
event.stopPropagation();
|
||||
|
||||
let groupValueChanged = this.radioGroup && this.value != this.radioGroup.value;
|
||||
const groupValueChanged = this.radioGroup && this.value != this.radioGroup.value;
|
||||
this.checked = true;
|
||||
this._emitChangeEvent();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user