Skip to content

Commit 1f6b62c

Browse files
authored
added support for template converters (#120)
1 parent 08c8226 commit 1f6b62c

File tree

3 files changed

+60
-25
lines changed

3 files changed

+60
-25
lines changed

samples/examples/templates.html

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
<title>Microsoft Graph Toolkit Test</title>
55

66
<script type="module" src="../../../dist/es6/index.js"></script>
7+
<script type="module" src="../../../dist/es6/mock/mgt-mock-provider.js"></script>
78
</head>
89
<body>
10+
<mgt-mock-provider></mgt-mock-provider>
11+
912
<mgt-agenda id="myDay">
1013
<template>
1114
<style>
@@ -43,7 +46,7 @@
4346
}
4447
</style>
4548
<button class="eventButton">
46-
<div class="event-subject">{{ event.subject }}</div>
49+
<div class="event-subject">{{{ testFunction(event.subject.toLowerCase()) }}}</div>
4750
<div data-for="attendee in event.attendees">
4851
<mgt-person person-query="{{ attendee.emailAddress.name }}" show-name show-email></mgt-person>
4952
</div>
@@ -59,6 +62,9 @@
5962

6063
<script type="module">
6164
let myDay = document.getElementById('myDay');
65+
66+
myDay.templateConverters.testFunction = str => str.length;
67+
6268
myDay.addEventListener('templateRendered', e => {
6369
if (e.detail && e.detail.context) {
6470
let button = e.detail.element.querySelector('.eventButton');

src/components/templateHelper.ts

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
export class TemplateHelper {
99
private static _expression = /{{\s*[\w\.]+\s*}}/g;
10+
private static _converterExpression = /{{{\s*[\w\.()]+\s*}}}/g;
1011

1112
/**
1213
* Gets the value of an expanded key in an object
@@ -32,24 +33,31 @@ export class TemplateHelper {
3233
return value;
3334
}
3435

35-
private static replaceExpression(str: string, context: object) {
36-
return str.replace(this._expression, match => {
37-
let key = match.substring(2, match.length - 2);
38-
let value = this.getValueFromObject(context, key);
39-
if (value) {
40-
if (typeof value == 'object') {
41-
return JSON.stringify(value);
42-
} else {
43-
return (<any>value).toString();
36+
private static replaceExpression(str: string, context: object, converters: object) {
37+
return str
38+
.replace(this._converterExpression, match => {
39+
if (!converters) {
40+
return '';
4441
}
45-
}
46-
return '';
47-
});
42+
return this.evalInContext(match.substring(3, match.length - 3).trim(), { ...converters, ...context });
43+
})
44+
.replace(this._expression, match => {
45+
let key = match.substring(2, match.length - 2);
46+
let value = this.getValueFromObject(context, key);
47+
if (value) {
48+
if (typeof value == 'object') {
49+
return JSON.stringify(value);
50+
} else {
51+
return (<any>value).toString();
52+
}
53+
}
54+
return '';
55+
});
4856
}
4957

50-
private static renderNode(node: Node, context: object) {
58+
private static renderNode(node: Node, context: object, converters: object) {
5159
if (node.nodeName === '#text') {
52-
node.textContent = this.replaceExpression(node.textContent, context);
60+
node.textContent = this.replaceExpression(node.textContent, context, converters);
5361
return node;
5462
}
5563

@@ -59,7 +67,7 @@ export class TemplateHelper {
5967
if (nodeElement.attributes) {
6068
for (let i = 0; i < nodeElement.attributes.length; i++) {
6169
let attribute = nodeElement.attributes[i];
62-
nodeElement.setAttribute(attribute.name, this.replaceExpression(attribute.value, context));
70+
nodeElement.setAttribute(attribute.name, this.replaceExpression(attribute.value, context, converters));
6371
}
6472
}
6573

@@ -81,7 +89,7 @@ export class TemplateHelper {
8189

8290
if (childElement.dataset.if) {
8391
let expression = childElement.dataset.if;
84-
if (!this.evalInContext(expression, context)) {
92+
if (!this.evalBoolInContext(expression, context)) {
8593
removeChildren.push(childElement);
8694
childWillBeRemoved = true;
8795
} else {
@@ -101,10 +109,10 @@ export class TemplateHelper {
101109
if (childElement.dataset.for && !childWillBeRemoved) {
102110
loopChildren.push(childElement);
103111
} else if (!childWillBeRemoved) {
104-
this.renderNode(childNode, context);
112+
this.renderNode(childNode, context, converters);
105113
}
106114
} else {
107-
this.renderNode(childNode, context);
115+
this.renderNode(childNode, context, converters);
108116
}
109117

110118
// clear the flag if the current node wasn't data-if
@@ -145,7 +153,7 @@ export class TemplateHelper {
145153
newContext['index'] = j;
146154

147155
let clone = childElement.cloneNode(true);
148-
this.renderNode(clone, newContext);
156+
this.renderNode(clone, newContext, converters);
149157
nodeElement.appendChild(clone);
150158
}
151159
}
@@ -155,10 +163,21 @@ export class TemplateHelper {
155163
return node;
156164
}
157165

158-
static evalInContext(expression, context) {
166+
private static evalBoolInContext(expression, context) {
159167
return new Function('with(this) { return !!(' + expression + ')}').call(context);
160168
}
161169

170+
private static evalInContext(expression, context) {
171+
let func = new Function('with(this) { return ' + expression + ';}');
172+
let result;
173+
try {
174+
result = func.call(context);
175+
} catch (e) {
176+
console.log(e);
177+
}
178+
return result;
179+
}
180+
162181
/**
163182
* Render a template into a HTMLElement with the appropriate data context
164183
*
@@ -174,17 +193,18 @@ export class TemplateHelper {
174193
*
175194
* @param template the template to render
176195
* @param context the data context to be applied
196+
* @param converters the converter functions used to transform the data
177197
*/
178-
public static renderTemplate(template: HTMLTemplateElement, context: object) {
198+
public static renderTemplate(template: HTMLTemplateElement, context: object, converters?: object) {
179199
if (template.content && template.content.childNodes.length) {
180200
let templateContent = template.content.cloneNode(true);
181-
return this.renderNode(templateContent, context);
201+
return this.renderNode(templateContent, context, converters);
182202
} else if (template.childNodes.length) {
183203
let div = document.createElement('div');
184204
for (let i = 0; i < template.childNodes.length; i++) {
185205
div.appendChild(template.childNodes[i].cloneNode(true));
186206
}
187-
return this.renderNode(div, context);
207+
return this.renderNode(div, context, converters);
188208
}
189209
}
190210
}

src/components/templatedComponent.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@ export abstract class MgtTemplatedComponent extends MgtBaseComponent {
1414
private _slotNamesAddedDuringRender = [];
1515
protected templates = {};
1616

17+
public templateConverters: any = {};
18+
19+
constructor() {
20+
super();
21+
22+
this.templateConverters.lower = (str: string) => str.toLowerCase();
23+
this.templateConverters.upper = (str: string) => str.toUpperCase();
24+
}
25+
1726
protected update(changedProperties) {
1827
this.templates = this.getTemplates();
1928
this._slotNamesAddedDuringRender = [];
@@ -79,7 +88,7 @@ export abstract class MgtTemplatedComponent extends MgtBaseComponent {
7988
}
8089
}
8190

82-
let templateContent = TemplateHelper.renderTemplate(this.templates[templateType], context);
91+
let templateContent = TemplateHelper.renderTemplate(this.templates[templateType], context, this.templateConverters);
8392

8493
let div = document.createElement('div');
8594
div.slot = slotName;

0 commit comments

Comments
 (0)