Skip to content

Commit a9d6fd9

Browse files
committed
feat(forms): implemented template-driven forms
1 parent 5c53cf6 commit a9d6fd9

19 files changed

+836
-350
lines changed
Lines changed: 28 additions & 279 deletions
Original file line numberDiff line numberDiff line change
@@ -1,279 +1,23 @@
1-
import {Directive, Ancestor} from 'angular2/src/core/annotations/decorators';
2-
import {Optional} from 'angular2/src/di/decorators';
3-
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
4-
import {Renderer} from 'angular2/src/render/api';
5-
import {
6-
isPresent,
7-
isString,
8-
CONST_EXPR,
9-
isBlank,
10-
BaseException,
11-
Type
12-
} from 'angular2/src/facade/lang';
13-
import {ListWrapper} from 'angular2/src/facade/collection';
14-
import {ControlGroup, Control, isControl} from './model';
15-
import {Validators} from './validators';
16-
17-
function _lookupControl(groupDirective: ControlGroupDirective, controlOrName: any): any {
18-
if (isControl(controlOrName)) {
19-
return controlOrName;
20-
}
21-
22-
if (isBlank(groupDirective)) {
23-
throw new BaseException(`No control group found for "${controlOrName}"`);
24-
}
25-
26-
var control = groupDirective.findControl(controlOrName);
27-
28-
if (isBlank(control)) {
29-
throw new BaseException(`Cannot find control "${controlOrName}"`);
30-
}
31-
32-
return control;
33-
}
34-
35-
/**
36-
* Binds a control group to a DOM element.
37-
*
38-
* # Example
39-
*
40-
* In this example, we bind the control group to the form element, and we bind the login and
41-
* password controls to the
42-
* login and password elements.
43-
*
44-
* Here we use {@link formDirectives}, rather than importing each form directive individually, e.g.
45-
* `ControlDirective`, `ControlGroupDirective`. This is just a shorthand for the same end result.
46-
*
47-
* ```
48-
* @Component({selector: "login-comp"})
49-
* @View({
50-
* directives: [formDirectives],
51-
* inline: "<form [control-group]='loginForm'>" +
52-
* "Login <input type='text' control='login'>" +
53-
* "Password <input type='password' control='password'>" +
54-
* "<button (click)="onLogin()">Login</button>" +
55-
* "</form>"
56-
* })
57-
* class LoginComp {
58-
* loginForm:ControlGroup;
59-
*
60-
* constructor() {
61-
* this.loginForm = new ControlGroup({
62-
* login: new Control(""),
63-
* password: new Control("")
64-
* });
65-
* }
66-
*
67-
* onLogin() {
68-
* // this.loginForm.value
69-
* }
70-
* }
71-
*
72-
* ```
73-
*
74-
* @exportedAs angular2/forms
75-
*/
76-
@Directive({selector: '[control-group]', properties: ['controlOrName: control-group']})
77-
export class ControlGroupDirective {
78-
_groupDirective: ControlGroupDirective;
79-
_directives: List<ControlDirective>;
80-
_controlOrName: any;
81-
82-
constructor(@Optional() @Ancestor() groupDirective: ControlGroupDirective) {
83-
this._groupDirective = groupDirective;
84-
this._directives = ListWrapper.create();
85-
}
86-
87-
set controlOrName(controlOrName) {
88-
this._controlOrName = controlOrName;
89-
this._updateDomValue();
90-
}
91-
92-
_updateDomValue() { ListWrapper.forEach(this._directives, (cd) => cd._updateDomValue()); }
93-
94-
addDirective(c: ControlDirective) { ListWrapper.push(this._directives, c); }
95-
96-
findControl(name: string): any { return this._getControlGroup().controls[name]; }
97-
98-
_getControlGroup(): ControlGroup {
99-
return _lookupControl(this._groupDirective, this._controlOrName);
100-
}
101-
}
102-
103-
104-
/**
105-
* Binds a control to a DOM element.
106-
*
107-
* # Example
108-
*
109-
* In this example, we bind the control to an input element. When the value of the input element
110-
* changes, the value of
111-
* the control will reflect that change. Likewise, if the value of the control changes, the input
112-
* element reflects that
113-
* change.
114-
*
115-
* Here we use {@link formDirectives}, rather than importing each form directive individually, e.g.
116-
* `ControlDirective`, `ControlGroupDirective`. This is just a shorthand for the same end result.
117-
*
118-
* ```
119-
* @Component({selector: "login-comp"})
120-
* @View({
121-
* directives: [formDirectives],
122-
* inline: "<input type='text' [control]='loginControl'>"
123-
* })
124-
* class LoginComp {
125-
* loginControl:Control;
126-
*
127-
* constructor() {
128-
* this.loginControl = new Control('');
129-
* }
130-
* }
131-
*
132-
* ```
133-
*
134-
* @exportedAs angular2/forms
135-
*/
136-
@Directive({selector: '[control]', properties: ['controlOrName: control']})
137-
export class ControlDirective {
138-
_groupDirective: ControlGroupDirective;
139-
140-
_controlOrName: any;
141-
valueAccessor: any; // ControlValueAccessor
142-
143-
validator: Function;
144-
145-
constructor(@Optional() @Ancestor() groupDirective: ControlGroupDirective) {
146-
this._groupDirective = groupDirective;
147-
this._controlOrName = null;
148-
this.validator = Validators.nullValidator;
149-
}
150-
151-
set controlOrName(controlOrName) {
152-
this._controlOrName = controlOrName;
153-
154-
if (isPresent(this._groupDirective)) {
155-
this._groupDirective.addDirective(this);
156-
}
157-
158-
var c = this._control();
159-
c.validator = Validators.compose([c.validator, this.validator]);
160-
161-
if (isBlank(this.valueAccessor)) {
162-
throw new BaseException(`Cannot find value accessor for control "${controlOrName}"`);
163-
}
164-
165-
this._updateDomValue();
166-
this._setUpUpdateControlValue();
167-
}
168-
169-
_updateDomValue() { this.valueAccessor.writeValue(this._control().value); }
170-
171-
_setUpUpdateControlValue() {
172-
this.valueAccessor.onChange = (newValue) => this._control().updateValue(newValue);
173-
}
174-
175-
_control() { return _lookupControl(this._groupDirective, this._controlOrName); }
176-
}
177-
178-
/**
179-
* The default accessor for writing a value and listening to changes that is used by a {@link
180-
* Control} directive.
181-
*
182-
* This is the default strategy that Angular uses when no other accessor is applied.
183-
*
184-
* # Example
185-
* ```
186-
* <input type="text" [control]="loginControl">
187-
* ```
188-
*
189-
* @exportedAs angular2/forms
190-
*/
191-
@Directive({
192-
selector: 'input:not([type=checkbox])[control],textarea[control]',
193-
hostListeners:
194-
{'change': 'onChange($event.target.value)', 'input': 'onChange($event.target.value)'},
195-
hostProperties: {'value': 'value'}
196-
})
197-
export class DefaultValueAccessor {
198-
value = null;
199-
onChange: Function;
200-
201-
constructor(cd: ControlDirective, private _elementRef: ElementRef, private _renderer: Renderer) {
202-
this.onChange = (_) => {};
203-
cd.valueAccessor = this;
204-
}
205-
206-
writeValue(value) {
207-
this._renderer.setElementProperty(this._elementRef.parentView.render,
208-
this._elementRef.boundElementIndex, 'value', value)
209-
}
210-
}
211-
212-
/**
213-
* The accessor for writing a value and listening to changes that is used by a {@link
214-
* Control} directive.
215-
*
216-
* This is the default strategy that Angular uses when no other accessor is applied.
217-
*
218-
* # Example
219-
* ```
220-
* <input type="text" [control]="loginControl">
221-
* ```
222-
*
223-
* @exportedAs angular2/forms
224-
*/
225-
@Directive({
226-
selector: 'select[control]',
227-
hostListeners:
228-
{'change': 'onChange($event.target.value)', 'input': 'onChange($event.target.value)'},
229-
hostProperties: {'value': 'value'}
230-
})
231-
export class SelectControlValueAccessor {
232-
value = null;
233-
onChange: Function;
234-
235-
constructor(cd: ControlDirective, private _elementRef: ElementRef, private _renderer: Renderer) {
236-
this.onChange = (_) => {};
237-
this.value = '';
238-
cd.valueAccessor = this;
239-
}
240-
241-
writeValue(value) {
242-
this._renderer.setElementProperty(this._elementRef.parentView.render,
243-
this._elementRef.boundElementIndex, 'value', value)
244-
}
245-
}
246-
247-
/**
248-
* The accessor for writing a value and listening to changes on a checkbox input element.
249-
*
250-
*
251-
* # Example
252-
* ```
253-
* <input type="checkbox" [control]="rememberLogin">
254-
* ```
255-
*
256-
* @exportedAs angular2/forms
257-
*/
258-
@Directive({
259-
selector: 'input[type=checkbox][control]',
260-
hostListeners: {'change': 'onChange($event.target.checked)'},
261-
hostProperties: {'checked': 'checked'}
262-
})
263-
export class CheckboxControlValueAccessor {
264-
checked: boolean;
265-
onChange: Function;
266-
267-
constructor(cd: ControlDirective, private _elementRef: ElementRef, private _renderer: Renderer) {
268-
this.onChange = (_) => {};
269-
cd.valueAccessor = this;
270-
}
271-
272-
writeValue(value) {
273-
this._renderer.setElementProperty(this._elementRef.parentView.render,
274-
this._elementRef.boundElementIndex, 'checked', value)
275-
}
276-
}
1+
import {Type, CONST_EXPR} from 'angular2/src/facade/lang';
2+
import {ControlNameDirective} from './directives/control_name_directive';
3+
import {FormControlDirective} from './directives/form_control_directive';
4+
import {ControlGroupDirective} from './directives/control_group_directive';
5+
import {FormModelDirective} from './directives/form_model_directive';
6+
import {TemplateDrivenFormDirective} from './directives/template_driven_form_directive';
7+
import {DefaultValueAccessor} from './directives/default_value_accessor';
8+
import {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor';
9+
import {SelectControlValueAccessor} from './directives/select_control_value_accessor';
10+
11+
export {ControlNameDirective} from './directives/control_name_directive';
12+
export {FormControlDirective} from './directives/form_control_directive';
13+
export {ControlDirective} from './directives/control_directive';
14+
export {ControlGroupDirective} from './directives/control_group_directive';
15+
export {FormModelDirective} from './directives/form_model_directive';
16+
export {TemplateDrivenFormDirective} from './directives/template_driven_form_directive';
17+
export {ControlValueAccessor} from './directives/control_value_accessor';
18+
export {DefaultValueAccessor} from './directives/default_value_accessor';
19+
export {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor';
20+
export {SelectControlValueAccessor} from './directives/select_control_value_accessor';
27721

27822
/**
27923
*
@@ -284,9 +28,14 @@ export class CheckboxControlValueAccessor {
28428
* @exportedAs angular2/forms
28529
*/
28630
export const formDirectives: List<Type> = CONST_EXPR([
31+
ControlNameDirective,
28732
ControlGroupDirective,
288-
ControlDirective,
289-
CheckboxControlValueAccessor,
33+
34+
FormControlDirective,
35+
FormModelDirective,
36+
TemplateDrivenFormDirective,
37+
29038
DefaultValueAccessor,
39+
CheckboxControlValueAccessor,
29140
SelectControlValueAccessor
292-
]);
41+
]);
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import {ElementRef, Directive} from 'angular2/angular2';
2+
import {Renderer} from 'angular2/src/render/api';
3+
import {ControlDirective} from './control_directive';
4+
import {ControlValueAccessor} from './control_value_accessor';
5+
6+
/**
7+
* The accessor for writing a value and listening to changes on a checkbox input element.
8+
*
9+
*
10+
* # Example
11+
* ```
12+
* <input type="checkbox" [control]="rememberLogin">
13+
* ```
14+
*
15+
* @exportedAs angular2/forms
16+
*/
17+
@Directive({
18+
selector: 'input[type=checkbox][control],input[type=checkbox][form-control]',
19+
hostListeners: {'change': 'onChange($event.target.checked)'},
20+
hostProperties: {'checked': 'checked'}
21+
})
22+
export class CheckboxControlValueAccessor implements ControlValueAccessor {
23+
checked: boolean;
24+
onChange: Function;
25+
26+
constructor(cd: ControlDirective, private _elementRef: ElementRef, private _renderer: Renderer) {
27+
this.onChange = (_) => {};
28+
cd.valueAccessor = this;
29+
}
30+
31+
writeValue(value) {
32+
this._renderer.setElementProperty(this._elementRef.parentView.render,
33+
this._elementRef.boundElementIndex, 'checked', value)
34+
}
35+
36+
registerOnChange(fn) { this.onChange = fn; }
37+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import {FormDirective} from './form_directive';
2+
import {List} from 'angular2/src/facade/collection';
3+
4+
export class ControlContainerDirective {
5+
name: string;
6+
get formDirective(): FormDirective { return null; }
7+
get path(): List<string> { return null; }
8+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import {ControlValueAccessor} from './control_value_accessor';
2+
import {Validators} from '../validators';
3+
4+
export class ControlDirective {
5+
name: string = null;
6+
valueAccessor: ControlValueAccessor = null;
7+
validator: Function;
8+
9+
get path(): List<string> { return null; }
10+
constructor() { this.validator = Validators.nullValidator; }
11+
}

0 commit comments

Comments
 (0)