Skip to content

Commit 251d76f

Browse files
committed
refectory to use property_setter_factory and setter metadata from directive dependencies.
1 parent 86e690e commit 251d76f

File tree

12 files changed

+294
-140
lines changed

12 files changed

+294
-140
lines changed

modules/angular2/src/render/api.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,13 @@ export class DirectiveMetadata {
7777
compileChildren:boolean;
7878
events:Map<string, string>;
7979
bind:Map<string, string>;
80-
constructor({selector, compileChildren, events, bind}) {
80+
setters:List<string>;
81+
constructor({selector, compileChildren, events, bind, setters}) {
8182
this.selector = selector;
8283
this.compileChildren = isPresent(compileChildren) ? compileChildren : true;
8384
this.events = events;
8485
this.bind = bind;
86+
this.setters = setters;
8587
}
8688
}
8789

modules/angular2/src/render/compiler/directive_parser.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ import {CompileStep} from './compile_step';
99
import {CompileElement} from './compile_element';
1010
import {CompileControl} from './compile_control';
1111

12-
import {DirectiveMetadata} from '../api';
12+
import {setterFactory} from './property_setter_factory';
1313

14+
import {DirectiveMetadata} from '../api';
1415
import {dashCaseToCamelCase, camelCaseToDashCase} from '../util';
1516

1617
/**
@@ -64,6 +65,11 @@ export class DirectiveParser extends CompileStep {
6465
this._bindDirectiveEvent(eventName, action, current, directiveBinder);
6566
});
6667
}
68+
if (isPresent(directive.setters)) {
69+
ListWrapper.forEach(directive.setters, (propertyName) => {
70+
directiveBinder.bindPropertySetter(propertyName, setterFactory(propertyName));
71+
});
72+
}
6773
});
6874
}
6975

modules/angular2/src/render/compiler/property_binding_parser.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {CompileElement} from './compile_element';
88
import {CompileControl} from './compile_control';
99

1010
import {dashCaseToCamelCase} from '../util';
11+
import {setterFactory} from './property_setter_factory';
1112

1213
// Group 1 = "bind"
1314
// Group 2 = "var"
@@ -91,9 +92,10 @@ export class PropertyBindingParser extends CompileStep {
9192
}
9293

9394
_bindPropertyAst(name, ast, current:CompileElement, newAttrs) {
94-
current.bindElement().bindProperty(
95-
dashCaseToCamelCase(name), ast
96-
);
95+
var binder = current.bindElement();
96+
var camelCaseName = dashCaseToCamelCase(name);
97+
binder.bindProperty(camelCaseName, ast);
98+
binder.bindPropertySetter(camelCaseName, setterFactory(name));
9799
MapWrapper.set(newAttrs, name, ast.source);
98100
}
99101

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import {StringWrapper, RegExpWrapper, BaseException, isPresent, isBlank, isString, stringify} from 'angular2/src/facade/lang';
2+
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
3+
import {DOM} from 'angular2/src/dom/dom_adapter';
4+
import {reflector} from 'angular2/src/reflection/reflection';
5+
6+
var DASH_CASE_REGEXP = RegExpWrapper.create('-([a-z])');
7+
var CAMEL_CASE_REGEXP = RegExpWrapper.create('([A-Z])');
8+
9+
export function dashCaseToCamelCase(input:string): string {
10+
return StringWrapper.replaceAllMapped(input, DASH_CASE_REGEXP, (m) => {
11+
return m[1].toUpperCase();
12+
});
13+
}
14+
15+
export function camelCaseToDashCase(input:string): string {
16+
return StringWrapper.replaceAllMapped(input, CAMEL_CASE_REGEXP, (m) => {
17+
return '-' + m[1].toLowerCase();
18+
});
19+
}
20+
21+
const STYLE_SEPARATOR = '.';
22+
var propertySettersCache = StringMapWrapper.create();
23+
var innerHTMLSetterCache;
24+
25+
export function setterFactory(property: string): Function {
26+
var setterFn, styleParts, styleSuffix;
27+
if (StringWrapper.startsWith(property, ATTRIBUTE_PREFIX)) {
28+
setterFn = attributeSetterFactory(StringWrapper.substring(property, ATTRIBUTE_PREFIX.length));
29+
} else if (StringWrapper.startsWith(property, CLASS_PREFIX)) {
30+
setterFn = classSetterFactory(StringWrapper.substring(property, CLASS_PREFIX.length));
31+
} else if (StringWrapper.startsWith(property, STYLE_PREFIX)) {
32+
styleParts = property.split(STYLE_SEPARATOR);
33+
styleSuffix = styleParts.length > 2 ? ListWrapper.get(styleParts, 2) : '';
34+
setterFn = styleSetterFactory(ListWrapper.get(styleParts, 1), styleSuffix);
35+
} else if (StringWrapper.equals(property, 'innerHtml')) {
36+
if (isBlank(innerHTMLSetterCache)) {
37+
innerHTMLSetterCache = (el, value) => DOM.setInnerHTML(el, value);
38+
}
39+
setterFn = innerHTMLSetterCache;
40+
} else {
41+
property = resolvePropertyName(property);
42+
setterFn = StringMapWrapper.get(propertySettersCache, property);
43+
if (isBlank(setterFn)) {
44+
var propertySetterFn = reflector.setter(property);
45+
setterFn = function(receiver, value) {
46+
if (DOM.hasProperty(receiver, property)) {
47+
return propertySetterFn(receiver, value);
48+
}
49+
}
50+
StringMapWrapper.set(propertySettersCache, property, setterFn);
51+
}
52+
}
53+
return setterFn;
54+
}
55+
56+
const ATTRIBUTE_PREFIX = 'attr.';
57+
var attributeSettersCache = StringMapWrapper.create();
58+
59+
function _isValidAttributeValue(attrName:string, value: any): boolean {
60+
if (attrName == "role") {
61+
return isString(value);
62+
} else {
63+
return isPresent(value);
64+
}
65+
}
66+
67+
function attributeSetterFactory(attrName:string): Function {
68+
var setterFn = StringMapWrapper.get(attributeSettersCache, attrName);
69+
var dashCasedAttributeName;
70+
71+
if (isBlank(setterFn)) {
72+
dashCasedAttributeName = camelCaseToDashCase(attrName);
73+
setterFn = function(element, value) {
74+
if (_isValidAttributeValue(dashCasedAttributeName, value)) {
75+
DOM.setAttribute(element, dashCasedAttributeName, stringify(value));
76+
} else {
77+
if (isPresent(value)) {
78+
throw new BaseException("Invalid " + dashCasedAttributeName +
79+
" attribute, only string values are allowed, got '" + stringify(value) + "'");
80+
}
81+
DOM.removeAttribute(element, dashCasedAttributeName);
82+
}
83+
};
84+
StringMapWrapper.set(attributeSettersCache, attrName, setterFn);
85+
}
86+
87+
return setterFn;
88+
}
89+
90+
const CLASS_PREFIX = 'class.';
91+
var classSettersCache = StringMapWrapper.create();
92+
93+
function classSetterFactory(className:string): Function {
94+
var setterFn = StringMapWrapper.get(classSettersCache, className);
95+
96+
if (isBlank(setterFn)) {
97+
setterFn = function(element, value) {
98+
if (value) {
99+
DOM.addClass(element, className);
100+
} else {
101+
DOM.removeClass(element, className);
102+
}
103+
};
104+
StringMapWrapper.set(classSettersCache, className, setterFn);
105+
}
106+
107+
return setterFn;
108+
}
109+
110+
const STYLE_PREFIX = 'style.';
111+
var styleSettersCache = StringMapWrapper.create();
112+
113+
function styleSetterFactory(styleName:string, styleSuffix:string): Function {
114+
var cacheKey = styleName + styleSuffix;
115+
var setterFn = StringMapWrapper.get(styleSettersCache, cacheKey);
116+
var dashCasedStyleName;
117+
118+
if (isBlank(setterFn)) {
119+
dashCasedStyleName = camelCaseToDashCase(styleName);
120+
setterFn = function(element, value) {
121+
var valAsStr;
122+
if (isPresent(value)) {
123+
valAsStr = stringify(value);
124+
DOM.setStyle(element, dashCasedStyleName, valAsStr + styleSuffix);
125+
} else {
126+
DOM.removeStyle(element, dashCasedStyleName);
127+
}
128+
};
129+
StringMapWrapper.set(styleSettersCache, cacheKey, setterFn);
130+
}
131+
132+
return setterFn;
133+
}
134+
135+
function resolvePropertyName(attrName:string): string {
136+
var mappedPropName = StringMapWrapper.get(DOM.attrToPropMap, attrName);
137+
return isPresent(mappedPropName) ? mappedPropName : attrName;
138+
}

modules/angular2/src/render/direct_renderer.js

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {ViewFactory} from './view/view_factory';
66
import {Compiler} from './compiler/compiler';
77
import {ShadowDomStrategy} from './shadow_dom/shadow_dom_strategy';
88
import {EventManager} from './events/event_manager';
9-
import {ElementPropertyAccessor} from './view/element_property_accessor';
109
import {ViewRef} from './direct_view_ref';
1110
import {DirectProtoViewRef} from './direct_proto_view_ref';
1211

@@ -41,17 +40,15 @@ export class DirectRenderer extends api.Renderer {
4140
_viewFactory: ViewFactory;
4241
_shadowDomStrategy: ShadowDomStrategy;
4342
_evenManager: EventManager;
44-
_propertyAccessor: ElementPropertyAccessor;
4543

4644
constructor(
4745
compiler: Compiler, viewFactory: ViewFactory, shadowDomStrategy: ShadowDomStrategy,
48-
eventManager: EventManager, propertyAccessor: ElementPropertyAccessor) {
46+
eventManager: EventManager) {
4947
super();
5048
this._compiler = compiler;
5149
this._viewFactory = viewFactory;
5250
this._shadowDomStrategy = shadowDomStrategy;
5351
this._evenManager = eventManager;
54-
this._propertyAccessor = propertyAccessor;
5552
}
5653

5754
// TODO(tbosch): union type return ProtoView or Promise<ProtoView>
@@ -83,7 +80,7 @@ export class DirectRenderer extends api.Renderer {
8380
}
8481

8582
setElementProperty(viewRef:api.ViewRef, elementIndex:number, propertyName:string, propertyValue:any):void {
86-
_resolveView(viewRef).setElementProperty(this._propertyAccessor, elementIndex, propertyName, propertyValue);
83+
_resolveView(viewRef).setElementProperty(elementIndex, propertyName, propertyValue);
8784
}
8885

8986
setComponentView(viewRef:api.ViewRef, elementIndex:number, nestedViewRef:api.ViewRef):void {

modules/angular2/src/render/view/element_property_accessor.js

Lines changed: 0 additions & 122 deletions
This file was deleted.

modules/angular2/src/render/view/proto_view.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import {isPresent} from 'angular2/src/facade/lang';
22
import {DOM} from 'angular2/src/dom/dom_adapter';
3+
import {SetterFn} from 'angular2/src/reflection/types';
4+
5+
import {List, Map} from 'angular2/src/facade/collection';
36

47
import {ElementBinder} from './element_binder';
58
import {NG_BINDING_CLASS} from '../util';
@@ -11,18 +14,21 @@ export class ProtoView {
1114
instantiateInPlace:boolean;
1215
rootBindingOffset:int;
1316
componentId:string;
17+
propertySetters: Map<string, SetterFn>;
1418

1519
constructor({
1620
elementBinders,
1721
element,
1822
instantiateInPlace,
19-
componentId
23+
componentId,
24+
propertySetters
2025
}) {
2126
this.element = element;
2227
this.elementBinders = elementBinders;
2328
this.isTemplateElement = DOM.isTemplateElement(this.element);
2429
this.instantiateInPlace = instantiateInPlace;
2530
this.rootBindingOffset = (isPresent(this.element) && DOM.hasClass(this.element, NG_BINDING_CLASS)) ? 1 : 0;
2631
this.componentId = componentId;
32+
this.propertySetters = propertySetters;
2733
}
2834
}

0 commit comments

Comments
 (0)