fix(forms): fixed the handling of the select element
This commit is contained in:
parent
9bad70be5e
commit
f1541e65b3
|
@ -7,7 +7,10 @@ import {NgFormModel} from './directives/ng_form_model';
|
||||||
import {NgForm} from './directives/ng_form';
|
import {NgForm} from './directives/ng_form';
|
||||||
import {DefaultValueAccessor} from './directives/default_value_accessor';
|
import {DefaultValueAccessor} from './directives/default_value_accessor';
|
||||||
import {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor';
|
import {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor';
|
||||||
import {SelectControlValueAccessor} from './directives/select_control_value_accessor';
|
import {
|
||||||
|
SelectControlValueAccessor,
|
||||||
|
NgSelectOption
|
||||||
|
} from './directives/select_control_value_accessor';
|
||||||
import {NgRequiredValidator} from './directives/validators';
|
import {NgRequiredValidator} from './directives/validators';
|
||||||
|
|
||||||
export {NgControlName} from './directives/ng_control_name';
|
export {NgControlName} from './directives/ng_control_name';
|
||||||
|
@ -40,6 +43,7 @@ export const formDirectives: List<Type> = CONST_EXPR([
|
||||||
NgFormModel,
|
NgFormModel,
|
||||||
NgForm,
|
NgForm,
|
||||||
|
|
||||||
|
NgSelectOption,
|
||||||
DefaultValueAccessor,
|
DefaultValueAccessor,
|
||||||
CheckboxControlValueAccessor,
|
CheckboxControlValueAccessor,
|
||||||
SelectControlValueAccessor,
|
SelectControlValueAccessor,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import {Directive} from 'angular2/angular2';
|
import {Directive, Renderer, ElementRef} from 'angular2/angular2';
|
||||||
import {NgControl} from './ng_control';
|
import {NgControl} from './ng_control';
|
||||||
import {ControlValueAccessor} from './control_value_accessor';
|
import {ControlValueAccessor} from './control_value_accessor';
|
||||||
|
import {setProperty} from './shared';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The accessor for writing a value and listening to changes on a checkbox input element.
|
* The accessor for writing a value and listening to changes on a checkbox input element.
|
||||||
|
@ -32,13 +33,18 @@ export class CheckboxControlValueAccessor implements ControlValueAccessor {
|
||||||
onChange: Function;
|
onChange: Function;
|
||||||
onTouched: Function;
|
onTouched: Function;
|
||||||
|
|
||||||
constructor(private cd: NgControl) {
|
constructor(private cd: NgControl, private renderer: Renderer, private elementRef: ElementRef) {
|
||||||
this.onChange = (_) => {};
|
this.onChange = (_) => {};
|
||||||
this.onTouched = (_) => {};
|
this.onTouched = (_) => {};
|
||||||
cd.valueAccessor = this;
|
cd.valueAccessor = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
writeValue(value) { this.checked = value; }
|
writeValue(value) {
|
||||||
|
// both this.checked and setProperty are required at the moment
|
||||||
|
// remove when a proper imperative API is provided
|
||||||
|
this.checked = value;
|
||||||
|
setProperty(this.renderer, this.elementRef, "checked", value);
|
||||||
|
}
|
||||||
|
|
||||||
registerOnChange(fn): void { this.onChange = fn; }
|
registerOnChange(fn): void { this.onChange = fn; }
|
||||||
registerOnTouched(fn): void { this.onTouched = fn; }
|
registerOnTouched(fn): void { this.onTouched = fn; }
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import {Directive} from 'angular2/angular2';
|
import {Directive, Renderer, ElementRef} from 'angular2/angular2';
|
||||||
import {NgControl} from './ng_control';
|
import {NgControl} from './ng_control';
|
||||||
import {ControlValueAccessor} from './control_value_accessor';
|
import {ControlValueAccessor} from './control_value_accessor';
|
||||||
import {isBlank} from 'angular2/src/facade/lang';
|
import {isBlank} from 'angular2/src/facade/lang';
|
||||||
|
import {setProperty} from './shared';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default accessor for writing a value and listening to changes that is used by the
|
* The default accessor for writing a value and listening to changes that is used by the
|
||||||
|
@ -35,13 +36,18 @@ export class DefaultValueAccessor implements ControlValueAccessor {
|
||||||
onChange: Function;
|
onChange: Function;
|
||||||
onTouched: Function;
|
onTouched: Function;
|
||||||
|
|
||||||
constructor(private cd: NgControl) {
|
constructor(private cd: NgControl, private renderer: Renderer, private elementRef: ElementRef) {
|
||||||
this.onChange = (_) => {};
|
this.onChange = (_) => {};
|
||||||
this.onTouched = (_) => {};
|
this.onTouched = (_) => {};
|
||||||
cd.valueAccessor = this;
|
cd.valueAccessor = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
writeValue(value) { this.value = isBlank(value) ? "" : value; }
|
writeValue(value) {
|
||||||
|
// both this.value and setProperty are required at the moment
|
||||||
|
// remove when a proper imperative API is provided
|
||||||
|
this.value = isBlank(value) ? '' : value;
|
||||||
|
setProperty(this.renderer, this.elementRef, 'value', this.value);
|
||||||
|
}
|
||||||
|
|
||||||
registerOnChange(fn): void { this.onChange = fn; }
|
registerOnChange(fn): void { this.onChange = fn; }
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,22 @@
|
||||||
import {Directive} from 'angular2/angular2';
|
import {Directive, Query, QueryList, Renderer, ElementRef} from 'angular2/angular2';
|
||||||
import {NgControl} from './ng_control';
|
import {NgControl} from './ng_control';
|
||||||
import {ControlValueAccessor} from './control_value_accessor';
|
import {ControlValueAccessor} from './control_value_accessor';
|
||||||
|
import {setProperty} from './shared';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks <option> as dynamic, so Angular can be notified when options change.
|
||||||
|
*
|
||||||
|
* #Example:
|
||||||
|
* ```
|
||||||
|
* <select ng-control="city">
|
||||||
|
* <option *ng-for="#c of cities" [value]="c"></option>
|
||||||
|
* </select>
|
||||||
|
* ``
|
||||||
|
* @exportedAs angular2/forms
|
||||||
|
*/
|
||||||
|
@Directive({selector: 'option'})
|
||||||
|
export class NgSelectOption {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The accessor for writing a value and listening to changes on a select element.
|
* The accessor for writing a value and listening to changes on a select element.
|
||||||
|
@ -23,19 +39,30 @@ import {ControlValueAccessor} from './control_value_accessor';
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
export class SelectControlValueAccessor implements ControlValueAccessor {
|
export class SelectControlValueAccessor implements ControlValueAccessor {
|
||||||
value = null;
|
value = '';
|
||||||
onChange: Function;
|
onChange: Function;
|
||||||
onTouched: Function;
|
onTouched: Function;
|
||||||
|
|
||||||
constructor(private cd: NgControl) {
|
constructor(private cd: NgControl, private renderer: Renderer, private elementRef: ElementRef,
|
||||||
|
@Query(NgSelectOption, {descendants: true}) query: QueryList<NgSelectOption>) {
|
||||||
this.onChange = (_) => {};
|
this.onChange = (_) => {};
|
||||||
this.onTouched = (_) => {};
|
this.onTouched = (_) => {};
|
||||||
this.value = '';
|
|
||||||
cd.valueAccessor = this;
|
cd.valueAccessor = this;
|
||||||
|
|
||||||
|
this._updateValueWhenListOfOptionsChanges(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
writeValue(value) { this.value = value; }
|
writeValue(value) {
|
||||||
|
// both this.value and setProperty are required at the moment
|
||||||
|
// remove when a proper imperative API is provided
|
||||||
|
this.value = value;
|
||||||
|
setProperty(this.renderer, this.elementRef, "value", value);
|
||||||
|
}
|
||||||
|
|
||||||
registerOnChange(fn): void { this.onChange = fn; }
|
registerOnChange(fn): void { this.onChange = fn; }
|
||||||
registerOnTouched(fn): void { this.onTouched = fn; }
|
registerOnTouched(fn): void { this.onTouched = fn; }
|
||||||
|
|
||||||
|
private _updateValueWhenListOfOptionsChanges(query: QueryList<NgSelectOption>) {
|
||||||
|
query.onChange(() => this.writeValue(this.value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import {ControlContainer} from './control_container';
|
||||||
import {NgControl} from './ng_control';
|
import {NgControl} from './ng_control';
|
||||||
import {Control} from '../model';
|
import {Control} from '../model';
|
||||||
import {Validators} from '../validators';
|
import {Validators} from '../validators';
|
||||||
|
import {Renderer, ElementRef} from 'angular2/angular2';
|
||||||
|
|
||||||
|
|
||||||
export function controlPath(name, parent: ControlContainer) {
|
export function controlPath(name, parent: ControlContainer) {
|
||||||
var p = ListWrapper.clone(parent.path);
|
var p = ListWrapper.clone(parent.path);
|
||||||
|
@ -37,3 +39,9 @@ function _throwError(dir: NgControl, message: string): void {
|
||||||
var path = ListWrapper.join(dir.path, " -> ");
|
var path = ListWrapper.join(dir.path, " -> ");
|
||||||
throw new BaseException(`${message} '${path}'`);
|
throw new BaseException(`${message} '${path}'`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setProperty(renderer: Renderer, elementRef: ElementRef, propName: string,
|
||||||
|
propValue: any) {
|
||||||
|
renderer.setElementProperty(elementRef.parentView.render, elementRef.boundElementIndex, propName,
|
||||||
|
propValue);
|
||||||
|
}
|
|
@ -19,7 +19,7 @@ import {
|
||||||
|
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
import {TestBed} from 'angular2/src/test_lib/test_bed';
|
import {TestBed} from 'angular2/src/test_lib/test_bed';
|
||||||
import {NgIf} from 'angular2/directives';
|
import {NgIf, NgFor} from 'angular2/directives';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Control,
|
Control,
|
||||||
|
@ -300,6 +300,28 @@ export function main() {
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it("should support <select> with a dynamic list of options",
|
||||||
|
inject([TestBed], fakeAsync((tb: TestBed) => {
|
||||||
|
var ctx = MyComp.create(
|
||||||
|
{form: new ControlGroup({"city": new Control("NYC")}), data: ['SF', 'NYC']});
|
||||||
|
|
||||||
|
var t = `<div [ng-form-model]="form">
|
||||||
|
<select ng-control="city">
|
||||||
|
<option *ng-for="#c of data" [value]="c"></option>
|
||||||
|
</select>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
tb.createView(MyComp, {context: ctx, html: t})
|
||||||
|
.then((view) => {
|
||||||
|
view.detectChanges();
|
||||||
|
tick();
|
||||||
|
|
||||||
|
var select = view.querySelector('select');
|
||||||
|
|
||||||
|
expect(select.value).toEqual('NYC');
|
||||||
|
});
|
||||||
|
})));
|
||||||
|
|
||||||
it("should support custom value accessors",
|
it("should support custom value accessors",
|
||||||
inject([TestBed, AsyncTestCompleter], (tb: TestBed, async) => {
|
inject([TestBed, AsyncTestCompleter], (tb: TestBed, async) => {
|
||||||
var ctx = MyComp.create({form: new ControlGroup({"name": new Control("aa")})});
|
var ctx = MyComp.create({form: new ControlGroup({"name": new Control("aa")})});
|
||||||
|
@ -752,15 +774,17 @@ class WrappedValue implements ControlValueAccessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({selector: "my-comp"})
|
@Component({selector: "my-comp"})
|
||||||
@View({directives: [formDirectives, WrappedValue, NgIf]})
|
@View({directives: [formDirectives, WrappedValue, NgIf, NgFor]})
|
||||||
class MyComp {
|
class MyComp {
|
||||||
form: any;
|
form: any;
|
||||||
name: string;
|
name: string;
|
||||||
|
data: any;
|
||||||
|
|
||||||
static create({form, name}: {form?: any, name?: any}) {
|
static create({form, name, data}: {form?: any, name?: any, data?: any}) {
|
||||||
var mc = new MyComp();
|
var mc = new MyComp();
|
||||||
mc.form = form;
|
mc.form = form;
|
||||||
mc.name = name;
|
mc.name = name;
|
||||||
|
mc.data = data;
|
||||||
return mc;
|
return mc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue