feat(forms): added ng-model
This commit is contained in:
parent
17e1d7f117
commit
559f54e92b
|
@ -1,6 +1,7 @@
|
|||
import {Type, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {ControlNameDirective} from './directives/control_name_directive';
|
||||
import {FormControlDirective} from './directives/form_control_directive';
|
||||
import {NgModelDirective} from './directives/ng_model_directive';
|
||||
import {ControlGroupDirective} from './directives/control_group_directive';
|
||||
import {FormModelDirective} from './directives/form_model_directive';
|
||||
import {TemplateDrivenFormDirective} from './directives/template_driven_form_directive';
|
||||
|
@ -10,6 +11,7 @@ import {SelectControlValueAccessor} from './directives/select_control_value_acce
|
|||
|
||||
export {ControlNameDirective} from './directives/control_name_directive';
|
||||
export {FormControlDirective} from './directives/form_control_directive';
|
||||
export {NgModelDirective} from './directives/ng_model_directive';
|
||||
export {ControlDirective} from './directives/control_directive';
|
||||
export {ControlGroupDirective} from './directives/control_group_directive';
|
||||
export {FormModelDirective} from './directives/form_model_directive';
|
||||
|
@ -32,6 +34,7 @@ export const formDirectives: List<Type> = CONST_EXPR([
|
|||
ControlGroupDirective,
|
||||
|
||||
FormControlDirective,
|
||||
NgModelDirective,
|
||||
FormModelDirective,
|
||||
TemplateDrivenFormDirective,
|
||||
|
||||
|
|
|
@ -15,7 +15,8 @@ import {ControlValueAccessor} from './control_value_accessor';
|
|||
* @exportedAs angular2/forms
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'input[type=checkbox][control],input[type=checkbox][form-control]',
|
||||
selector:
|
||||
'input[type=checkbox][control],input[type=checkbox][form-control],input[type=checkbox][ng-model]',
|
||||
hostListeners: {'change': 'onChange($event.target.checked)'},
|
||||
hostProperties: {'checked': 'checked'}
|
||||
})
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
import {FormDirective} from './form_directive';
|
||||
import {List} from 'angular2/src/facade/collection';
|
||||
|
||||
/**
|
||||
* A directive that contains a group of [ControlDirective].
|
||||
*
|
||||
* @exportedAs angular2/forms
|
||||
*/
|
||||
export class ControlContainerDirective {
|
||||
name: string;
|
||||
get formDirective(): FormDirective { return null; }
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
import {ControlValueAccessor} from './control_value_accessor';
|
||||
import {Validators} from '../validators';
|
||||
|
||||
/**
|
||||
* A directive that bind a [Control] object to a DOM element.
|
||||
*
|
||||
* @exportedAs angular2/forms
|
||||
*/
|
||||
export class ControlDirective {
|
||||
name: string = null;
|
||||
valueAccessor: ControlValueAccessor = null;
|
||||
|
@ -8,4 +13,6 @@ export class ControlDirective {
|
|||
|
||||
get path(): List<string> { return null; }
|
||||
constructor() { this.validator = Validators.nullValidator; }
|
||||
|
||||
viewToModelUpdate(newValue: any): void {}
|
||||
}
|
||||
|
|
|
@ -14,9 +14,8 @@ const controlGroupBinding = CONST_EXPR(
|
|||
*
|
||||
* # Example
|
||||
*
|
||||
* In this example, we bind the control group to the form element, and we bind the login and
|
||||
* password controls to the
|
||||
* login and password elements.
|
||||
* In this example, we create a control group, and we bind the login and
|
||||
* password controls to the login and password elements.
|
||||
*
|
||||
* Here we use {@link formDirectives}, rather than importing each form directive individually, e.g.
|
||||
* `ControlDirective`, `ControlGroupDirective`. This is just a shorthand for the same end result.
|
||||
|
@ -25,10 +24,13 @@ const controlGroupBinding = CONST_EXPR(
|
|||
* @Component({selector: "login-comp"})
|
||||
* @View({
|
||||
* directives: [formDirectives],
|
||||
* template: "<form [control-group]='loginForm'>" +
|
||||
* template:
|
||||
* "<form [form-model]='loginForm'>" +
|
||||
* "<div control-group="credentials">
|
||||
* "Login <input type='text' control='login'>" +
|
||||
* "Password <input type='password' control='password'>" +
|
||||
* "<button (click)="onLogin()">Login</button>" +
|
||||
* "</div>"
|
||||
* "</form>"
|
||||
* })
|
||||
* class LoginComp {
|
||||
|
@ -36,8 +38,10 @@ const controlGroupBinding = CONST_EXPR(
|
|||
*
|
||||
* constructor() {
|
||||
* this.loginForm = new ControlGroup({
|
||||
* login: new Control(""),
|
||||
* password: new Control("")
|
||||
* credentials: new ControlGroup({
|
||||
* login: new Control(""),
|
||||
* password: new Control("")
|
||||
* })
|
||||
* });
|
||||
* }
|
||||
*
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {List} from 'angular2/src/facade/collection';
|
||||
import {Directive, Ancestor, onDestroy, onInit} from 'angular2/angular2';
|
||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {List, StringMapWrapper, StringMap} from 'angular2/src/facade/collection';
|
||||
import {Directive, Ancestor, onDestroy, onChange} from 'angular2/angular2';
|
||||
import {FORWARD_REF, Binding, Inject} from 'angular2/di';
|
||||
|
||||
import {ControlContainerDirective} from './control_container_directive';
|
||||
|
@ -11,11 +12,12 @@ const controlNameBinding =
|
|||
CONST_EXPR(new Binding(ControlDirective, {toAlias: FORWARD_REF(() => ControlNameDirective)}));
|
||||
|
||||
/**
|
||||
* Binds a control to a DOM element.
|
||||
* Binds a control with the specified name to a DOM element.
|
||||
*
|
||||
* # Example
|
||||
*
|
||||
* In this example, we bind the control to an input element. When the value of the input element
|
||||
* In this example, we bind the login control to an input element. When the value of the input
|
||||
* element
|
||||
* changes, the value of
|
||||
* the control will reflect that change. Likewise, if the value of the control changes, the input
|
||||
* element reflects that
|
||||
|
@ -28,13 +30,23 @@ const controlNameBinding =
|
|||
* @Component({selector: "login-comp"})
|
||||
* @View({
|
||||
* directives: [formDirectives],
|
||||
* template: "<input type='text' [control]='loginControl'>"
|
||||
* template:
|
||||
* "<form [form-model]='loginForm'>" +
|
||||
* "Login <input type='text' control='login'>" +
|
||||
* "<button (click)="onLogin()">Login</button>" +
|
||||
* "</form>"
|
||||
* })
|
||||
* class LoginComp {
|
||||
* loginControl:Control;
|
||||
* loginForm:ControlGroup;
|
||||
*
|
||||
* constructor() {
|
||||
* this.loginControl = new Control('');
|
||||
* this.loginForm = new ControlGroup({
|
||||
* login: new Control(""),
|
||||
* });
|
||||
* }
|
||||
*
|
||||
* onLogin() {
|
||||
* // this.loginForm.value
|
||||
* }
|
||||
* }
|
||||
*
|
||||
|
@ -45,20 +57,37 @@ const controlNameBinding =
|
|||
@Directive({
|
||||
selector: '[control]',
|
||||
hostInjector: [controlNameBinding],
|
||||
properties: ['name: control'],
|
||||
lifecycle: [onDestroy, onInit]
|
||||
properties: ['name: control', 'model: ng-model'],
|
||||
events: ['ngModel'],
|
||||
lifecycle: [onDestroy, onChange]
|
||||
})
|
||||
export class ControlNameDirective extends ControlDirective {
|
||||
_parent: ControlContainerDirective;
|
||||
ngModel: EventEmitter;
|
||||
model: any;
|
||||
_added: boolean;
|
||||
|
||||
constructor(@Ancestor() _parent: ControlContainerDirective) {
|
||||
super();
|
||||
this._parent = _parent;
|
||||
this.ngModel = new EventEmitter();
|
||||
this._added = false;
|
||||
}
|
||||
|
||||
onInit() { this.formDirective.addControl(this); }
|
||||
onChange(c: StringMap<string, any>) {
|
||||
if (!this._added) {
|
||||
this.formDirective.addControl(this);
|
||||
this._added = true;
|
||||
}
|
||||
if (StringMapWrapper.contains(c, "model")) {
|
||||
this.formDirective.updateModel(this, this.model);
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy() { this.formDirective.removeControl(this); }
|
||||
|
||||
viewToModelUpdate(newValue: any): void { ObservableWrapper.callNext(this.ngModel, newValue); }
|
||||
|
||||
get path(): List<string> { return controlPath(this.name, this._parent); }
|
||||
|
||||
get formDirective(): any { return this._parent.formDirective; }
|
||||
|
|
|
@ -11,14 +11,15 @@ import {ControlValueAccessor} from './control_value_accessor';
|
|||
*
|
||||
* # Example
|
||||
* ```
|
||||
* <input type="text" [control]="loginControl">
|
||||
* <input type="text" [form-control]="loginControl">
|
||||
* ```
|
||||
*
|
||||
* @exportedAs angular2/forms
|
||||
*/
|
||||
@Directive({
|
||||
selector:
|
||||
'input:not([type=checkbox])[control],textarea[control],input:not([type=checkbox])[form-control],textarea[form-control]',
|
||||
selector: 'input:not([type=checkbox])[control],textarea[control],' +
|
||||
'input:not([type=checkbox])[form-control],textarea[form-control],' +
|
||||
'input:not([type=checkbox])[ng-model],textarea[ng-model]',
|
||||
hostListeners:
|
||||
{'change': 'onChange($event.target.value)', 'input': 'onChange($event.target.value)'},
|
||||
hostProperties: {'value': 'value'}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
import {Directive, Ancestor, onChange} from 'angular2/angular2';
|
||||
import {FORWARD_REF, Binding} from 'angular2/di';
|
||||
|
||||
|
@ -9,17 +11,63 @@ import {setUpControl} from './shared';
|
|||
const formControlBinding =
|
||||
CONST_EXPR(new Binding(ControlDirective, {toAlias: FORWARD_REF(() => FormControlDirective)}));
|
||||
|
||||
/**
|
||||
* Binds a control to a DOM element.
|
||||
*
|
||||
* # Example
|
||||
*
|
||||
* In this example, we bind the control to an input element. When the value of the input element
|
||||
* changes, the value of
|
||||
* the control will reflect that change. Likewise, if the value of the control changes, the input
|
||||
* element reflects that
|
||||
* change.
|
||||
*
|
||||
* Here we use {@link formDirectives}, rather than importing each form directive individually, e.g.
|
||||
* `ControlDirective`, `ControlGroupDirective`. This is just a shorthand for the same end result.
|
||||
*
|
||||
* ```
|
||||
* @Component({selector: "login-comp"})
|
||||
* @View({
|
||||
* directives: [formDirectives],
|
||||
* template: "<input type='text' [form-control]='loginControl'>"
|
||||
* })
|
||||
* class LoginComp {
|
||||
* loginControl:Control;
|
||||
*
|
||||
* constructor() {
|
||||
* this.loginControl = new Control('');
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* ```
|
||||
*
|
||||
* @exportedAs angular2/forms
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[form-control]',
|
||||
hostInjector: [formControlBinding],
|
||||
properties: ['control: form-control'],
|
||||
properties: ['control: form-control', 'model: ng-model'],
|
||||
events: ['ngModel'],
|
||||
lifecycle: [onChange]
|
||||
})
|
||||
export class FormControlDirective extends ControlDirective {
|
||||
control: Control;
|
||||
ngModel: EventEmitter;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.ngModel = new EventEmitter();
|
||||
}
|
||||
|
||||
onChange(_) {
|
||||
setUpControl(this.control, this);
|
||||
this.control.updateValidity();
|
||||
}
|
||||
|
||||
set model(value) {
|
||||
this.control.updateValue(value);
|
||||
this.valueAccessor.writeValue(value);
|
||||
}
|
||||
|
||||
viewToModelUpdate(newValue: any): void { ObservableWrapper.callNext(this.ngModel, newValue); }
|
||||
}
|
||||
|
|
|
@ -6,4 +6,5 @@ export interface FormDirective {
|
|||
removeControl(dir: ControlDirective): void;
|
||||
addControlGroup(dir: ControlGroupDirective): void;
|
||||
removeControlGroup(dir: ControlGroupDirective): void;
|
||||
updateModel(dir: ControlDirective, value: any): void;
|
||||
}
|
|
@ -6,12 +6,53 @@ import {ControlDirective} from './control_directive';
|
|||
import {ControlGroupDirective} from './control_group_directive';
|
||||
import {ControlContainerDirective} from './control_container_directive';
|
||||
import {FormDirective} from './form_directive';
|
||||
import {ControlGroup} from '../model';
|
||||
import {Control, ControlGroup} from '../model';
|
||||
import {setUpControl} from './shared';
|
||||
|
||||
const formDirectiveBinding = CONST_EXPR(
|
||||
new Binding(ControlContainerDirective, {toAlias: FORWARD_REF(() => FormModelDirective)}));
|
||||
|
||||
/**
|
||||
* Binds a control group to a DOM element.
|
||||
*
|
||||
* # Example
|
||||
*
|
||||
* In this example, we bind the control group to the form element, and we bind the login and
|
||||
* password controls to the
|
||||
* login and password elements.
|
||||
*
|
||||
* Here we use {@link formDirectives}, rather than importing each form directive individually, e.g.
|
||||
* `ControlDirective`, `ControlGroupDirective`. This is just a shorthand for the same end result.
|
||||
*
|
||||
* ```
|
||||
* @Component({selector: "login-comp"})
|
||||
* @View({
|
||||
* directives: [formDirectives],
|
||||
* template: "<form [form-model]='loginForm'>" +
|
||||
* "Login <input type='text' control='login'>" +
|
||||
* "Password <input type='password' control='password'>" +
|
||||
* "<button (click)="onLogin()">Login</button>" +
|
||||
* "</form>"
|
||||
* })
|
||||
* class LoginComp {
|
||||
* loginForm:ControlGroup;
|
||||
*
|
||||
* constructor() {
|
||||
* this.loginForm = new ControlGroup({
|
||||
* login: new Control(""),
|
||||
* password: new Control("")
|
||||
* });
|
||||
* }
|
||||
*
|
||||
* onLogin() {
|
||||
* // this.loginForm.value
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* ```
|
||||
*
|
||||
* @exportedAs angular2/forms
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[form-model]',
|
||||
hostInjector: [formDirectiveBinding],
|
||||
|
@ -46,6 +87,12 @@ export class FormModelDirective extends ControlContainerDirective implements For
|
|||
|
||||
removeControlGroup(dir: ControlGroupDirective) {}
|
||||
|
||||
updateModel(dir: ControlDirective, value: any): void {
|
||||
var c = <Control>this.form.find(dir.path);
|
||||
c.value = value;
|
||||
dir.valueAccessor.writeValue(value);
|
||||
}
|
||||
|
||||
_updateDomValue() {
|
||||
ListWrapper.forEach(this.directives, dir => {
|
||||
var c: any = this.form.find(dir.path);
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {Directive, Ancestor, onChange} from 'angular2/angular2';
|
||||
import {FORWARD_REF, Binding} from 'angular2/di';
|
||||
|
||||
import {ControlDirective} from './control_directive';
|
||||
import {Control} from '../model';
|
||||
import {setUpControl} from './shared';
|
||||
|
||||
const formControlBinding =
|
||||
CONST_EXPR(new Binding(ControlDirective, {toAlias: FORWARD_REF(() => NgModelDirective)}));
|
||||
|
||||
@Directive({
|
||||
selector: '[ng-model]:not([control]):not([form-control])',
|
||||
hostInjector: [formControlBinding],
|
||||
properties: ['model: ng-model'],
|
||||
events: ['ngModel'],
|
||||
lifecycle: [onChange]
|
||||
})
|
||||
export class NgModelDirective extends ControlDirective {
|
||||
control: Control;
|
||||
ngModel: EventEmitter;
|
||||
model: any;
|
||||
_added: boolean;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.control = new Control("");
|
||||
this.ngModel = new EventEmitter();
|
||||
this._added = false;
|
||||
}
|
||||
|
||||
onChange(c) {
|
||||
if (!this._added) {
|
||||
setUpControl(this.control, this);
|
||||
this.control.updateValidity();
|
||||
this._added = true;
|
||||
};
|
||||
|
||||
if (StringMapWrapper.contains(c, "model")) {
|
||||
this.control.value = this.model;
|
||||
this.valueAccessor.writeValue(this.model);
|
||||
}
|
||||
}
|
||||
|
||||
get path(): List<string> { return []; }
|
||||
|
||||
viewToModelUpdate(newValue: any): void { ObservableWrapper.callNext(this.ngModel, newValue); }
|
||||
}
|
|
@ -17,7 +17,7 @@ import {ControlValueAccessor} from './control_value_accessor';
|
|||
* @exportedAs angular2/forms
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'select[control],select[form-control]',
|
||||
selector: 'select[control],select[form-control],select[ng-model]',
|
||||
hostListeners:
|
||||
{'change': 'onChange($event.target.value)', 'input': 'onChange($event.target.value)'},
|
||||
hostProperties: {'value': 'value'}
|
||||
|
|
|
@ -18,7 +18,10 @@ export function setUpControl(c: Control, dir: ControlDirective) {
|
|||
|
||||
c.validator = Validators.compose([c.validator, dir.validator]);
|
||||
dir.valueAccessor.writeValue(c.value);
|
||||
dir.valueAccessor.registerOnChange(newValue => c.updateValue(newValue));
|
||||
dir.valueAccessor.registerOnChange(newValue => {
|
||||
dir.viewToModelUpdate(newValue);
|
||||
c.updateValue(newValue);
|
||||
});
|
||||
}
|
||||
|
||||
function _throwError(dir: ControlDirective, message: string): void {
|
||||
|
|
|
@ -62,6 +62,14 @@ export class TemplateDrivenFormDirective extends ControlContainerDirective imple
|
|||
});
|
||||
}
|
||||
|
||||
updateModel(dir: ControlDirective, value: any): void {
|
||||
this._later(_ => {
|
||||
var c = <Control>this.form.find(dir.path);
|
||||
c.value = value;
|
||||
dir.valueAccessor.writeValue(value);
|
||||
});
|
||||
}
|
||||
|
||||
_findContainer(path: List<string>): ControlGroup {
|
||||
ListWrapper.removeLast(path);
|
||||
return <ControlGroup>this.form.find(path);
|
||||
|
|
|
@ -8,11 +8,12 @@ import {
|
|||
dispatchEvent,
|
||||
fakeAsync,
|
||||
flushMicrotasks,
|
||||
tick,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
inject,
|
||||
iit,
|
||||
xit
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
|
@ -347,6 +348,56 @@ export function main() {
|
|||
}));
|
||||
});
|
||||
|
||||
it("should support ng-model for complex forms",
|
||||
inject(
|
||||
[TestBed], fakeAsync(tb => {
|
||||
var form = new ControlGroup({"name": new Control("")});
|
||||
var ctx = MyComp.create({name: "oldValue", form: form});
|
||||
|
||||
var t =
|
||||
`<div [form-model]="form"><input type="text" control="name" [(ng-model)]="name"></div>`;
|
||||
|
||||
tb.createView(MyComp, {context: ctx, html: t})
|
||||
.then((view) => {
|
||||
view.detectChanges();
|
||||
|
||||
var input = view.querySelector("input");
|
||||
expect(input.value).toEqual("oldValue");
|
||||
|
||||
input.value = "updatedValue";
|
||||
dispatchEvent(input, "change");
|
||||
|
||||
tick();
|
||||
|
||||
expect(ctx.name).toEqual("updatedValue");
|
||||
});
|
||||
flushMicrotasks();
|
||||
})));
|
||||
|
||||
it("should support ng-model for single fields",
|
||||
inject([TestBed], fakeAsync(tb => {
|
||||
var form = new Control("");
|
||||
var ctx = MyComp.create({name: "oldValue", form: form});
|
||||
|
||||
var t = `<div><input type="text" [form-control]="form" [(ng-model)]="name"></div>`;
|
||||
|
||||
tb.createView(MyComp, {context: ctx, html: t})
|
||||
.then((view) => {
|
||||
view.detectChanges();
|
||||
|
||||
var input = view.querySelector("input");
|
||||
expect(input.value).toEqual("oldValue");
|
||||
|
||||
input.value = "updatedValue";
|
||||
dispatchEvent(input, "change");
|
||||
|
||||
tick();
|
||||
|
||||
expect(ctx.name).toEqual("updatedValue");
|
||||
});
|
||||
flushMicrotasks();
|
||||
})));
|
||||
|
||||
describe("template-driven forms", () => {
|
||||
it("should add new controls and control groups",
|
||||
inject([TestBed], fakeAsync(tb => {
|
||||
|
@ -365,7 +416,7 @@ export function main() {
|
|||
view.rawView.elementInjectors[0].get(TemplateDrivenFormDirective);
|
||||
expect(form.controls['user']).not.toBeDefined();
|
||||
|
||||
flushMicrotasks();
|
||||
tick();
|
||||
|
||||
expect(form.controls['user']).toBeDefined();
|
||||
expect(form.controls['user'].controls['login']).toBeDefined();
|
||||
|
@ -388,13 +439,13 @@ export function main() {
|
|||
var form = view.rawView.elementInjectors[0].get(
|
||||
TemplateDrivenFormDirective);
|
||||
|
||||
flushMicrotasks();
|
||||
tick();
|
||||
|
||||
expect(form.controls['login']).toBeDefined();
|
||||
|
||||
ctx.name = 'hide';
|
||||
view.detectChanges();
|
||||
flushMicrotasks();
|
||||
tick();
|
||||
|
||||
expect(form.controls['login']).not.toBeDefined();
|
||||
});
|
||||
|
@ -430,6 +481,56 @@ export function main() {
|
|||
});
|
||||
flushMicrotasks();
|
||||
})));
|
||||
|
||||
it("should support ng-model for complex forms",
|
||||
inject([TestBed], fakeAsync(tb => {
|
||||
var ctx = MyComp.create({name: "oldValue"});
|
||||
|
||||
var t = `<div form>
|
||||
<input type="text" control="name" [(ng-model)]="name">
|
||||
</div>`;
|
||||
|
||||
tb.createView(MyComp, {context: ctx, html: t})
|
||||
.then((view) => {
|
||||
view.detectChanges();
|
||||
tick();
|
||||
|
||||
var input = view.querySelector("input");
|
||||
expect(input.value).toEqual("oldValue");
|
||||
|
||||
input.value = "updatedValue";
|
||||
dispatchEvent(input, "change");
|
||||
|
||||
tick();
|
||||
|
||||
expect(ctx.name).toEqual("updatedValue");
|
||||
});
|
||||
flushMicrotasks();
|
||||
})));
|
||||
|
||||
|
||||
it("should support ng-model for single fields",
|
||||
inject([TestBed], fakeAsync(tb => {
|
||||
var ctx = MyComp.create({name: "oldValue"});
|
||||
|
||||
var t = `<div><input type="text" [(ng-model)]="name"></div>`;
|
||||
|
||||
tb.createView(MyComp, {context: ctx, html: t})
|
||||
.then((view) => {
|
||||
view.detectChanges();
|
||||
|
||||
var input = view.querySelector("input");
|
||||
expect(input.value).toEqual("oldValue");
|
||||
|
||||
input.value = "updatedValue";
|
||||
dispatchEvent(input, "change");
|
||||
|
||||
tick();
|
||||
|
||||
expect(ctx.name).toEqual("updatedValue");
|
||||
});
|
||||
flushMicrotasks();
|
||||
})));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue