Skip to content

Commit df06762

Browse files
committed
feat(checkbox): Added radiobutton support for reactive forms
Closes #168
1 parent 1284a92 commit df06762

File tree

8 files changed

+103
-27
lines changed

8 files changed

+103
-27
lines changed

demo/src/app/pages/modules/checkbox/checkbox.page.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,16 @@ const exampleRadioButtonTemplate = `
2828
<div class="grouped fields">
2929
<label>Radio Button Example</label>
3030
<div class="field">
31-
<sui-radio-button value="hello" [(ngModel)]="eRadio">Value: "hello"</sui-radio-button>
31+
<sui-radio-button name="example" value="hello" [(ngModel)]="eRadio">Value: "hello"</sui-radio-button>
3232
</div>
3333
<div class="field">
34-
<sui-radio-button value="world" [(ngModel)]="eRadio">Value: "world"</sui-radio-button>
34+
<sui-radio-button name="example" value="world" [(ngModel)]="eRadio">Value: "world"</sui-radio-button>
3535
</div>
3636
<div class="field">
37-
<sui-radio-button value="example" [(ngModel)]="eRadio">Value: "example"</sui-radio-button>
37+
<sui-radio-button name="example" value="example" [(ngModel)]="eRadio">Value: "example"</sui-radio-button>
3838
</div>
3939
<div class="field">
40-
<sui-radio-button [value]="{ example: 'object' }" [(ngModel)]="eRadio">
40+
<sui-radio-button name="example" [value]="{ example: 'object' }" [(ngModel)]="eRadio">
4141
Value: {{ '{' }} example: "object" }
4242
</sui-radio-button>
4343
</div>
@@ -62,12 +62,12 @@ const exampleStyledTemplate = `
6262
<div class="grouped fields">
6363
<label>Radio Button Style Examples</label>
6464
<div class="field">
65-
<sui-radio-button class="slider" value="a" [(ngModel)]="eStyledRadio">
65+
<sui-radio-button class="slider" name="styled" value="a" [(ngModel)]="eStyledRadio">
6666
Slider radio button
6767
</sui-radio-button>
6868
</div>
6969
<div class="field">
70-
<sui-radio-button class="toggle" value="b" [(ngModel)]="eStyledRadio">
70+
<sui-radio-button class="toggle" name="styled" value="b" [(ngModel)]="eStyledRadio">
7171
Toggle radio button
7272
</sui-radio-button>
7373
</div>
@@ -126,18 +126,19 @@ export class CheckboxPage {
126126
{
127127
name: "name",
128128
type: "string",
129-
description: "Sets the name on the internal <code>&lt;input&gt;</code> component."
129+
description: "Sets the name on the internal <code>&lt;input&gt;</code> component.",
130+
required: true
130131
},
131132
{
132133
name: "value",
133134
type: "T",
134-
description: "Sets the value that selecting this radio button returns. Supports binding any object."
135+
description: "Sets the value that selecting this radio button returns. Supports binding any object.",
136+
required: true
135137
},
136138
{
137139
name: "ngModel",
138140
type: "T",
139-
description: "Bind the radio button value to the value of the provided variable.",
140-
required: true
141+
description: "Bind the radio button value to the value of the provided variable."
141142
},
142143
{
143144
name: "isDisabled",

src/collections/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
export * from "./message/message.module";
2-
export * from "./pagination/pagination.module";
1+
export * from "./message";
2+
export * from "./pagination";

src/misc/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export * from "./util/util.module";
1+
export * from "./util";

src/misc/util/helpers/util.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ export const Util = {
4242
}
4343
return groups;
4444
},
45+
groupBy<T>(items:T[], field:keyof T):{ [name:string]:T[] } {
46+
return items.reduce<{ [name:string]:T[] }>(
47+
(groups, i) => {
48+
const fieldValue = i[field].toString();
49+
groups[fieldValue] = groups[fieldValue] || [];
50+
groups[fieldValue].push(i);
51+
return groups;
52+
},
53+
Object());
54+
},
4555
flatten<T>(items:T[][]):T[] {
4656
return items.reduce((is, i) => is.concat(i), []);
4757
}

src/modules/checkbox/checkbox.module.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { NgModule } from "@angular/core";
22
import { CommonModule } from "@angular/common";
33
import { FormsModule } from "@angular/forms";
44
import { SuiCheckbox, SuiCheckboxValueAccessor } from "./components/checkbox";
5-
import { SuiRadioButton, SuiRadioButtonValueAccessor } from "./components/radiobutton";
5+
import { SuiRadio, SuiRadioValueAccessor } from "./components/radio";
6+
import { SuiRadioManager } from "./directives/radio-manager";
67

78
@NgModule({
89
imports: [
@@ -12,14 +13,16 @@ import { SuiRadioButton, SuiRadioButtonValueAccessor } from "./components/radiob
1213
declarations: [
1314
SuiCheckbox,
1415
SuiCheckboxValueAccessor,
15-
SuiRadioButton,
16-
SuiRadioButtonValueAccessor
16+
SuiRadio,
17+
SuiRadioValueAccessor,
18+
SuiRadioManager
1719
],
1820
exports: [
1921
SuiCheckbox,
2022
SuiCheckboxValueAccessor,
21-
SuiRadioButton,
22-
SuiRadioButtonValueAccessor
23+
SuiRadio,
24+
SuiRadioValueAccessor,
25+
SuiRadioManager
2326
]
2427
})
2528
export class SuiCheckboxModule {}

src/modules/checkbox/components/radiobutton.ts renamed to src/modules/checkbox/components/radio.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import {
22
Component, Directive, Input, Output, HostListener, HostBinding,
3-
EventEmitter, ViewChild, ElementRef
3+
EventEmitter, ViewChild, ElementRef, ContentChildren, AfterContentInit, QueryList
44
} from "@angular/core";
5-
import { ICustomValueAccessorHost, customValueAccessorFactory, CustomValueAccessor } from "../../../misc/util";
5+
import {
6+
ICustomValueAccessorHost, customValueAccessorFactory, CustomValueAccessor,
7+
Util
8+
} from "../../../misc/util";
9+
import { Subscription } from "rxjs/Subscription";
610

711
@Component({
8-
selector: "sui-radio-button[ngModel]",
9-
exportAs: "suiRadioButton",
12+
selector: "sui-radio-button",
1013
template: `
1114
<input class="hidden"
1215
type="checkbox"
@@ -21,7 +24,7 @@ import { ICustomValueAccessorHost, customValueAccessorFactory, CustomValueAccess
2124
</label>
2225
`
2326
})
24-
export class SuiRadioButton<T> implements ICustomValueAccessorHost<T> {
27+
export class SuiRadio<T> implements ICustomValueAccessorHost<T> {
2528
@HostBinding("class.ui")
2629
@HostBinding("class.radio")
2730
@HostBinding("class.checkbox")
@@ -113,10 +116,10 @@ export class SuiRadioButton<T> implements ICustomValueAccessorHost<T> {
113116
"(currentValueChange)": "onChange($event)",
114117
"(touched)": "onTouched()"
115118
},
116-
providers: [customValueAccessorFactory(SuiRadioButtonValueAccessor)]
119+
providers: [customValueAccessorFactory(SuiRadioValueAccessor)]
117120
})
118-
export class SuiRadioButtonValueAccessor<T> extends CustomValueAccessor<T, SuiRadioButton<T>> {
119-
constructor(host:SuiRadioButton<T>) {
121+
export class SuiRadioValueAccessor<T> extends CustomValueAccessor<T, SuiRadio<T>> {
122+
constructor(host:SuiRadio<T>) {
120123
super(host);
121124
}
122125
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { Directive, AfterContentInit, ContentChildren, QueryList, ElementRef } from "@angular/core";
2+
import { SuiRadio } from "../components/radio";
3+
import { Subscription } from "rxjs/Subscription";
4+
import { Util } from "../../../misc/util";
5+
6+
@Directive({
7+
selector: "form:not([ngForm]):not([[ngForm]]),ngForm,[ngForm]"
8+
})
9+
export class SuiRadioManager<T> implements AfterContentInit {
10+
11+
public isNested:boolean;
12+
13+
@ContentChildren(SuiRadioManager, { descendants: true })
14+
private _subManagers:QueryList<SuiRadioManager<T>>;
15+
16+
@ContentChildren(SuiRadio, { descendants: true })
17+
private _renderedRadios:QueryList<SuiRadio<T>>;
18+
19+
private _radioSubs:Subscription[];
20+
21+
constructor(public element:ElementRef) {
22+
this.isNested = false;
23+
this._radioSubs = [];
24+
}
25+
26+
public ngAfterContentInit():void {
27+
this.updateNesting();
28+
this._subManagers.changes.subscribe(() => this.updateNesting());
29+
30+
this.updateRadios();
31+
this._renderedRadios.changes.subscribe(() => this.updateRadios());
32+
}
33+
34+
private updateNesting():void {
35+
this._subManagers
36+
.filter(m => m !== this)
37+
.forEach(m => m.isNested = true);
38+
}
39+
40+
private updateRadios():void {
41+
this._radioSubs.forEach(s => s.unsubscribe());
42+
this._radioSubs = [];
43+
44+
const groups = Util.Array.groupBy(this._renderedRadios.toArray(), "name");
45+
Object
46+
.keys(groups)
47+
.map(k => groups[k])
48+
.forEach(g => g
49+
.forEach(r => this._radioSubs
50+
.push(r.onCurrentValueChange
51+
.subscribe((v:T) => {
52+
if (!this.isNested) {
53+
g.forEach(radio => radio.writeValue(v));
54+
}
55+
}))));
56+
}
57+
}

src/modules/checkbox/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export * from "./components/checkbox";
2-
export * from "./components/radiobutton";
2+
export * from "./components/radio";
3+
4+
export * from "./directives/radio-manager";
35

46
export * from "./checkbox.module";

0 commit comments

Comments
 (0)