parent
afda43dc02
commit
b39d2c0101
|
@ -6,6 +6,7 @@ import {Decorator, Component, Viewport} from '../../annotations/annotations';
|
||||||
import {ElementBinder} from '../element_binder';
|
import {ElementBinder} from '../element_binder';
|
||||||
import {ProtoElementInjector} from '../element_injector';
|
import {ProtoElementInjector} from '../element_injector';
|
||||||
import {ProtoView} from '../view';
|
import {ProtoView} from '../view';
|
||||||
|
import {dashCaseToCamelCase} from './util';
|
||||||
|
|
||||||
import {AST} from 'angular2/change_detection';
|
import {AST} from 'angular2/change_detection';
|
||||||
|
|
||||||
|
@ -114,7 +115,7 @@ export class CompileElement {
|
||||||
if (isBlank(this.propertyBindings)) {
|
if (isBlank(this.propertyBindings)) {
|
||||||
this.propertyBindings = MapWrapper.create();
|
this.propertyBindings = MapWrapper.create();
|
||||||
}
|
}
|
||||||
MapWrapper.set(this.propertyBindings, property, expression);
|
MapWrapper.set(this.propertyBindings, dashCaseToCamelCase(property), expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
addVariableBinding(variableName:string, variableValue:string) {
|
addVariableBinding(variableName:string, variableValue:string) {
|
||||||
|
|
|
@ -10,7 +10,8 @@ import {CompileStep} from './compile_step';
|
||||||
import {CompileElement} from './compile_element';
|
import {CompileElement} from './compile_element';
|
||||||
import {CompileControl} from './compile_control';
|
import {CompileControl} from './compile_control';
|
||||||
|
|
||||||
import {isSpecialProperty} from './element_binder_builder';;
|
import {isSpecialProperty} from './element_binder_builder';
|
||||||
|
import {dashCaseToCamelCase, camelCaseToDashCase} from './util';
|
||||||
|
|
||||||
var PROPERTY_BINDING_REGEXP = RegExpWrapper.create('^ *([^\\s\\|]+)');
|
var PROPERTY_BINDING_REGEXP = RegExpWrapper.create('^ *([^\\s\\|]+)');
|
||||||
|
|
||||||
|
@ -84,7 +85,7 @@ function updateMatchedProperties(matchedProperties, selector, directive) {
|
||||||
if (isPresent(attrs)) {
|
if (isPresent(attrs)) {
|
||||||
for (var idx = 0; idx<attrs.length; idx+=2) {
|
for (var idx = 0; idx<attrs.length; idx+=2) {
|
||||||
// attribute name is stored on even indexes
|
// attribute name is stored on even indexes
|
||||||
StringMapWrapper.set(matchedProperties, attrs[idx], true);
|
StringMapWrapper.set(matchedProperties, dashCaseToCamelCase(attrs[idx]), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// some properties can be used by the directive, so we need to register them
|
// some properties can be used by the directive, so we need to register them
|
||||||
|
@ -97,7 +98,7 @@ function updateMatchedProperties(matchedProperties, selector, directive) {
|
||||||
// keep the property name and remove the pipe
|
// keep the property name and remove the pipe
|
||||||
var bindProp = RegExpWrapper.firstMatch(PROPERTY_BINDING_REGEXP, value);
|
var bindProp = RegExpWrapper.firstMatch(PROPERTY_BINDING_REGEXP, value);
|
||||||
if (isPresent(bindProp) && isPresent(bindProp[1])) {
|
if (isPresent(bindProp) && isPresent(bindProp[1])) {
|
||||||
StringMapWrapper.set(matchedProperties, bindProp[1], true);
|
StringMapWrapper.set(matchedProperties, dashCaseToCamelCase(bindProp[1]), true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -130,7 +131,7 @@ function checkMissingDirectives(current, matchedProperties, isTemplateElement) {
|
||||||
MapWrapper.forEach(ppBindings, (expression, prop) => {
|
MapWrapper.forEach(ppBindings, (expression, prop) => {
|
||||||
if (!DOM.hasProperty(current.element, prop) && !isSpecialProperty(prop)) {
|
if (!DOM.hasProperty(current.element, prop) && !isSpecialProperty(prop)) {
|
||||||
if (!isPresent(matchedProperties) || !isPresent(StringMapWrapper.get(matchedProperties, prop))) {
|
if (!isPresent(matchedProperties) || !isPresent(StringMapWrapper.get(matchedProperties, prop))) {
|
||||||
throw new BaseException(`Missing directive to handle '${prop}' in ${current.elementDescription}`);
|
throw new BaseException(`Missing directive to handle '${camelCaseToDashCase(prop)}' in ${current.elementDescription}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,21 +11,24 @@ import {DirectiveMetadata} from '../directive_metadata';
|
||||||
import {CompileStep} from './compile_step';
|
import {CompileStep} from './compile_step';
|
||||||
import {CompileElement} from './compile_element';
|
import {CompileElement} from './compile_element';
|
||||||
import {CompileControl} from './compile_control';
|
import {CompileControl} from './compile_control';
|
||||||
|
import {dashCaseToCamelCase, camelCaseToDashCase} from './util';
|
||||||
|
|
||||||
var DOT_REGEXP = RegExpWrapper.create('\\.');
|
var DOT_REGEXP = RegExpWrapper.create('\\.');
|
||||||
|
|
||||||
const ARIA_PREFIX = 'aria-';
|
const ARIA_PREFIX = 'aria';
|
||||||
var ariaSettersCache = StringMapWrapper.create();
|
var ariaSettersCache = StringMapWrapper.create();
|
||||||
|
|
||||||
function ariaSetterFactory(attrName:string) {
|
function ariaSetterFactory(attrName:string) {
|
||||||
var setterFn = StringMapWrapper.get(ariaSettersCache, attrName);
|
var setterFn = StringMapWrapper.get(ariaSettersCache, attrName);
|
||||||
|
var ariaAttrName;
|
||||||
|
|
||||||
if (isBlank(setterFn)) {
|
if (isBlank(setterFn)) {
|
||||||
|
ariaAttrName = camelCaseToDashCase(attrName);
|
||||||
setterFn = function(element, value) {
|
setterFn = function(element, value) {
|
||||||
if (isPresent(value)) {
|
if (isPresent(value)) {
|
||||||
DOM.setAttribute(element, attrName, stringify(value));
|
DOM.setAttribute(element, ariaAttrName, stringify(value));
|
||||||
} else {
|
} else {
|
||||||
DOM.removeAttribute(element, attrName);
|
DOM.removeAttribute(element, ariaAttrName);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
StringMapWrapper.set(ariaSettersCache, attrName, setterFn);
|
StringMapWrapper.set(ariaSettersCache, attrName, setterFn);
|
||||||
|
@ -34,7 +37,6 @@ function ariaSetterFactory(attrName:string) {
|
||||||
return setterFn;
|
return setterFn;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CLASS_ATTR = 'class';
|
|
||||||
const CLASS_PREFIX = 'class.';
|
const CLASS_PREFIX = 'class.';
|
||||||
var classSettersCache = StringMapWrapper.create();
|
var classSettersCache = StringMapWrapper.create();
|
||||||
|
|
||||||
|
@ -55,22 +57,23 @@ function classSetterFactory(className:string) {
|
||||||
return setterFn;
|
return setterFn;
|
||||||
}
|
}
|
||||||
|
|
||||||
const STYLE_ATTR = 'style';
|
|
||||||
const STYLE_PREFIX = 'style.';
|
const STYLE_PREFIX = 'style.';
|
||||||
var styleSettersCache = StringMapWrapper.create();
|
var styleSettersCache = StringMapWrapper.create();
|
||||||
|
|
||||||
function styleSetterFactory(styleName:string, stylesuffix:string) {
|
function styleSetterFactory(styleName:string, stylesuffix:string) {
|
||||||
var cacheKey = styleName + stylesuffix;
|
var cacheKey = styleName + stylesuffix;
|
||||||
var setterFn = StringMapWrapper.get(styleSettersCache, cacheKey);
|
var setterFn = StringMapWrapper.get(styleSettersCache, cacheKey);
|
||||||
|
var dashCasedStyleName;
|
||||||
|
|
||||||
if (isBlank(setterFn)) {
|
if (isBlank(setterFn)) {
|
||||||
|
dashCasedStyleName = camelCaseToDashCase(styleName);
|
||||||
setterFn = function(element, value) {
|
setterFn = function(element, value) {
|
||||||
var valAsStr;
|
var valAsStr;
|
||||||
if (isPresent(value)) {
|
if (isPresent(value)) {
|
||||||
valAsStr = stringify(value);
|
valAsStr = stringify(value);
|
||||||
DOM.setStyle(element, styleName, valAsStr + stylesuffix);
|
DOM.setStyle(element, dashCasedStyleName, valAsStr + stylesuffix);
|
||||||
} else {
|
} else {
|
||||||
DOM.removeStyle(element, styleName);
|
DOM.removeStyle(element, dashCasedStyleName);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
StringMapWrapper.set(classSettersCache, cacheKey, setterFn);
|
StringMapWrapper.set(classSettersCache, cacheKey, setterFn);
|
||||||
|
@ -229,7 +232,7 @@ export class ElementBinderBuilder extends CompileStep {
|
||||||
var elProp = ListWrapper.removeAt(pipes, 0);
|
var elProp = ListWrapper.removeAt(pipes, 0);
|
||||||
|
|
||||||
var bindingAst = isPresent(compileElement.propertyBindings) ?
|
var bindingAst = isPresent(compileElement.propertyBindings) ?
|
||||||
MapWrapper.get(compileElement.propertyBindings, elProp) :
|
MapWrapper.get(compileElement.propertyBindings, dashCaseToCamelCase(elProp)) :
|
||||||
null;
|
null;
|
||||||
|
|
||||||
if (isBlank(bindingAst)) {
|
if (isBlank(bindingAst)) {
|
||||||
|
@ -246,7 +249,7 @@ export class ElementBinderBuilder extends CompileStep {
|
||||||
directiveIndex,
|
directiveIndex,
|
||||||
fullExpAstWithBindPipes,
|
fullExpAstWithBindPipes,
|
||||||
dirProp,
|
dirProp,
|
||||||
reflector.setter(dirProp)
|
reflector.setter(dashCaseToCamelCase(dirProp))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import {StringWrapper, RegExpWrapper} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
var DASH_CASE_REGEXP = RegExpWrapper.create('-([a-z])');
|
||||||
|
var CAMEL_CASE_REGEXP = RegExpWrapper.create('([A-Z])');
|
||||||
|
|
||||||
|
export function dashCaseToCamelCase(input:string) {
|
||||||
|
return StringWrapper.replaceAllMapped(input, DASH_CASE_REGEXP, (m) => {
|
||||||
|
return m[1].toUpperCase();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function camelCaseToDashCase(input:string) {
|
||||||
|
return StringWrapper.replaceAllMapped(input, CAMEL_CASE_REGEXP, (m) => {
|
||||||
|
return '-' + m[1].toLowerCase();
|
||||||
|
});
|
||||||
|
}
|
|
@ -21,7 +21,7 @@ class BrowserDomAdapter extends GenericBrowserDomAdapter {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final attrToPropMap = const {
|
final attrToPropMap = const {
|
||||||
'inner-html': 'innerHtml',
|
'innerHtml': 'innerHtml',
|
||||||
'readonly': 'readOnly',
|
'readonly': 'readOnly',
|
||||||
'tabindex': 'tabIndex',
|
'tabindex': 'tabIndex',
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,9 +4,9 @@ import {setRootDomAdapter} from './dom_adapter';
|
||||||
import {GenericBrowserDomAdapter} from './generic_browser_adapter';
|
import {GenericBrowserDomAdapter} from './generic_browser_adapter';
|
||||||
|
|
||||||
var _attrToPropMap = {
|
var _attrToPropMap = {
|
||||||
'inner-html': 'innerHTML',
|
'innerHtml': 'innerHTML',
|
||||||
'readonly': 'readOnly',
|
'readonly': 'readOnly',
|
||||||
'tabindex': 'tabIndex',
|
'tabindex': 'tabIndex'
|
||||||
};
|
};
|
||||||
|
|
||||||
export class BrowserDomAdapter extends GenericBrowserDomAdapter {
|
export class BrowserDomAdapter extends GenericBrowserDomAdapter {
|
||||||
|
|
|
@ -13,7 +13,7 @@ import {BaseException, isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||||
import {SelectorMatcher, CssSelector} from 'angular2/src/core/compiler/selector';
|
import {SelectorMatcher, CssSelector} from 'angular2/src/core/compiler/selector';
|
||||||
|
|
||||||
var _attrToPropMap = {
|
var _attrToPropMap = {
|
||||||
'inner-html': 'innerHTML',
|
'innerHtml': 'innerHTML',
|
||||||
'readonly': 'readOnly',
|
'readonly': 'readOnly',
|
||||||
'tabindex': 'tabIndex',
|
'tabindex': 'tabIndex',
|
||||||
};
|
};
|
||||||
|
@ -206,7 +206,7 @@ export class Parse5DomAdapter extends DomAdapter {
|
||||||
}
|
}
|
||||||
setText(el, value:string) {
|
setText(el, value:string) {
|
||||||
if (this.isTextNode(el)) {
|
if (this.isTextNode(el)) {
|
||||||
el.data = value;
|
el.data = value;
|
||||||
} else {
|
} else {
|
||||||
this.clearNodes(el);
|
this.clearNodes(el);
|
||||||
treeAdapter.insertText(el, value);
|
treeAdapter.insertText(el, value);
|
||||||
|
@ -315,7 +315,7 @@ export class Parse5DomAdapter extends DomAdapter {
|
||||||
for (var key in styleMap) {
|
for (var key in styleMap) {
|
||||||
var newValue = styleMap[key];
|
var newValue = styleMap[key];
|
||||||
if (newValue && newValue.length > 0) {
|
if (newValue && newValue.length > 0) {
|
||||||
styleAttrValue += key + ":" + styleMap[key] + ";";
|
styleAttrValue += key + ":" + styleMap[key] + ";";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
element.attribs["style"] = styleAttrValue;
|
element.attribs["style"] = styleAttrValue;
|
||||||
|
@ -427,7 +427,7 @@ export class Parse5DomAdapter extends DomAdapter {
|
||||||
var declaration = parsedRule.declarations[j];
|
var declaration = parsedRule.declarations[j];
|
||||||
rule.style[declaration.property] = declaration.value;
|
rule.style[declaration.property] = declaration.value;
|
||||||
rule.style.cssText += declaration.property + ": " + declaration.value + ";";
|
rule.style.cssText += declaration.property + ": " + declaration.value + ";";
|
||||||
}
|
}
|
||||||
} else if (parsedRule.type == "media") {
|
} else if (parsedRule.type == "media") {
|
||||||
rule.type = 4;
|
rule.type = 4;
|
||||||
rule.media = {mediaText: parsedRule.media};
|
rule.media = {mediaText: parsedRule.media};
|
||||||
|
|
|
@ -133,6 +133,23 @@ export function main() {
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should consume binding to camel-cased properties using dash-cased syntax in templates', inject([AsyncTestCompleter], (async) => {
|
||||||
|
tplResolver.setTemplate(MyComp, new Template({inline: '<input [read-only]="ctxBoolProp">'}));
|
||||||
|
|
||||||
|
compiler.compile(MyComp).then((pv) => {
|
||||||
|
createView(pv);
|
||||||
|
|
||||||
|
cd.detectChanges();
|
||||||
|
expect(view.nodes[0].readOnly).toBeFalsy();
|
||||||
|
|
||||||
|
ctx.ctxBoolProp = true;
|
||||||
|
cd.detectChanges();
|
||||||
|
expect(view.nodes[0].readOnly).toBeTruthy();
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should consume binding to inner-html', inject([AsyncTestCompleter], (async) => {
|
it('should consume binding to inner-html', inject([AsyncTestCompleter], (async) => {
|
||||||
tplResolver.setTemplate(MyComp, new Template({inline: '<div inner-html="{{ctxProp}}"></div>'}));
|
tplResolver.setTemplate(MyComp, new Template({inline: '<div inner-html="{{ctxProp}}"></div>'}));
|
||||||
|
|
||||||
|
@ -592,9 +609,11 @@ class PushBasedComp {
|
||||||
class MyComp {
|
class MyComp {
|
||||||
ctxProp:string;
|
ctxProp:string;
|
||||||
ctxNumProp;
|
ctxNumProp;
|
||||||
|
ctxBoolProp;
|
||||||
constructor() {
|
constructor() {
|
||||||
this.ctxProp = 'initial value';
|
this.ctxProp = 'initial value';
|
||||||
this.ctxNumProp = 0;
|
this.ctxNumProp = 0;
|
||||||
|
this.ctxBoolProp = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue