Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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\\|]+)');

Expand Down Expand Up @@ -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
Expand All @@ -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);
}
});
}
Expand Down Expand Up @@ -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}`);
}
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -34,7 +37,6 @@ function ariaSetterFactory(attrName:string) {
return setterFn;
}

const CLASS_ATTR = 'class';
const CLASS_PREFIX = 'class.';
var classSettersCache = StringMapWrapper.create();

Expand All @@ -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);
Expand Down Expand Up @@ -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)) {
Expand All @@ -246,7 +249,7 @@ export class ElementBinderBuilder extends CompileStep {
directiveIndex,
fullExpAstWithBindPipes,
dirProp,
reflector.setter(dirProp)
reflector.setter(dashCaseToCamelCase(dirProp))
);
}
});
Expand Down
16 changes: 16 additions & 0 deletions modules/angular2/src/core/compiler/pipeline/util.js
Original file line number Diff line number Diff line change
@@ -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();
});
}
2 changes: 1 addition & 1 deletion modules/angular2/src/dom/browser_adapter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class BrowserDomAdapter extends GenericBrowserDomAdapter {

@override
final attrToPropMap = const {
'inner-html': 'innerHtml',
'innerHtml': 'innerHtml',
'readonly': 'readOnly',
'tabindex': 'tabIndex',
};
Expand Down
4 changes: 2 additions & 2 deletions modules/angular2/src/dom/browser_adapter.es6
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
8 changes: 4 additions & 4 deletions modules/angular2/src/dom/parse5_adapter.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -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',
};
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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};
Expand Down
19 changes: 19 additions & 0 deletions modules/angular2/test/core/compiler/integration_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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>'}));

Expand Down Expand Up @@ -592,9 +609,11 @@ class PushBasedComp {
class MyComp {
ctxProp:string;
ctxNumProp;
ctxBoolProp;
constructor() {
this.ctxProp = 'initial value';
this.ctxNumProp = 0;
this.ctxBoolProp = false;
}
}

Expand Down