diff --git a/lib/directive/ng_model.dart b/lib/directive/ng_model.dart
index 33454b685..0b9bcd411 100644
--- a/lib/directive/ng_model.dart
+++ b/lib/directive/ng_model.dart
@@ -40,6 +40,8 @@ class NgModel extends NgControl implements AttachAware {
Watch _watch;
bool _watchCollection;
+ bool stringBasedInput = false;
+
NgModel(this._scope, NgElement element, DirectiveInjector injector, NodeAttrs attrs,
Animate animate, ElementProbe probe)
: super(element, injector, animate)
@@ -196,6 +198,12 @@ class NgModel extends NgControl implements AttachAware {
get viewValue => _viewValue;
void set viewValue(value) {
+ if (stringBasedInput && value != null) {
+ // string-based input types will always deal with string-based values.
+ // Therefore there is no reason to allow other types to be set
+ // as the view value
+ value = value.toString();
+ }
_viewValue = value;
modelValue = value;
}
@@ -203,6 +211,9 @@ class NgModel extends NgControl implements AttachAware {
get modelValue => _modelValue;
void set modelValue(value) {
try {
+ if (stringBasedInput && value != null) {
+ value = value.toString();
+ }
value = converter.parse(value);
} catch(e) {
value = null;
@@ -364,6 +375,7 @@ class InputTextLike {
}
InputTextLike(this.inputElement, this.ngModel, this.scope, this.ngModelOptions) {
+ ngModel.stringBasedInput = true;
ngModel.render = (value) {
scope.rootScope.domWrite(() {
if (value == null) value = '';
@@ -824,7 +836,9 @@ class InputRadio {
@Decorator(selector: '[contenteditable][ng-model]')
class ContentEditable extends InputTextLike {
ContentEditable(dom.Element inputElement, NgModel ngModel, Scope scope, NgModelOptions modelOptions)
- : super(inputElement, ngModel, scope, modelOptions);
+ : super(inputElement, ngModel, scope, modelOptions) {
+ ngModel.stringBasedInput = true;
+ }
// The implementation is identical to InputTextLike but use innerHtml instead of value
String get typedValue => (inputElement as dynamic).innerHtml;
diff --git a/test/directive/ng_model_spec.dart b/test/directive/ng_model_spec.dart
index d97fb3f07..e63a21585 100644
--- a/test/directive/ng_model_spec.dart
+++ b/test/directive/ng_model_spec.dart
@@ -1583,6 +1583,39 @@ void main() {
expect(model.viewValue).toEqual('iee');
expect(model.modelValue).toEqual('hi there');
});
+
+ it('should always provide the parser input as a string for string-based inputs', (Scope scope) {
+ _.compile('');
+ scope.apply();
+
+ var probe = scope.context['i'];
+ var input = probe.element;
+ var model = probe.directive(NgModel);
+
+ model.converter = new StringAssertConverter();
+ model.viewValue = new Date();
+ _.rootScope.apply();
+
+ expect(model.converter.isParsedString).toBe(true);
+ });
+
+ it('should always render the initial value as a string for string-based inputs', (Scope scope) {
+ scope.context['model'] = new Date();
+
+ _.compile('');
+ scope.apply();
+
+ var probe = scope.context['i'];
+ var input = probe.element;
+ var model = probe.directive(NgModel);
+
+ model.converter = new StringAssertConverter();
+ _.rootScope.apply();
+
+ model.reset();
+ _.rootScope.apply();
+ expect(model.converter.isParsedString).toBe(true);
+ });
});
});
}
@@ -1593,6 +1626,17 @@ void main() {
class ComponentWithNoLove {
}
+class StringAssertConverter implements NgModelConverter {
+ final name = 'stringAssert';
+ bool isParsedString = false;
+
+ format(value) => value;
+ parse(value) {
+ isParsedString = !!(value is String);
+ return value;
+ }
+}
+
class LowercaseValueParser implements NgModelConverter {
final name = 'lowercase';
format(value) => value;