fix(forms): use strict runtimeType checks instead of instanceof

Currently, validators extending built-in validators are treated as built-in.
This can result in an error when both a real built-in validator and a custom one are applied to the same element.

Closes #6981
This commit is contained in:
vsavkin 2016-02-09 17:46:38 -08:00 committed by Brian Ford
parent 8f47aa3530
commit 50548fb565
4 changed files with 27 additions and 5 deletions

View File

@ -1,5 +1,5 @@
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection'; import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {isBlank, isPresent, looseIdentical} from 'angular2/src/facade/lang'; import {isBlank, isPresent, looseIdentical, hasConstructor} from 'angular2/src/facade/lang';
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions'; import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
import {ControlContainer} from './control_container'; import {ControlContainer} from './control_container';
@ -82,11 +82,13 @@ export function selectValueAccessor(dir: NgControl,
var builtinAccessor; var builtinAccessor;
var customAccessor; var customAccessor;
valueAccessors.forEach(v => { valueAccessors.forEach(v => {
if (v instanceof DefaultValueAccessor) { if (hasConstructor(v, DefaultValueAccessor)) {
defaultAccessor = v; defaultAccessor = v;
} else if (v instanceof CheckboxControlValueAccessor || v instanceof NumberValueAccessor || } else if (hasConstructor(v, CheckboxControlValueAccessor) ||
v instanceof SelectControlValueAccessor || v instanceof RadioControlValueAccessor) { hasConstructor(v, NumberValueAccessor) ||
hasConstructor(v, SelectControlValueAccessor) ||
hasConstructor(v, RadioControlValueAccessor)) {
if (isPresent(builtinAccessor)) if (isPresent(builtinAccessor))
_throwError(dir, "More than one built-in value accessor matches"); _throwError(dir, "More than one built-in value accessor matches");
builtinAccessor = v; builtinAccessor = v;

View File

@ -353,3 +353,7 @@ var global = null;
dynamic evalExpression(String sourceUrl, String expr, String declarations, Map<String, String> vars) { dynamic evalExpression(String sourceUrl, String expr, String declarations, Map<String, String> vars) {
throw "Dart does not support evaluating expression during runtime!"; throw "Dart does not support evaluating expression during runtime!";
} }
bool hasConstructor(Object value, Type type) {
return value.runtimeType == type;
}

View File

@ -464,3 +464,7 @@ export function evalExpression(sourceUrl: string, expr: string, declarations: st
export function isPrimitive(obj: any): boolean { export function isPrimitive(obj: any): boolean {
return !isJsObject(obj); return !isJsObject(obj);
} }
export function hasConstructor(value: Object, type: Type): boolean {
return value.constructor === type;
}

View File

@ -4,9 +4,13 @@ import {
RegExpWrapper, RegExpWrapper,
RegExpMatcherWrapper, RegExpMatcherWrapper,
StringWrapper, StringWrapper,
CONST_EXPR CONST_EXPR,
hasConstructor
} from 'angular2/src/facade/lang'; } from 'angular2/src/facade/lang';
class MySuperclass {}
class MySubclass extends MySuperclass {}
export function main() { export function main() {
describe('RegExp', () => { describe('RegExp', () => {
it('should expose the index for each match', () => { it('should expose the index for each match', () => {
@ -119,5 +123,13 @@ export function main() {
expect(StringWrapper.stripRight(null, "S")).toEqual(null); expect(StringWrapper.stripRight(null, "S")).toEqual(null);
}); });
}); });
describe('hasConstructor', () => {
it("should be true when the type matches",
() => { expect(hasConstructor(new MySuperclass(), MySuperclass)).toEqual(true); });
it("should be false for subtypes",
() => { expect(hasConstructor(new MySubclass(), MySuperclass)).toEqual(false); });
});
}); });
} }