Skip to content

Commit d480a5e

Browse files
committed
[ Add ] Data store & scope
1 parent 0ca5210 commit d480a5e

File tree

6 files changed

+145
-59
lines changed

6 files changed

+145
-59
lines changed

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "dom-renderer",
3-
"version": "0.3.0",
3+
"version": "0.4.0",
44
"description": "Template engine based on HTML 5 & ECMAScript 6",
55
"keywords": [
66
"template",

source/Model.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
const view_data = new WeakMap();
2+
3+
/**
4+
* @abstract
5+
*/
6+
export default class Model {
7+
/**
8+
* @param {Object} parent - Scope of this Model
9+
*/
10+
constructor(parent) {
11+
if (this.constructor === Model)
12+
throw TypeError('Model() is an abstract class');
13+
14+
view_data.set(this, parent ? Object.setPrototypeOf({}, parent) : {});
15+
}
16+
17+
/**
18+
* @type {Object}
19+
*/
20+
get data() {
21+
return view_data.get(this);
22+
}
23+
24+
/**
25+
* @type {Object}
26+
*/
27+
get scope() {
28+
return Object.getPrototypeOf(view_data.get(this));
29+
}
30+
31+
/**
32+
* @param {Object} data
33+
*
34+
* @return {Object}
35+
*/
36+
patch(data) {
37+
const _data_ = this.data,
38+
update = Object.setPrototypeOf({}, this.scope);
39+
40+
Object.assign(_data_, data);
41+
42+
for (let key in _data_)
43+
if (
44+
_data_.hasOwnProperty(key) &&
45+
(typeof data[key] === 'object' ||
46+
typeof _data_[key] !== 'object')
47+
)
48+
update[key] = _data_[key];
49+
50+
return update;
51+
}
52+
}

source/View.js

Lines changed: 52 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,27 @@
1+
import Model from './Model';
2+
13
import { parseDOM, scanTemplate, stringifyDOM, attributeMap } from './utility';
24

35
import Template from './Template';
46

5-
const forEach = [].forEach;
7+
const { forEach, push } = Array.prototype;
68

79
const view_template = Symbol('View template'),
8-
view_top = new Map();
10+
view_top = new Map(),
11+
view_injection = Symbol('View injection'),
12+
view_varible = ['view', 'scope'];
913

10-
export default class View extends Map {
14+
export default class View extends Model {
1115
/**
12-
* @param {String} template
16+
* @param {String} template
17+
* @param {?Object} scope - Data of parent View
18+
* @param {Object} [injection={}] - Key for Template varible
1319
*/
14-
constructor(template) {
15-
super()[view_template] = template + '';
20+
constructor(template, scope, injection = {}) {
21+
super(scope);
22+
23+
(this[view_template] = template + ''),
24+
(this[view_injection] = injection);
1625

1726
forEach.call(parseDOM(template).childNodes, node => {
1827
view_top.set(node, this);
@@ -43,17 +52,16 @@ export default class View extends Map {
4352
* @param {Element} root
4453
*/
4554
static clear(root) {
46-
forEach.call(
47-
root.childNodes,
48-
node => view_top.delete(node) && node.remove()
49-
);
55+
Array.from(view_top.keys())
56+
.filter(node => root.compareDocumentPosition(node) & 16)
57+
.forEach(node => (view_top.delete(node), node.remove()));
5058
}
5159

5260
/**
5361
* @return {View}
5462
*/
5563
clone() {
56-
return new View(this[view_template]);
64+
return new View(this[view_template], this.scope, this[view_injection]);
5765
}
5866

5967
/**
@@ -73,59 +81,52 @@ export default class View extends Map {
7381
}
7482

7583
/**
76-
* @private
77-
*
78-
* @param {String} type
79-
* @param {Element} element
80-
* @param {Template|View} renderer
81-
*/
82-
addNode(type, element, renderer) {
83-
this.set({ type, element }, renderer);
84-
}
85-
86-
/**
87-
* @private
84+
* @protected
8885
*
8986
* @param {Element} root
9087
*/
9188
parseTree(root) {
89+
const injection = view_varible.concat(
90+
Object.keys(this[view_injection])
91+
);
92+
9293
scanTemplate(root, Template.Expression, '[data-view]', {
9394
attribute: ({ ownerElement, name, value }) => {
9495
name = attributeMap[name] || name;
9596

96-
this.addNode(
97-
'Attr',
98-
ownerElement,
99-
new Template(
97+
push.call(this, {
98+
type: 'Attr',
99+
element: ownerElement,
100+
renderer: new Template(
100101
value,
101-
['view'],
102+
injection,
102103
name in ownerElement
103104
? value => (ownerElement[name] = value)
104105
: value => ownerElement.setAttribute(name, value)
105106
)
106-
);
107+
});
107108
},
108109
text: node => {
109110
const { parentNode } = node;
110111

111-
this.addNode(
112-
'Text',
113-
parentNode,
114-
new Template(
112+
push.call(this, {
113+
type: 'Text',
114+
element: parentNode,
115+
renderer: new Template(
115116
node.nodeValue,
116-
['view'],
117+
injection,
117118
parentNode.firstElementChild
118119
? value => (node.nodeValue = value)
119120
: value => (parentNode.innerHTML = value)
120121
)
121-
);
122+
});
122123
},
123124
view: node =>
124-
this.addNode(
125-
'View',
126-
node,
127-
new View(View.getTemplate(node).trim())
128-
)
125+
push.call(this, {
126+
type: 'View',
127+
element: node,
128+
renderer: new View(View.getTemplate(node).trim(), this.data)
129+
})
129130
});
130131
}
131132

@@ -135,11 +136,20 @@ export default class View extends Map {
135136
* @return {View}
136137
*/
137138
render(data) {
138-
this.forEach((renderer, { type, element }) => {
139+
data = this.patch(data);
140+
141+
const injection = [data, this.scope].concat(
142+
Object.values(this[view_injection])
143+
);
144+
145+
forEach.call(this, ({ type, element, renderer }) => {
139146
switch (type) {
140147
case 'Attr':
141148
case 'Text':
142-
return renderer.evaluate(element, data);
149+
return renderer.evaluate.apply(
150+
renderer,
151+
[element].concat(injection)
152+
);
143153
}
144154

145155
var _data_ = data[element.dataset.view];

test/View.js

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ describe('DOM View', () => {
1515
it('Parsing', () => {
1616
view = new View(view);
1717

18-
Array.from(view.keys(), ({ type }) => type).should.match([
18+
Array.from(view, ({ type }) => type).should.match([
1919
'Attr',
2020
'Text',
2121
'View',
@@ -32,10 +32,12 @@ describe('DOM View', () => {
3232
3333
<ul data-view="profile">
3434
<template>
35-
<li>\${view.URL}</li>
35+
<li title="\${scope.name}">
36+
\${view.URL}
37+
</li>
3638
<li>\${view.title}</li>
3739
</template>
38-
<li>https://tech-query.me/</li>
40+
<li title="TechQuery">https://tech-query.me/</li>
3941
<li>Web/JavaScript full-stack engineer</li></ul>
4042
4143
<ol data-view="job">
@@ -46,29 +48,49 @@ describe('DOM View', () => {
4648
`);
4749
});
4850

51+
function getLasts() {
52+
return view.topNodes
53+
.map(node => node.nodeType === 1 && node.lastChild)
54+
.filter(Boolean);
55+
}
56+
4957
/**
50-
* @test {View#render}
58+
* @test {Model#patch}
5159
*/
5260
it('Updating', () => {
53-
function getLasts() {
54-
return view.topNodes
55-
.map(node => node.nodeType === 1 && node.lastChild)
56-
.filter(Boolean);
57-
}
58-
5961
const last = getLasts(),
6062
_data_ = Object.assign({}, data);
6163

6264
_data_.name = 'tech-query';
63-
_data_.profile = null;
64-
delete _data_.job;
65+
delete _data_.profile;
66+
_data_.job = null;
6567

6668
view.render(_data_);
6769

6870
const now = getLasts();
6971

7072
now[0].should.not.be.equal(last[0]);
71-
now[1].nodeName.should.not.be.equal('LI');
72-
now[2].should.be.equal(last[2]);
73+
now[1].should.be.equal(last[1]);
74+
now[2].nodeName.should.not.be.equal('LI');
75+
76+
(view + '').should.be.equal(`
77+
<h1>tech-query</h1>
78+
79+
<ul data-view="profile">
80+
<template>
81+
<li title="\${scope.name}">
82+
\${view.URL}
83+
</li>
84+
<li>\${view.title}</li>
85+
</template>
86+
<li title="TechQuery">https://tech-query.me/</li>
87+
<li>Web/JavaScript full-stack engineer</li></ul>
88+
89+
<ol data-view="job">
90+
<template>
91+
<li>\${view.title}</li>
92+
</template>
93+
</ol>
94+
`);
7395
});
7496
});

test/source/index.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ <h1 hidden="${! view.name}">
55

66
<ul data-view="profile">
77
<template>
8-
<li>${view.URL}</li>
8+
<li title="${scope.name}">
9+
${view.URL}
10+
</li>
911
<li>${view.title}</li>
1012
</template>
1113
</ul>

0 commit comments

Comments
 (0)