Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(102)

Unified Diff: third_party/pkg/angular/test/directive/ng_model_spec.dart

Issue 257423008: Update all Angular libs (run update_all.sh). (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/pkg/angular/test/directive/ng_model_spec.dart
diff --git a/third_party/pkg/angular/test/directive/ng_model_spec.dart b/third_party/pkg/angular/test/directive/ng_model_spec.dart
index 645e9c1a4c00823fca80d0d4f72d63e12d812471..0fd41441e4ad7691495ca0f0963f76f294755c1c 100644
--- a/third_party/pkg/angular/test/directive/ng_model_spec.dart
+++ b/third_party/pkg/angular/test/directive/ng_model_spec.dart
@@ -3,18 +3,51 @@ library ng_model_spec;
import '../_specs.dart';
import 'dart:html' as dom;
+//-----------------------------------------------------------------------------
+// Utility functions
+
+/* This function simulates typing the given text into the input field. The
+ * text will be added wherever the insertion point happens to be. This method
+ * has as side-effect to set the focus on the input (without setting the
+ * focus, the text dispatch may not work).
+ */
+void simulateTypingText(InputElement input, String text) {
+ input..focus()..dispatchEvent(new TextEvent('textInput', data: text));
+}
+
+bool simulateTypingTextWithConfirmation(InputElement input, String text,
+ { bool shouldWorkForChrome : true }) {
+ bool result;
+ String val = input.value;
+ try {
+ simulateTypingText(input, text);
+ result = input.value == val + text;
+ } catch (e) {
+ result = false;
+ }
+ if (!result && shouldWorkForChrome) expect(isBrowser('Chrome')).toBeFalsy();
+ return result;
+}
+
+bool isBrowser(String pattern) => dom.window.navigator.userAgent.indexOf(pattern) > 0;
+
+//-----------------------------------------------------------------------------
+
void main() {
describe('ng-model', () {
TestBed _;
- beforeEach(module((Module module) {
- module..type(ControllerWithNoLove);
- }));
+ beforeEachModule((Module module) {
+ module
+ ..type(ControllerWithNoLove)
+ ..type(MyCustomInputValidator)
+ ..type(CountingValidator);
+ });
- beforeEach(inject((TestBed tb) => _ = tb));
+ beforeEach((TestBed tb) => _ = tb);
describe('type="text" like', () {
- it('should update input value from model', inject(() {
+ it('should update input value from model', () {
_.compile('<input type="text" ng-model="model">');
_.rootScope.apply();
@@ -22,9 +55,9 @@ void main() {
_.rootScope.apply('model = "misko"');
expect((_.rootElement as dom.InputElement).value).toEqual('misko');
- }));
+ });
- it('should render null as the empty string', inject(() {
+ it('should render null as the empty string', () {
_.compile('<input type="text" ng-model="model">');
_.rootScope.apply();
@@ -32,9 +65,9 @@ void main() {
_.rootScope.apply('model = null');
expect((_.rootElement as dom.InputElement).value).toEqual('');
- }));
+ });
- it('should update model from the input value', inject(() {
+ it('should update model from the input value', () {
_.compile('<input type="text" ng-model="model" probe="p">');
Probe probe = _.rootScope.context['p'];
var ngModel = probe.directive(NgModel);
@@ -45,12 +78,69 @@ void main() {
expect(_.rootScope.context['model']).toEqual('abc');
inputElement.value = 'def';
- var input = probe.directive(InputTextLikeDirective);
+ var input = probe.directive(InputTextLike);
input.processValue();
expect(_.rootScope.context['model']).toEqual('def');
- }));
+ });
+
+ it('should write to input only if the value is different',
+ (Injector i, Animate animate) {
+
+ var scope = _.rootScope;
+ var element = new dom.InputElement();
+ var ngElement = new NgElement(element, scope, animate);
+
+ NodeAttrs nodeAttrs = new NodeAttrs(new DivElement());
+ nodeAttrs['ng-model'] = 'model';
+ var model = new NgModel(scope, ngElement, i.createChild([new Module()]),
+ nodeAttrs, new Animate());
+ dom.querySelector('body').append(element);
+ var input = new InputTextLike(element, model, scope);
+
+ element
+ ..value = 'abc'
+ ..selectionStart = 1
+ ..selectionEnd = 2;
+
+ scope.apply(() {
+ scope.context['model'] = 'abc';
+ });
+
+ expect(element.value).toEqual('abc');
+ // No update. selectionStart/End is unchanged.
+ expect(element.selectionStart).toEqual(1);
+ expect(element.selectionEnd).toEqual(2);
+
+ scope.apply(() {
+ scope.context['model'] = 'xyz';
+ });
+
+ // Value updated. selectionStart/End changed.
+ expect(element.value).toEqual('xyz');
+ expect(element.selectionStart).toEqual(3);
+ expect(element.selectionEnd).toEqual(3);
+ });
+
+ it('should only render the input value upon the next digest', (Scope scope) {
+ _.compile('<input type="text" ng-model="model" probe="p">');
+ Probe probe = _.rootScope.context['p'];
+ var ngModel = probe.directive(NgModel);
+ InputElement inputElement = probe.element;
+
+ ngModel.render('xyz');
+ scope.context['model'] = 'xyz';
+
+ expect(inputElement.value).not.toEqual('xyz');
- it('should update model from the input value for type=number', inject(() {
+ scope.apply();
+
+ expect(inputElement.value).toEqual('xyz');
+ });
+ });
+
+ describe('type="number" or type="range"', () {
+
+ it('should update model from the input value for type=number', () {
_.compile('<input type="number" ng-model="model" probe="p">');
Probe probe = _.rootScope.context['p'];
var ngModel = probe.directive(NgModel);
@@ -61,12 +151,12 @@ void main() {
expect(_.rootScope.context['model']).toEqual(12);
inputElement.value = '14';
- var input = probe.directive(InputNumberLikeDirective);
+ var input = probe.directive(InputNumberLike);
input.processValue();
expect(_.rootScope.context['model']).toEqual(14);
- }));
+ });
- it('should update input type=number to blank when model is null', inject(() {
+ it('should update input type=number to blank when model is null', () {
_.compile('<input type="number" ng-model="model" probe="p">');
Probe probe = _.rootScope.context['p'];
var ngModel = probe.directive(NgModel);
@@ -79,55 +169,28 @@ void main() {
_.rootScope.context['model'] = null;
_.rootScope.apply();
expect(inputElement.value).toEqual('');
- }));
-
- it('should write to input only if value is different', inject((Injector i, AstParser parser) {
- var scope = _.rootScope;
- var element = new dom.InputElement();
- NodeAttrs nodeAttrs = new NodeAttrs(new DivElement());
- nodeAttrs['ng-model'] = 'model';
- var model = new NgModel(scope, element, i.createChild([new Module()]), new NgNullForm(), parser, nodeAttrs);
- dom.querySelector('body').append(element);
- var input = new InputTextLikeDirective(element, model, scope);
-
- element
- ..value = 'abc'
- ..selectionStart = 1
- ..selectionEnd = 2;
-
- model.render('abc');
-
- expect(element.value).toEqual('abc');
- // No update. selectionStart/End is unchanged.
- expect(element.selectionStart).toEqual(1);
- expect(element.selectionEnd).toEqual(2);
-
- model.render('xyz');
-
- // Value updated. selectionStart/End changed.
- expect(element.value).toEqual('xyz');
- expect(element.selectionStart).toEqual(3);
- expect(element.selectionEnd).toEqual(3);
- }));
- });
+ });
- /* This function simulates typing the given text into the input
- * field. The text will be added wherever the insertion point
- * happens to be. This method has as side-effect to set the
- * focus on the input (without setting the focus the text
- * dispatch may not work).
- */
- void simulateTypingText(InputElement input, String text) {
- input..focus()..dispatchEvent(new TextEvent('textInput', data: text));
- }
+ it('should be invalid when the input value results in a NaN value', () {
+ _.compile('<input type="number" ng-model="model" probe="p">');
+ Probe probe = _.rootScope.context['p'];
+ var ngModel = probe.directive(NgModel);
+ InputElement inputElement = probe.element;
- describe('type="number" like', () {
+ inputElement.value = 'aa';
+ _.triggerEvent(inputElement, 'change');
+ expect(_.rootScope.context['model'].isNaN).toBe(true);
+ expect(ngModel.valid).toBe(false);
+ });
- it('should leave input unchanged when text does not represent a valid number', inject((Injector i) {
+ it('should leave input unchanged when text does not represent a valid number', (Injector i) {
var modelFieldName = 'modelForNumFromInvalid1';
var element = _.compile('<input type="number" ng-model="$modelFieldName">');
dom.querySelector('body').append(element);
+ if (!simulateTypingTextWithConfirmation(element, '1')) return; // skip test.
+ element.value = ''; // reset input
+
// This test will progressively enter the text '1e1'
// '1' is a valid number.
// '1e' is not a valid number.
@@ -149,35 +212,38 @@ void main() {
_.triggerEvent(element, 'change');
expect(element.value).toEqual('1e1');
expect(_.rootScope.context[modelFieldName]).toEqual(10);
- }));
+ });
- it('should not reformat user input to equivalent numeric representation', inject((Injector i) {
+ it('should not reformat user input to equivalent numeric representation', (Injector i) {
var modelFieldName = 'modelForNumFromInvalid2';
var element = _.compile('<input type="number" ng-model="$modelFieldName">');
dom.querySelector('body').append(element);
+ if (!simulateTypingTextWithConfirmation(element, '1')) return; // skip test.
+ element.value = ''; // reset input
+
simulateTypingText(element, '1e-1');
expect(element.value).toEqual('1e-1');
expect(_.rootScope.context[modelFieldName]).toEqual(0.1);
- }));
+ });
- it('should update input value from model', inject(() {
+ it('should update input value from model', () {
_.compile('<input type="number" ng-model="model">');
_.rootScope.apply();
_.rootScope.apply('model = 42');
expect((_.rootElement as dom.InputElement).value).toEqual('42');
- }));
+ });
- it('should update input value from model for range inputs', inject(() {
+ it('should update input value from model for range inputs', () {
_.compile('<input type="range" ng-model="model">');
_.rootScope.apply();
_.rootScope.apply('model = 42');
expect((_.rootElement as dom.InputElement).value).toEqual('42');
- }));
+ });
- it('should update model from the input value', inject(() {
+ it('should update model from the input value', () {
_.compile('<input type="number" ng-model="model" probe="p">');
Probe probe = _.rootScope.context['p'];
var ngModel = probe.directive(NgModel);
@@ -188,12 +254,12 @@ void main() {
expect(_.rootScope.context['model']).toEqual(42);
inputElement.value = '43';
- var input = probe.directive(InputNumberLikeDirective);
+ var input = probe.directive(InputNumberLike);
input.processValue();
expect(_.rootScope.context['model']).toEqual(43);
- }));
+ });
- it('should update model to NaN from a blank input value', inject(() {
+ it('should update model to NaN from a blank input value', () {
_.compile('<input type="number" ng-model="model" probe="p">');
Probe probe = _.rootScope.context['p'];
var ngModel = probe.directive(NgModel);
@@ -202,9 +268,9 @@ void main() {
inputElement.value = '';
_.triggerEvent(inputElement, 'change');
expect(_.rootScope.context['model'].isNaN).toBeTruthy();
- }));
+ });
- it('should update model from the input value for range inputs', inject(() {
+ it('should update model from the input value for range inputs', () {
_.compile('<input type="range" ng-model="model" probe="p">');
Probe probe = _.rootScope.context['p'];
var ngModel = probe.directive(NgModel);
@@ -215,12 +281,12 @@ void main() {
expect(_.rootScope.context['model']).toEqual(42);
inputElement.value = '43';
- var input = probe.directive(InputNumberLikeDirective);
+ var input = probe.directive(InputNumberLike);
input.processValue();
expect(_.rootScope.context['model']).toEqual(43);
- }));
+ });
- it('should update model to a native default value from a blank range input value', inject(() {
+ it('should update model to a native default value from a blank range input value', () {
_.compile('<input type="range" ng-model="model" probe="p">');
Probe probe = _.rootScope.context['p'];
var ngModel = probe.directive(NgModel);
@@ -229,20 +295,36 @@ void main() {
inputElement.value = '';
_.triggerEvent(inputElement, 'change');
expect(_.rootScope.context['model']).toBeDefined();
- }));
+ });
- it('should render null as blank', inject(() {
+ it('should render null as blank', () {
_.compile('<input type="number" ng-model="model">');
_.rootScope.apply();
_.rootScope.apply('model = null');
expect((_.rootElement as dom.InputElement).value).toEqual('');
- }));
+ });
+
+ it('should only render the input value upon the next digest', (Scope scope) {
+ _.compile('<input type="number" ng-model="model" probe="p">');
+ Probe probe = _.rootScope.context['p'];
+ var ngModel = probe.directive(NgModel);
+ InputElement inputElement = probe.element;
+
+ ngModel.render(123);
+ scope.context['model'] = 123;
+
+ expect(inputElement.value).not.toEqual('123');
+
+ scope.apply();
+
+ expect(inputElement.value).toEqual('123');
+ });
});
describe('type="password"', () {
- it('should update input value from model', inject(() {
+ it('should update input value from model', () {
_.compile('<input type="password" ng-model="model">');
_.rootScope.apply();
@@ -250,9 +332,9 @@ void main() {
_.rootScope.apply('model = "misko"');
expect((_.rootElement as dom.InputElement).value).toEqual('misko');
- }));
+ });
- it('should render null as the empty string', inject(() {
+ it('should render null as the empty string', () {
_.compile('<input type="password" ng-model="model">');
_.rootScope.apply();
@@ -260,9 +342,9 @@ void main() {
_.rootScope.apply('model = null');
expect((_.rootElement as dom.InputElement).value).toEqual('');
- }));
+ });
- it('should update model from the input value', inject(() {
+ it('should update model from the input value', () {
_.compile('<input type="password" ng-model="model" probe="p">');
Probe probe = _.rootScope.context['p'];
var ngModel = probe.directive(NgModel);
@@ -273,42 +355,67 @@ void main() {
expect(_.rootScope.context['model']).toEqual('abc');
inputElement.value = 'def';
- var input = probe.directive(InputTextLikeDirective);
+ var input = probe.directive(InputTextLike);
input.processValue();
expect(_.rootScope.context['model']).toEqual('def');
- }));
+ });
+
+ it('should write to input only if value is different',
+ (Injector i, Animate animate) {
- it('should write to input only if value is different', inject((Injector i, AstParser parser) {
var scope = _.rootScope;
var element = new dom.InputElement();
+ var ngElement = new NgElement(element, scope, animate);
+
NodeAttrs nodeAttrs = new NodeAttrs(new DivElement());
nodeAttrs['ng-model'] = 'model';
- var model = new NgModel(scope, element, i.createChild([new Module()]), new NgNullForm(), parser, nodeAttrs);
+ var model = new NgModel(scope, ngElement, i.createChild([new Module()]),
+ nodeAttrs, new Animate());
dom.querySelector('body').append(element);
- var input = new InputTextLikeDirective(element, model, scope);
+ var input = new InputTextLike(element, model, scope);
element
..value = 'abc'
..selectionStart = 1
..selectionEnd = 2;
- model.render('abc');
+ scope.apply(() {
+ scope.context['model'] = 'abc';
+ });
expect(element.value).toEqual('abc');
expect(element.selectionStart).toEqual(1);
expect(element.selectionEnd).toEqual(2);
- model.render('xyz');
+ scope.apply(() {
+ scope.context['model'] = 'xyz';
+ });
expect(element.value).toEqual('xyz');
expect(element.selectionStart).toEqual(3);
expect(element.selectionEnd).toEqual(3);
- }));
+ });
+
+ it('should only render the input value upon the next digest', (Scope scope) {
+ _.compile('<input type="password" ng-model="model" probe="p">');
+ Probe probe = _.rootScope.context['p'];
+ var ngModel = probe.directive(NgModel);
+ InputElement inputElement = probe.element;
+
+ ngModel.render('xyz');
+ scope.context['model'] = 'xyz';
+
+ expect(inputElement.value).not.toEqual('xyz');
+
+ scope.apply();
+
+ expect(inputElement.value).toEqual('xyz');
+ });
});
describe('type="search"', () {
- it('should update input value from model', inject(() {
+ it('should update input value from model', () {
_.compile('<input type="search" ng-model="model">');
_.rootScope.apply();
@@ -316,9 +423,9 @@ void main() {
_.rootScope.apply('model = "misko"');
expect((_.rootElement as dom.InputElement).value).toEqual('misko');
- }));
+ });
- it('should render null as the empty string', inject(() {
+ it('should render null as the empty string', () {
_.compile('<input type="search" ng-model="model">');
_.rootScope.apply();
@@ -326,9 +433,9 @@ void main() {
_.rootScope.apply('model = null');
expect((_.rootElement as dom.InputElement).value).toEqual('');
- }));
+ });
- it('should update model from the input value', inject(() {
+ it('should update model from the input value', () {
_.compile('<input type="search" ng-model="model" probe="p">');
Probe probe = _.rootScope.context['p'];
var ngModel = probe.directive(NgModel);
@@ -339,49 +446,74 @@ void main() {
expect(_.rootScope.context['model']).toEqual('abc');
inputElement.value = 'def';
- var input = probe.directive(InputTextLikeDirective);
+ var input = probe.directive(InputTextLike);
input.processValue();
expect(_.rootScope.context['model']).toEqual('def');
- }));
+ });
+
+ it('should write to input only if value is different',
+ (Injector i, Animate animate) {
- it('should write to input only if value is different', inject((Injector i, AstParser parser) {
var scope = _.rootScope;
var element = new dom.InputElement();
+ var ngElement = new NgElement(element, scope, animate);
+
NodeAttrs nodeAttrs = new NodeAttrs(new DivElement());
nodeAttrs['ng-model'] = 'model';
- var model = new NgModel(scope, element, i.createChild([new Module()]), new NgNullForm(), parser, nodeAttrs);
+ var model = new NgModel(scope, ngElement, i.createChild([new Module()]),
+ nodeAttrs, new Animate());
dom.querySelector('body').append(element);
- var input = new InputTextLikeDirective(element, model, scope);
+ var input = new InputTextLike(element, model, scope);
element
..value = 'abc'
..selectionStart = 1
..selectionEnd = 2;
- model.render('abc');
+ scope.apply(() {
+ scope.context['model'] = 'abc';
+ });
expect(element.value).toEqual('abc');
// No update. selectionStart/End is unchanged.
expect(element.selectionStart).toEqual(1);
expect(element.selectionEnd).toEqual(2);
- model.render('xyz');
+ scope.apply(() {
+ scope.context['model'] = 'xyz';
+ });
// Value updated. selectionStart/End changed.
expect(element.value).toEqual('xyz');
expect(element.selectionStart).toEqual(3);
expect(element.selectionEnd).toEqual(3);
- }));
+ });
+
+ it('should only render the input value upon the next digest', (Scope scope) {
+ _.compile('<input type="search" ng-model="model" probe="p">');
+ Probe probe = _.rootScope.context['p'];
+ var ngModel = probe.directive(NgModel);
+ InputElement inputElement = probe.element;
+
+ ngModel.render('xyz');
+ scope.context['model'] = 'xyz';
+
+ expect(inputElement.value).not.toEqual('xyz');
+
+ scope.apply();
+
+ expect(inputElement.value).toEqual('xyz');
+ });
});
describe('no type attribute', () {
- it('should be set "text" as default value for "type" attribute', inject(() {
+ it('should be set "text" as default value for "type" attribute', () {
_.compile('<input ng-model="model">');
_.rootScope.apply();
expect((_.rootElement as dom.InputElement).attributes['type']).toEqual('text');
- }));
+ });
- it('should update input value from model', inject(() {
+ it('should update input value from model', () {
_.compile('<input ng-model="model">');
_.rootScope.apply();
@@ -389,9 +521,9 @@ void main() {
_.rootScope.apply('model = "misko"');
expect((_.rootElement as dom.InputElement).value).toEqual('misko');
- }));
+ });
- it('should render null as the empty string', inject(() {
+ it('should render null as the empty string', () {
_.compile('<input ng-model="model">');
_.rootScope.apply();
@@ -399,9 +531,9 @@ void main() {
_.rootScope.apply('model = null');
expect((_.rootElement as dom.InputElement).value).toEqual('');
- }));
+ });
- it('should update model from the input value', inject(() {
+ it('should update model from the input value', () {
_.compile('<input ng-model="model" probe="p">');
Probe probe = _.rootScope.context['p'];
var ngModel = probe.directive(NgModel);
@@ -412,41 +544,66 @@ void main() {
expect(_.rootScope.context['model']).toEqual('abc');
inputElement.value = 'def';
- var input = probe.directive(InputTextLikeDirective);
+ var input = probe.directive(InputTextLike);
input.processValue();
expect(_.rootScope.context['model']).toEqual('def');
- }));
+ });
+
+ it('should write to input only if value is different',
+ (Injector i, Animate animate) {
- it('should write to input only if value is different', inject((Injector i, AstParser parser) {
var scope = _.rootScope;
var element = new dom.InputElement();
+ var ngElement = new NgElement(element, scope, animate);
+
NodeAttrs nodeAttrs = new NodeAttrs(new DivElement());
nodeAttrs['ng-model'] = 'model';
- var model = new NgModel(scope, element, i.createChild([new Module()]), new NgNullForm(), parser, nodeAttrs);
+ var model = new NgModel(scope, ngElement, i.createChild([new Module()]),
+ nodeAttrs, new Animate());
dom.querySelector('body').append(element);
- var input = new InputTextLikeDirective(element, model, scope);
+ var input = new InputTextLike(element, model, scope);
element
..value = 'abc'
..selectionStart = 1
..selectionEnd = 2;
- model.render('abc');
+ scope.apply(() {
+ scope.context['model'] = 'abc';
+ });
expect(element.value).toEqual('abc');
expect(element.selectionStart).toEqual(1);
expect(element.selectionEnd).toEqual(2);
- model.render('xyz');
+ scope.apply(() {
+ scope.context['model'] = 'xyz';
+ });
expect(element.value).toEqual('xyz');
expect(element.selectionStart).toEqual(3);
expect(element.selectionEnd).toEqual(3);
- }));
+ });
+
+ it('should only render the input value upon the next digest', (Scope scope) {
+ _.compile('<input ng-model="model" probe="p">');
+ Probe probe = _.rootScope.context['p'];
+ var ngModel = probe.directive(NgModel);
+ InputElement inputElement = probe.element;
+
+ ngModel.render('xyz');
+ scope.context['model'] = 'xyz';
+
+ expect(inputElement.value).not.toEqual('xyz');
+
+ scope.apply();
+
+ expect(inputElement.value).toEqual('xyz');
+ });
});
describe('type="checkbox"', () {
- it('should update input value from model', inject((Scope scope) {
+ it('should update input value from model', (Scope scope) {
var element = _.compile('<input type="checkbox" ng-model="model">');
scope.apply(() {
@@ -458,9 +615,9 @@ void main() {
scope.context['model'] = false;
});
expect(element.checked).toBe(false);
- }));
+ });
- it('should render as dirty when checked', inject((Scope scope) {
+ it('should render as dirty when checked', (Scope scope) {
var element = _.compile('<input type="text" ng-model="my_model" probe="i" />');
Probe probe = _.rootScope.context['i'];
var model = probe.directive(NgModel);
@@ -472,10 +629,9 @@ void main() {
expect(model.pristine).toEqual(false);
expect(model.dirty).toEqual(true);
- }));
-
+ });
- it('should update input value from model using ng-true-value/false', inject((Scope scope) {
+ it('should update input value from model using ng-true-value/false', (Scope scope) {
var element = _.compile('<input type="checkbox" ng-model="model" ng-true-value="1" ng-false-value="0">');
scope.apply(() {
@@ -495,10 +651,9 @@ void main() {
element.checked = false;
_.triggerEvent(element, 'change');
expect(scope.context['model']).toBe(0);
- }));
-
+ });
- it('should allow non boolean values like null, 0, 1', inject((Scope scope) {
+ it('should allow non boolean values like null, 0, 1', (Scope scope) {
var element = _.compile('<input type="checkbox" ng-model="model">');
scope.apply(() {
@@ -515,10 +670,9 @@ void main() {
scope.context['model'] = null;
});
expect(element.checked).toBe(false);
- }));
-
+ });
- it('should update model from the input value', inject((Scope scope) {
+ it('should update model from the input value', (Scope scope) {
var element = _.compile('<input type="checkbox" ng-model="model">');
element.checked = true;
@@ -528,11 +682,44 @@ void main() {
element.checked = false;
_.triggerEvent(element, 'change');
expect(scope.context['model']).toBe(false);
- }));
+ });
+
+ it('should update model from the input using ng-true-value/false', (Scope scope) {
+ var element = _.compile('<input type="checkbox" ng-model="model" '
+ 'ng-true-value="yes" ng-false-value="no">');
+ scope.apply(() {
+ scope.context['yes'] = 'yes sir!';
+ scope.context['no'] = 'no, sorry';
+ });
+
+ element.checked = true;
+ _.triggerEvent(element, 'change');
+ expect(scope.context['model']).toEqual('yes sir!');
+
+ element.checked = false;
+ _.triggerEvent(element, 'change');
+ expect(scope.context['model']).toEqual('no, sorry');
+ });
+
+ it('should only render the input value upon the next digest', (Scope scope) {
+ _.compile('<input type="checkbox" ng-model="model" probe="p">');
+ Probe probe = _.rootScope.context['p'];
+ var ngModel = probe.directive(NgModel);
+ InputElement inputElement = probe.element;
+
+ ngModel.render('xyz');
+ scope.context['model'] = true;
+
+ expect(inputElement.checked).toBe(false);
+
+ scope.apply();
+
+ expect(inputElement.checked).toBe(true);
+ });
});
describe('textarea', () {
- it('should update textarea value from model', inject(() {
+ it('should update textarea value from model', () {
_.compile('<textarea ng-model="model">');
_.rootScope.apply();
@@ -540,9 +727,9 @@ void main() {
_.rootScope.apply('model = "misko"');
expect((_.rootElement as dom.TextAreaElement).value).toEqual('misko');
- }));
+ });
- it('should render null as the empty string', inject(() {
+ it('should render null as the empty string', () {
_.compile('<textarea ng-model="model">');
_.rootScope.apply();
@@ -550,9 +737,9 @@ void main() {
_.rootScope.apply('model = null');
expect((_.rootElement as dom.TextAreaElement).value).toEqual('');
- }));
+ });
- it('should update model from the input value', inject(() {
+ it('should update model from the input value', () {
_.compile('<textarea ng-model="model" probe="p">');
Probe probe = _.rootScope.context['p'];
var ngModel = probe.directive(NgModel);
@@ -563,22 +750,27 @@ void main() {
expect(_.rootScope.context['model']).toEqual('abc');
element.value = 'def';
- var textarea = probe.directive(InputTextLikeDirective);
+ var textarea = probe.directive(InputTextLike);
textarea.processValue();
expect(_.rootScope.context['model']).toEqual('def');
- }));
+ });
// NOTE(deboer): This test passes on Dartium, but fails in the content_shell.
// The Dart team is looking into this bug.
- xit('should write to input only if value is different', inject((Injector i, AstParser parser) {
+ xit('should write to input only if value is different',
+ (Injector i, Animate animate) {
+
var scope = _.rootScope;
var element = new dom.TextAreaElement();
+ var ngElement = new NgElement(element, scope, animate);
+
NodeAttrs nodeAttrs = new NodeAttrs(new DivElement());
nodeAttrs['ng-model'] = 'model';
- var model = new NgModel(scope, element, i.createChild([new Module()]), new NgNullForm(), parser, nodeAttrs);
+ var model = new NgModel(scope, ngElement, i.createChild([new Module()]),
+ nodeAttrs, new Animate());
dom.querySelector('body').append(element);
- var input = new InputTextLikeDirective(element, model, scope);
+ var input = new InputTextLike(element, model, scope);
element
..value = 'abc'
@@ -598,11 +790,27 @@ void main() {
expect(element.value).toEqual('xyz');
expect(element.selectionStart).toEqual(0);
expect(element.selectionEnd).toEqual(0);
- }));
+ });
+
+ it('should only render the input value upon the next digest', (Scope scope) {
+ _.compile('<textarea ng-model="model" probe="p"></textarea>');
+ Probe probe = _.rootScope.context['p'];
+ var ngModel = probe.directive(NgModel);
+ TextAreaElement inputElement = probe.element;
+
+ ngModel.render('xyz');
+ scope.context['model'] = 'xyz';
+
+ expect(inputElement.value).not.toEqual('xyz');
+
+ scope.apply();
+
+ expect(inputElement.value).toEqual('xyz');
+ });
});
describe('type="radio"', () {
- it('should update input value from model', inject(() {
+ it('should update input value from model', () {
_.compile('<input type="radio" name="color" value="red" ng-model="color" probe="r">' +
'<input type="radio" name="color" value="green" ng-model="color" probe="g">' +
'<input type="radio" name="color" value="blue" ng-model="color" probe="b">');
@@ -643,7 +851,7 @@ void main() {
expect(redBtn.checked).toBe(false);
expect(greenBtn.checked).toBe(true);
expect(blueBtn.checked).toBe(false);
- }));
+ });
it('should support ng-value', () {
_.compile('<input type="radio" name="color" ng-value="red" ng-model="color" probe="r">' +
@@ -699,7 +907,7 @@ void main() {
expect(blueBtn.checked).toBe(false);
});
- it('should render as dirty when checked', inject((Scope scope) {
+ it('should render as dirty when checked', (Scope scope) {
var element = _.compile(
'<div>' +
' <input type="radio" id="on" ng-model="my_model" probe="i" value="on" />' +
@@ -713,6 +921,8 @@ void main() {
var input1 = element.querySelector("#on");
var input2 = element.querySelector("#off");
+ scope.apply();
+
expect(model.pristine).toEqual(true);
expect(model.dirty).toEqual(false);
@@ -723,6 +933,7 @@ void main() {
input1.checked = true;
_.triggerEvent(input1, 'click');
+ scope.apply();
expect(model.pristine).toEqual(false);
expect(model.dirty).toEqual(true);
@@ -730,16 +941,45 @@ void main() {
input1.checked = false;
input2.checked = true;
_.triggerEvent(input2, 'click');
+ scope.apply();
expect(input1.classes.contains("ng-dirty")).toBe(true);
expect(input2.classes.contains("ng-dirty")).toBe(true);
expect(input1.classes.contains("ng-pristine")).toBe(false);
expect(input1.classes.contains("ng-pristine")).toBe(false);
- }));
+ });
+
+ it('should only render the input value upon the next digest', (Scope scope) {
+ var element = _.compile(
+ '<div>' +
+ ' <input type="radio" id="on" ng-model="model" probe="i" value="on" />' +
+ ' <input type="radio" id="off" ng-model="model" probe="j" value="off" />' +
+ '</div>'
+ );
+
+ Probe probe1 = _.rootScope.context['i'];
+ var ngModel1 = probe1.directive(NgModel);
+ InputElement inputElement1 = probe1.element;
+
+ Probe probe2 = _.rootScope.context['j'];
+ var ngModel2 = probe2.directive(NgModel);
+ InputElement inputElement2 = probe2.element;
+
+ ngModel1.render('on');
+ scope.context['model'] = 'on';
+
+ expect(inputElement1.checked).toBe(false);
+ expect(inputElement2.checked).toBe(false);
+
+ scope.apply();
+
+ expect(inputElement1.checked).toBe(true);
+ expect(inputElement2.checked).toBe(false);
+ });
});
describe('type="search"', () {
- it('should update input value from model', inject(() {
+ it('should update input value from model', () {
_.compile('<input type="search" ng-model="model">');
_.rootScope.apply();
@@ -747,9 +987,9 @@ void main() {
_.rootScope.apply('model = "matias"');
expect((_.rootElement as dom.InputElement).value).toEqual('matias');
- }));
+ });
- it('should render null as the empty string', inject(() {
+ it('should render null as the empty string', () {
_.compile('<input type="search" ng-model="model">');
_.rootScope.apply();
@@ -757,9 +997,9 @@ void main() {
_.rootScope.apply('model = null');
expect((_.rootElement as dom.InputElement).value).toEqual('');
- }));
+ });
- it('should update model from the input value', inject(() {
+ it('should update model from the input value', () {
_.compile('<input type="search" ng-model="model" probe="p">');
Probe probe = _.rootScope.context['p'];
var ngModel = probe.directive(NgModel);
@@ -770,14 +1010,30 @@ void main() {
expect(_.rootScope.context['model']).toEqual('xzy');
inputElement.value = '123';
- var input = probe.directive(InputTextLikeDirective);
+ var input = probe.directive(InputTextLike);
input.processValue();
expect(_.rootScope.context['model']).toEqual('123');
- }));
+ });
+
+ it('should only render the input value upon the next digest', (Scope scope) {
+ _.compile('<input type="search" ng-model="model" probe="p">');
+ Probe probe = _.rootScope.context['p'];
+ var ngModel = probe.directive(NgModel);
+ InputElement inputElement = probe.element;
+
+ ngModel.render('xyz');
+ scope.context['model'] = 'xyz';
+
+ expect(inputElement.value).not.toEqual('xyz');
+
+ scope.apply();
+
+ expect(inputElement.value).toEqual('xyz');
+ });
});
describe('contenteditable', () {
- it('should update content from model', inject(() {
+ it('should update content from model', () {
_.compile('<p contenteditable ng-model="model">');
_.rootScope.apply();
@@ -785,9 +1041,9 @@ void main() {
_.rootScope.apply('model = "misko"');
expect(_.rootElement.text).toEqual('misko');
- }));
+ });
- it('should update model from the input value', inject(() {
+ it('should update model from the input value', () {
_.compile('<p contenteditable ng-model="model">');
Element element = _.rootElement;
@@ -796,42 +1052,65 @@ void main() {
expect(_.rootScope.context['model']).toEqual('abc');
element.innerHtml = 'def';
- var input = ngInjector(element).get(ContentEditableDirective);
+ var input = ngInjector(element).get(ContentEditable);
input.processValue();
expect(_.rootScope.context['model']).toEqual('def');
- }));
+ });
+
+ it('should only render the input value upon the next digest', (Scope scope) {
+ _.compile('<div contenteditable ng-model="model" probe="p"></div>');
+ Probe probe = _.rootScope.context['p'];
+ var ngModel = probe.directive(NgModel);
+ Element element = probe.element;
+
+ ngModel.render('xyz');
+ scope.context['model'] = 'xyz';
+
+ expect(element.innerHtml).not.toEqual('xyz');
+
+ scope.apply();
+
+ expect(element.innerHtml).toEqual('xyz');
+ });
});
describe('pristine / dirty', () {
- it('should be set to pristine by default', inject((Scope scope) {
+ it('should be set to pristine by default', (Scope scope) {
_.compile('<input type="text" ng-model="my_model" probe="i" />');
Probe probe = _.rootScope.context['i'];
var model = probe.directive(NgModel);
expect(model.pristine).toEqual(true);
expect(model.dirty).toEqual(false);
- }));
+ });
- it('should add and remove the correct CSS classes when set to dirty and to pristine', inject((Scope scope) {
+ it('should add and remove the correct CSS classes when set to dirty and to pristine', (Scope scope) {
_.compile('<input type="text" ng-model="my_model" probe="i" />');
Probe probe = _.rootScope.context['i'];
NgModel model = probe.directive(NgModel);
InputElement element = probe.element;
- model.dirty = true;
+ model.addInfo('ng-dirty');
+ scope.apply();
+
expect(model.pristine).toEqual(false);
expect(model.dirty).toEqual(true);
- expect(element.classes.contains('ng-pristine')).toBe(false);
- expect(element.classes.contains('ng-dirty')).toBe(true);
+ expect(element).not.toHaveClass('ng-pristine');
+ expect(element).toHaveClass('ng-dirty');
+
+ model.removeInfo('ng-dirty');
+ scope.apply();
- model.pristine = true;
expect(model.pristine).toEqual(true);
expect(model.dirty).toEqual(false);
- expect(element.classes.contains('ng-pristine')).toBe(true);
- expect(element.classes.contains('ng-dirty')).toBe(false);
- }));
+ expect(element).toHaveClass('ng-pristine');
+ expect(element).not.toHaveClass('ng-dirty');
+ });
+
+ // TODO(matias): figure out why the 2nd apply is optional
+ it('should render the parent form/fieldset as dirty but not the other models',
+ (Scope scope) {
- it('should render the parent form/fieldset as dirty but not the other models', inject((Scope scope) {
_.compile('<form name="myForm">' +
' <fieldset name="myFieldset">' +
' <input type="text" ng-model="my_model1" probe="myModel1" />' +
@@ -839,42 +1118,45 @@ void main() {
' </fieldset>' +
'</form>');
+ var formElement = _.rootScope.context['myForm'].element.node;
+ var fieldsetElement = _.rootScope.context['myFieldset'].element.node;
var inputElement1 = _.rootScope.context['myModel1'].element;
var inputElement2 = _.rootScope.context['myModel2'].element;
- var formElement = _.rootScope.context['myForm'].element;
- var fieldsetElement = _.rootScope.context['myFieldset'].element;
- expect(formElement.classes.contains('ng-pristine')).toBe(true);
- expect(formElement.classes.contains('ng-dirty')).toBe(false);
+ scope.apply();
- expect(fieldsetElement.classes.contains('ng-pristine')).toBe(true);
- expect(fieldsetElement.classes.contains('ng-dirty')).toBe(false);
+ expect(formElement).toHaveClass('ng-pristine');
+ expect(formElement).not.toHaveClass('ng-dirty');
- expect(inputElement1.classes.contains('ng-pristine')).toBe(true);
- expect(inputElement1.classes.contains('ng-dirty')).toBe(false);
+ expect(fieldsetElement).toHaveClass('ng-pristine');
+ expect(fieldsetElement).not.toHaveClass('ng-dirty');
- expect(inputElement2.classes.contains('ng-pristine')).toBe(true);
- expect(inputElement2.classes.contains('ng-dirty')).toBe(false);
+ expect(inputElement1).toHaveClass('ng-pristine');
+ expect(inputElement1).not.toHaveClass('ng-dirty');
+
+ expect(inputElement2).toHaveClass('ng-pristine');
+ expect(inputElement2).not.toHaveClass('ng-dirty');
inputElement1.value = '...hi...';
_.triggerEvent(inputElement1, 'change');
+ scope.apply();
- expect(formElement.classes.contains('ng-pristine')).toBe(false);
- expect(formElement.classes.contains('ng-dirty')).toBe(true);
+ expect(formElement).not.toHaveClass('ng-pristine');
+ expect(formElement).toHaveClass('ng-dirty');
- expect(fieldsetElement.classes.contains('ng-pristine')).toBe(false);
- expect(fieldsetElement.classes.contains('ng-dirty')).toBe(true);
+ expect(fieldsetElement).not.toHaveClass('ng-pristine');
+ expect(fieldsetElement).toHaveClass('ng-dirty');
- expect(inputElement1.classes.contains('ng-pristine')).toBe(false);
- expect(inputElement1.classes.contains('ng-dirty')).toBe(true);
+ expect(inputElement1).not.toHaveClass('ng-pristine');
+ expect(inputElement1).toHaveClass('ng-dirty');
- expect(inputElement2.classes.contains('ng-pristine')).toBe(true);
- expect(inputElement2.classes.contains('ng-dirty')).toBe(false);
- }));
+ expect(inputElement2).toHaveClass('ng-pristine');
+ expect(inputElement2).not.toHaveClass('ng-dirty');
+ });
});
describe('validation', () {
- it('should happen automatically when the scope changes', inject((Scope scope) {
+ it('should happen automatically when the scope changes', (Scope scope) {
_.compile('<input type="text" ng-model="model" probe="i" required>');
_.rootScope.apply();
@@ -888,14 +1170,15 @@ void main() {
expect(model.invalid).toBe(false);
expect(model.valid).toBe(true);
- }));
+ });
- it('should happen automatically upon user input via the onInput event', inject(() {
+ it('should happen automatically upon user input via the onInput event', () {
_.compile('<input type="text" ng-model="model" probe="i" required>');
+ _.rootScope.apply();
Probe probe = _.rootScope.context['i'];
var model = probe.directive(NgModel);
- InputElement inputElement = model.element;
+ InputElement inputElement = model.element.node;
expect(model.invalid).toBe(true);
expect(model.valid).toBe(false);
@@ -905,72 +1188,100 @@ void main() {
expect(model.invalid).toBe(false);
expect(model.valid).toBe(true);
- }));
+ });
});
describe('valid / invalid', () {
- it('should add and remove the correct flags when set to valid and to invalid', inject((Scope scope) {
+ it('should add and remove the correct flags when set to valid and to invalid', (Scope scope) {
_.compile('<input type="text" ng-model="my_model" probe="i" />');
Probe probe = _.rootScope.context['i'];
var model = probe.directive(NgModel);
InputElement element = probe.element;
- model.invalid = true;
+ model.addError('ng-required');
+ model.validate();
+ scope.apply();
+
expect(model.valid).toEqual(false);
expect(model.invalid).toEqual(true);
- expect(element.classes.contains('ng-valid')).toBe(false);
- expect(element.classes.contains('ng-invalid')).toBe(true);
+ //expect(element).not.toHaveClass('ng-valid');
+ expect(element).toHaveClass('ng-invalid');
+
+ model.removeError('ng-required');
+ model.validate();
+ scope.apply();
- model.valid = true;
expect(model.valid).toEqual(true);
expect(model.invalid).toEqual(false);
- expect(element.classes.contains('ng-invalid')).toBe(false);
- expect(element.classes.contains('ng-valid')).toBe(true);
- }));
+ expect(element).not.toHaveClass('ng-invalid');
+ // expect(element).toHaveClass('ng-valid');
+ });
- it('should set the validity with respect to all existing validations when setValidity() is used', inject((Scope scope) {
+ it('should set the validity with respect to all existing validations when setValidity() is used', (Scope scope) {
_.compile('<input type="text" ng-model="my_model" probe="i" />');
Probe probe = _.rootScope.context['i'];
var model = probe.directive(NgModel);
- model.setValidity("required", false);
+ model.addError("required");
expect(model.valid).toEqual(false);
expect(model.invalid).toEqual(true);
- model.setValidity("format", false);
+ model.addError("format");
expect(model.valid).toEqual(false);
expect(model.invalid).toEqual(true);
- model.setValidity("format", true);
+ model.removeError("format");
expect(model.valid).toEqual(false);
expect(model.invalid).toEqual(true);
- model.setValidity("required", true);
+ model.removeError("required");
expect(model.valid).toEqual(true);
expect(model.invalid).toEqual(false);
- }));
+ });
- it('should register each error only once when invalid', inject((Scope scope) {
+ it('should register each error only once when invalid', (Scope scope) {
_.compile('<input type="text" ng-model="my_model" probe="i" />');
Probe probe = _.rootScope.context['i'];
var model = probe.directive(NgModel);
- model.setValidity("distinct-error", false);
+ model.addError("distinct-error");
expect(model.valid).toEqual(false);
expect(model.invalid).toEqual(true);
- model.setValidity("distinct-error", false);
+ model.addError("distinct-error");
expect(model.valid).toEqual(false);
expect(model.invalid).toEqual(true);
- model.setValidity("distinct-error", true);
+ model.removeError("distinct-error");
expect(model.valid).toEqual(true);
expect(model.invalid).toEqual(false);
- }));
+ });
+ });
+
+ describe('error handling', () {
+ it('should return true or false depending on if an error exists on a form',
+ (Scope scope, TestBed _) {
+
+ _.compile('<input type="text" ng-model="input" name="input" probe="i" />');
+ scope.apply();
+
+ Probe p = scope.context['i'];
+ NgModel model = p.directive(NgModel);
+
+ expect(model.hasErrorState('big-failure')).toBe(false);
+
+ model.addError("big-failure");
+
+ expect(model.hasErrorState('big-failure')).toBe(true);
+
+ model.removeError("big-failure");
+
+ expect(model.hasErrorState('big-failure')).toBe(false);
+ });
});
describe('text-like events', () {
- it('should update the binding on the "input" event', inject(() {
+ it('should update the binding on the "input" event', () {
_.compile('<input type="text" ng-model="model" probe="p">');
Probe probe = _.rootScope.context['p'];
InputElement inputElement = probe.element;
@@ -982,7 +1293,7 @@ void main() {
_.triggerEvent(inputElement, 'input');
expect(_.rootScope.context['model']).toEqual('waaaah');
- }));
+ });
});
describe('error messages', () {
@@ -1043,12 +1354,264 @@ void main() {
expect(model.touched).toBe(false);
expect(model.untouched).toBe(true);
});
+
+ describe('validators', () {
+ it('should display the valid and invalid CSS classes on the element for each validation',
+ (TestBed _, Scope scope) {
+
+ var input = _.compile('<input type="email" ng-model="myModel" />');
+
+ scope.apply(() {
+ scope.context['myModel'] = 'value';
+ });
+
+ expect(input).toHaveClass('ng-email-invalid');
+ expect(input).not.toHaveClass('ng-email-valid');
+
+ scope.apply(() {
+ scope.context['myModel'] = 'value@email.com';
+ });
+
+ expect(input).toHaveClass('ng-email-valid');
+ expect(input).not.toHaveClass('ng-email-invalid');
+ });
+
+ it('should display the valid and invalid CSS classes on the element for custom validations',
+ (TestBed _, Scope scope) {
+
+ var input = _.compile('<input type="text" ng-model="myModel" custom-input-validation />');
+
+ scope.apply();
+
+ expect(input).toHaveClass('custom-invalid');
+ expect(input).not.toHaveClass('custom-valid');
+
+ scope.apply(() {
+ scope.context['myModel'] = 'yes';
+ });
+
+ expect(input).toHaveClass('custom-valid');
+ expect(input).not.toHaveClass('custom-invalid');
+ });
+
+ it('should only validate twice during compilation and once upon scope digest',
+ (TestBed _, Scope scope) {
+
+ scope.context['required'] = true;
+ _.compile('<input type="text" '
+ 'ng-model="model" '
+ 'ng-required="required" '
+ 'ng-pattern="pattern" '
+ 'counting-validator '
+ 'probe="i">');
+
+ scope.context['pattern'] = '^[aeiou]+\$';
+ scope.context['required'] = true;
+
+ scope.apply();
+
+ var model = scope.context['i'].directive(NgModel);
+ var counter = model.validators.firstWhere((validator) => validator.name == 'counting');
+
+ // TODO(#881): There is a bug in ngModel where the validators are validated too often.
+ // Should be 2. One for ngModel and one for all the other ones
+ // Currently, this count is 2 on Chrome and 3 on Firefox.
+ expect(counter.count == 2 || counter.count == 3).toBe(true);
+ expect(model.invalid).toBe(true);
+
+ counter.count = 0;
+ scope.context['pattern'] = '';
+ scope.context['required'] = false;
+ scope.apply();
+
+ expect(counter.count).toBe(1);
+ });
+
+ it('should only validate twice regardless of attribute order', (TestBed _, Scope scope) {
+ scope.context['required'] = true;
+ _.compile('<input type="text" '
+ 'ng-required="required" '
+ 'ng-pattern="pattern" '
+ 'counting-validator '
+ 'ng-model="model" '
+ 'probe="i">');
+
+ scope.context['pattern'] = '^[aeiou]+\$';
+ scope.context['required'] = true;
+
+ scope.apply();
+
+ var model = scope.context['i'].directive(NgModel);
+
+ var counter = model.validators.firstWhere((validator) => validator.name == 'counting');
+
+ // TODO(#881): There is a bug in ngModel where the validators are validated too often.
+ // Should be 2. One for ngModel and one for all the other ones
+ // Currently, this count is 3 on Chrome and 1 on Firefox.
+ expect(counter.count == 2 || counter.count == 3).toBe(true);
+ });
+ });
+
+ describe('converters', () {
+ it('should parse the model value according to the given parser', (Scope scope) {
+ _.compile('<input type="text" ng-model="model" probe="i">');
+ scope.apply();
+
+ var probe = scope.context['i'];
+ var input = probe.element;
+ var model = probe.directive(NgModel);
+ model.converter = new LowercaseValueParser();
+
+ input.value = 'HELLO';
+ _.triggerEvent(input, 'change');
+ _.rootScope.apply();
+
+ expect(model.viewValue).toEqual('HELLO');
+ expect(model.modelValue).toEqual('hello');
+ });
+
+ it('should format the model value according to the given formatter', (Scope scope) {
+ _.compile('<input type="text" ng-model="model" probe="i">');
+ scope.apply();
+
+ var probe = scope.context['i'];
+ var input = probe.element;
+ var model = probe.directive(NgModel);
+ model.converter = new UppercaseValueFormatter();
+
+ scope.apply(() {
+ scope.context['model'] = 'greetings';
+ });
+
+ expect(model.viewValue).toEqual('GREETINGS');
+ expect(model.modelValue).toEqual('greetings');
+ });
+
+ it('should retain the current input value if the parser fails', (Scope scope) {
+ _.compile('<form name="myForm">' +
+ ' <input type="text" ng-model="model1" name="myModel1" probe="i">' +
+ ' <input type="text" ng-model="model2" name="myModel2" probe="j">' +
+ '</form>');
+ scope.apply();
+
+ var probe1 = scope.context['i'];
+ var input1 = probe1.element;
+ var model1 = probe1.directive(NgModel);
+
+ var probe2 = scope.context['j'];
+ var input2 = probe2.element;
+ var model2 = probe2.directive(NgModel);
+
+ model1.converter = new FailedValueParser();
+
+ input1.value = '123';
+ _.triggerEvent(input1, 'change');
+ _.rootScope.apply();
+
+ expect(model1.viewValue).toEqual('123');
+ expect(input1.value).toEqual('123');
+ expect(model1.modelValue).toEqual(null);
+
+ expect(model2.viewValue).toEqual(null);
+ expect(input2.value).toEqual('');
+ expect(model2.modelValue).toEqual(null);
+ });
+
+ it('should reformat the viewValue when the formatter is changed', (Scope scope) {
+ _.compile('<input type="text" ng-model="model" probe="i">');
+ scope.apply();
+
+ var probe = scope.context['i'];
+ var input = probe.element;
+ var model = probe.directive(NgModel);
+ model.converter = new LowercaseValueParser();
+
+ input.value = 'HI THERE';
+ _.triggerEvent(input, 'change');
+ _.rootScope.apply();
+
+ expect(model.viewValue).toEqual('HI THERE');
+ expect(model.modelValue).toEqual('hi there');
+
+ model.converter = new VowelValueParser();
+
+ expect(model.viewValue).toEqual('iee');
+ expect(model.modelValue).toEqual('hi there');
+ });
+ });
});
}
-@NgController(
+@Controller(
selector: '[no-love]',
publishAs: 'ctrl')
class ControllerWithNoLove {
var apathy = null;
}
+
+class LowercaseValueParser implements NgModelConverter {
+ final name = 'lowercase';
+ format(value) => value;
+ parse(value) {
+ return value != null ? value.toLowerCase() : null;
+ }
+}
+
+class UppercaseValueFormatter implements NgModelConverter {
+ final name = 'uppercase';
+ parse(value) => value;
+ format(value) {
+ return value != null ? value.toUpperCase() : null;
+ }
+}
+
+class FailedValueParser implements NgModelConverter {
+ final name = 'failed';
+ format(value) => value;
+ parse(value) {
+ throw new Exception();
+ }
+}
+
+class VowelValueParser implements NgModelConverter {
+ final name = 'vowel';
+ parse(value) => value;
+ format(value) {
+ if(value != null) {
+ var exp = new RegExp("[^aeiouAEIOU]");
+ value = value.replaceAll(exp, "");
+ }
+ return value;
+ }
+}
+
+@Decorator(
+ selector: '[custom-input-validation]')
+class MyCustomInputValidator extends NgValidator {
+ MyCustomInputValidator(NgModel ngModel) {
+ ngModel.addValidator(this);
+ }
+
+ final String name = 'custom';
+
+ bool isValid(name) {
+ return name != null && name == 'yes';
+ }
+}
+
+@Decorator(
+ selector: '[counting-validator]')
+class CountingValidator extends NgValidator {
+
+ final String name = 'counting';
+ int count = 0;
+
+ CountingValidator(NgModel ngModel) {
+ ngModel.addValidator(this);
+ }
+
+ bool isValid(String modelValue) {
+ count++;
+ return true;
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698