parent
afda43dc02
commit
b39d2c0101
|
@ -6,6 +6,7 @@ import {Decorator, Component, Viewport} from '../../annotations/annotations';
|
|||
import {ElementBinder} from '../element_binder';
|
||||
import {ProtoElementInjector} from '../element_injector';
|
||||
import {ProtoView} from '../view';
|
||||
import {dashCaseToCamelCase} from './util';
|
||||
|
||||
import {AST} from 'angular2/change_detection';
|
||||
|
||||
|
@ -114,7 +115,7 @@ export class CompileElement {
|
|||
if (isBlank(this.propertyBindings)) {
|
||||
this.propertyBindings = MapWrapper.create();
|
||||
}
|
||||
MapWrapper.set(this.propertyBindings, property, expression);
|
||||
MapWrapper.set(this.propertyBindings, dashCaseToCamelCase(property), expression);
|
||||
}
|
||||
|
||||
addVariableBinding(variableName:string, variableValue:string) {
|
||||
|
|
|
@ -10,7 +10,8 @@ import {CompileStep} from './compile_step';
|
|||
import {CompileElement} from './compile_element';
|
||||
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\\|]+)');
|
||||
|
||||
|
@ -84,7 +85,7 @@ function updateMatchedProperties(matchedProperties, selector, directive) {
|
|||
if (isPresent(attrs)) {
|
||||
for (var idx = 0; idx<attrs.length; idx+=2) {
|
||||
// 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
|
||||
|
@ -97,7 +98,7 @@ function updateMatchedProperties(matchedProperties, selector, directive) {
|
|||
// keep the property name and remove the pipe
|
||||
var bindProp = RegExpWrapper.firstMatch(PROPERTY_BINDING_REGEXP, value);
|
||||
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) => {
|
||||
if (!DOM.hasProperty(current.element, prop) && !isSpecialProperty(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 {CompileElement} from './compile_element';
|
||||
import {CompileControl} from './compile_control';
|
||||
import {dashCaseToCamelCase, camelCaseToDashCase} from './util';
|
||||
|
||||
var DOT_REGEXP = RegExpWrapper.create('\\.');
|
||||
|
||||
const ARIA_PREFIX = 'aria-';
|
||||
const ARIA_PREFIX = 'aria';
|
||||
var ariaSettersCache = StringMapWrapper.create();
|
||||
|
||||
function ariaSetterFactory(attrName:string) {
|
||||
var setterFn = StringMapWrapper.get(ariaSettersCache, attrName);
|
||||
var ariaAttrName;
|
||||
|
||||
if (isBlank(setterFn)) {
|
||||
ariaAttrName = camelCaseToDashCase(attrName);
|
||||
setterFn = function(element, value) {
|
||||
if (isPresent(value)) {
|
||||
DOM.setAttribute(element, attrName, stringify(value));
|
||||
DOM.setAttribute(element, ariaAttrName, stringify(value));
|
||||
} else {
|
||||
DOM.removeAttribute(element, attrName);
|
||||
DOM.removeAttribute(element, ariaAttrName);
|
||||
}
|
||||
};
|
||||
StringMapWrapper.set(ariaSettersCache, attrName, setterFn);
|
||||
|
@ -34,7 +37,6 @@ function ariaSetterFactory(attrName:string) {
|
|||
return setterFn;
|
||||
}
|
||||
|
||||
const CLASS_ATTR = 'class';
|
||||
const CLASS_PREFIX = 'class.';
|
||||
var classSettersCache = StringMapWrapper.create();
|
||||
|
||||
|
@ -55,22 +57,23 @@ function classSetterFactory(className:string) {
|
|||
return setterFn;
|
||||
}
|
||||
|
||||
const STYLE_ATTR = 'style';
|
||||
const STYLE_PREFIX = 'style.';
|
||||
var styleSettersCache = StringMapWrapper.create();
|
||||
|
||||
function styleSetterFactory(styleName:string, stylesuffix:string) {
|
||||
var cacheKey = styleName + stylesuffix;
|
||||
var setterFn = StringMapWrapper.get(styleSettersCache, cacheKey);
|
||||
var dashCasedStyleName;
|
||||
|
||||
if (isBlank(setterFn)) {
|
||||
dashCasedStyleName = camelCaseToDashCase(styleName);
|
||||
setterFn = function(element, value) {
|
||||
var valAsStr;
|
||||
if (isPresent(value)) {
|
||||
valAsStr = stringify(value);
|
||||
DOM.setStyle(element, styleName, valAsStr + stylesuffix);
|
||||
DOM.setStyle(element, dashCasedStyleName, valAsStr + stylesuffix);
|
||||
} else {
|
||||
DOM.removeStyle(element, styleName);
|
||||
DOM.removeStyle(element, dashCasedStyleName);
|
||||
}
|
||||
};
|
||||
StringMapWrapper.set(classSettersCache, cacheKey, setterFn);
|
||||
|
@ -229,7 +232,7 @@ export class ElementBinderBuilder extends CompileStep {
|
|||
var elProp = ListWrapper.removeAt(pipes, 0);
|
||||
|
||||
var bindingAst = isPresent(compileElement.propertyBindings) ?
|
||||
MapWrapper.get(compileElement.propertyBindings, elProp) :
|
||||
MapWrapper.get(compileElement.propertyBindings, dashCaseToCamelCase(elProp)) :
|
||||
null;
|
||||
|
||||
if (isBlank(bindingAst)) {
|
||||
|
@ -246,7 +249,7 @@ export class ElementBinderBuilder extends CompileStep {
|
|||
directiveIndex,
|
||||
fullExpAstWithBindPipes,
|
||||
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
|
||||
final attrToPropMap = const {
|
||||
'inner-html': 'innerHtml',
|
||||
'innerHtml': 'innerHtml',
|
||||
'readonly': 'readOnly',
|
||||
'tabindex': 'tabIndex',
|
||||
};
|
||||
|
|
|
@ -4,9 +4,9 @@ import {setRootDomAdapter} from './dom_adapter';
|
|||
import {GenericBrowserDomAdapter} from './generic_browser_adapter';
|
||||
|
||||
var _attrToPropMap = {
|
||||
'inner-html': 'innerHTML',
|
||||
'innerHtml': 'innerHTML',
|
||||
'readonly': 'readOnly',
|
||||
'tabindex': 'tabIndex',
|
||||
'tabindex': 'tabIndex'
|
||||
};
|
||||
|
||||
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';
|
||||
|
||||
var _attrToPropMap = {
|
||||
'inner-html': 'innerHTML',
|
||||
'innerHtml': 'innerHTML',
|
||||
'readonly': 'readOnly',
|
||||
'tabindex': 'tabIndex',
|
||||
};
|
||||
|
@ -206,7 +206,7 @@ export class Parse5DomAdapter extends DomAdapter {
|
|||
}
|
||||
setText(el, value:string) {
|
||||
if (this.isTextNode(el)) {
|
||||
el.data = value;
|
||||
el.data = value;
|
||||
} else {
|
||||
this.clearNodes(el);
|
||||
treeAdapter.insertText(el, value);
|
||||
|
@ -315,7 +315,7 @@ export class Parse5DomAdapter extends DomAdapter {
|
|||
for (var key in styleMap) {
|
||||
var newValue = styleMap[key];
|
||||
if (newValue && newValue.length > 0) {
|
||||
styleAttrValue += key + ":" + styleMap[key] + ";";
|
||||
styleAttrValue += key + ":" + styleMap[key] + ";";
|
||||
}
|
||||
}
|
||||
element.attribs["style"] = styleAttrValue;
|
||||
|
@ -427,7 +427,7 @@ export class Parse5DomAdapter extends DomAdapter {
|
|||
var declaration = parsedRule.declarations[j];
|
||||
rule.style[declaration.property] = declaration.value;
|
||||
rule.style.cssText += declaration.property + ": " + declaration.value + ";";
|
||||
}
|
||||
}
|
||||
} else if (parsedRule.type == "media") {
|
||||
rule.type = 4;
|
||||
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) => {
|
||||
tplResolver.setTemplate(MyComp, new Template({inline: '<div inner-html="{{ctxProp}}"></div>'}));
|
||||
|
||||
|
@ -592,9 +609,11 @@ class PushBasedComp {
|
|||
class MyComp {
|
||||
ctxProp:string;
|
||||
ctxNumProp;
|
||||
ctxBoolProp;
|
||||
constructor() {
|
||||
this.ctxProp = 'initial value';
|
||||
this.ctxNumProp = 0;
|
||||
this.ctxBoolProp = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue