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
@@ -1,5 +1,5 @@
import {int, isPresent, isBlank, Type, BaseException, StringWrapper, RegExpWrapper, isString, stringify} from 'angular2/src/facade/lang';
import {Element, DOM} from 'angular2/src/facade/dom';
import {Element, DOM, attrToPropMap} from 'angular2/src/facade/dom';
import {ListWrapper, List, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';

import {reflector} from 'angular2/src/reflection/reflection';
Expand Down Expand Up @@ -91,11 +91,19 @@ function roleSetter(element:Element, value) {
}
}

// special mapping for cases where attribute name doesn't match property name
var attrToProp = StringMapWrapper.merge({
"inner-html": "innerHTML",
"readonly": "readOnly",
"tabindex": "tabIndex",
}, attrToPropMap);

// tells if an attribute is handled by the ElementBinderBuilder step
export function isSpecialProperty(propName:string) {
return StringWrapper.startsWith(propName, ARIA_PREFIX)
|| StringWrapper.startsWith(propName, CLASS_PREFIX)
|| StringWrapper.startsWith(propName, STYLE_PREFIX);
return StringWrapper.startsWith(propName, ARIA_PREFIX)
|| StringWrapper.startsWith(propName, CLASS_PREFIX)
|| StringWrapper.startsWith(propName, STYLE_PREFIX)
|| StringMapWrapper.contains(attrToProp, propName);
}

/**
Expand Down Expand Up @@ -176,10 +184,14 @@ export class ElementBinderBuilder extends CompileStep {
setterFn = classSetterFactory(StringWrapper.substring(property, CLASS_PREFIX.length));
} else if (StringWrapper.startsWith(property, STYLE_PREFIX)) {
styleParts = StringWrapper.split(property, DOT_REGEXP);
styleSuffix = styleParts.length > 2 ? ListWrapper.get(styleParts, 2) : '';
styleSuffix = styleParts.length > 2 ? ListWrapper.get(styleParts, 2) : '';
setterFn = styleSetterFactory(ListWrapper.get(styleParts, 1), styleSuffix);
} else if (DOM.hasProperty(compileElement.element, property)) {
setterFn = reflector.setter(property);
} else {
property = this._resolvePropertyName(property);
//TODO(pk): special casing innerHtml, see: https://github.com/angular/angular/issues/789
if (DOM.hasProperty(compileElement.element, property) || StringWrapper.equals(property, 'innerHtml')) {
setterFn = reflector.setter(property);
}
}

if (isPresent(setterFn)) {
Expand Down Expand Up @@ -236,4 +248,9 @@ export class ElementBinderBuilder extends CompileStep {
var parts = StringWrapper.split(bindConfig, RegExpWrapper.create("\\|"));
return ListWrapper.map(parts, (s) => s.trim());
}

_resolvePropertyName(attrName:string) {
var mappedPropName = StringMapWrapper.get(attrToProp, attrName);
return isPresent(mappedPropName) ? mappedPropName : attrName;
}
}
8 changes: 7 additions & 1 deletion modules/angular2/src/facade/dom.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ export 'dart:html' show
InputElement,
AnchorElement,
Text,
window;
window,
attrToPropMap;

// TODO(tbosch): Is there a builtin one? Why is Dart
// removing unknown elements by default?
Expand All @@ -36,6 +37,11 @@ void gc() {

final identitySanitizer = new IdentitySanitizer();

// override JS logic of attribute to property mapping
var attrToPropMap = {
"inner-html": "innerHtml"
};

class DOM {
static query(String selector) => document.querySelector(selector);

Expand Down
2 changes: 2 additions & 0 deletions modules/angular2/src/facade/dom.es6
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export var gc = window.gc ? () => window.gc() : () => null;
export var CssRule = window.CSSRule;
export var CssKeyframesRule = window.CSSKeyframesRule;

export var attrToPropMap = {};

export class DOM {
static query(selector) {
return document.querySelector(selector);
Expand Down
37 changes: 37 additions & 0 deletions modules/angular2/test/core/compiler/integration_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,41 @@ export function main() {
});
});

it('should consume binding to propery names where attr name and property name do not match', (done) => {
tplResolver.setTemplate(MyComp, new Template({inline: '<div [tabindex]="ctxNumProp"></div>'}));

compiler.compile(MyComp).then((pv) => {
createView(pv);

cd.detectChanges();
expect(view.nodes[0].tabIndex).toEqual(0);

ctx.ctxNumProp = 5;
cd.detectChanges();
expect(view.nodes[0].tabIndex).toEqual(5);

done();
});
});

it('should consume binding to inner-html', (done) => {
tplResolver.setTemplate(MyComp, new Template({inline: '<div inner-html="{{ctxProp}}"></div>'}));

compiler.compile(MyComp).then((pv) => {
createView(pv);

ctx.ctxProp = 'Some <span>HTML</span>';
cd.detectChanges();
expect(DOM.getInnerHTML(view.nodes[0])).toEqual('Some <span>HTML</span>');

ctx.ctxProp = 'Some other <div>HTML</div>';
cd.detectChanges();
expect(DOM.getInnerHTML(view.nodes[0])).toEqual('Some other <div>HTML</div>');

done();
});
});

it('should consume directive watch expression change.', (done) => {
var tpl =
'<div>' +
Expand Down Expand Up @@ -490,8 +525,10 @@ class PushBasedComp {
@Component()
class MyComp {
ctxProp:string;
ctxNumProp;
constructor() {
this.ctxProp = 'initial value';
this.ctxNumProp = 0;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,27 @@ export function main() {
expect(view.nodes[0].hidden).toEqual(false);
});

it('should bind element properties where attr name and prop name do not match', () => {
var propertyBindings = MapWrapper.createFromStringMap({
'tabindex': 'prop1'
});
var pipeline = createPipeline({propertyBindings: propertyBindings});
var results = pipeline.process(el('<div viewroot prop-binding></div>'));
var pv = results[0].inheritedProtoView;

expect(pv.elementBinders[0].hasElementPropertyBindings).toBe(true);

instantiateView(pv);

evalContext.prop1 = 1;
changeDetector.detectChanges();
expect(view.nodes[0].tabIndex).toEqual(1);

evalContext.prop1 = 0;
changeDetector.detectChanges();
expect(view.nodes[0].tabIndex).toEqual(0);
});

it('should bind to aria-* attributes when exp evaluates to strings', () => {
var propertyBindings = MapWrapper.createFromStringMap({
'aria-label': 'prop1'
Expand Down