Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.

fix(ngModel): ensure that string-based inputs always provide string values to parsers #1640

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion lib/directive/ng_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -196,13 +198,22 @@ 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;
}

get modelValue => _modelValue;
void set modelValue(value) {
try {
if (stringBasedInput && value != null) {
value = value.toString();
}
value = converter.parse(value);
} catch(e) {
value = null;
Expand Down Expand Up @@ -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 = '';
Expand Down Expand Up @@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

super call handles that.

}

// The implementation is identical to InputTextLike but use innerHtml instead of value
String get typedValue => (inputElement as dynamic).innerHtml;
Expand Down
44 changes: 44 additions & 0 deletions test/directive/ng_model_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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('<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 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('<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 StringAssertConverter();
_.rootScope.apply();

model.reset();
_.rootScope.apply();
expect(model.converter.isParsedString).toBe(true);
});
});
});
}
Expand All @@ -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;
Expand Down