-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: Add support for a 'Fragment' tag #38
base: v2
Are you sure you want to change the base?
Conversation
One challenge with using fragments in that they behave a little strangely when being added to the DOM. f = document.createDocumentFragment()
f.appendChild(document.createElement('p'))
f.childElementCount // => 1
document.body.appendChild(f)
f.childElementCount // => 0 This means we no longer have a reference to the containing element so dynamic content won't work inside document fragments. A related feature that I've been thinking about adding is a way to register specific tags to arbitrary handlers. That way someone could register document fragments or other element constructors to take more control over customized behavior. Something like: ul
Item(@click @text)
span Neat! Jadelet.register
Item: ({click, text, children}) ->
el = document.createElement 'li'
el.onclick = click;
el.append(text, children...)
return el It needs a bit more thought, especially around observable properties and content but I think there might be something to it. |
Can you show me an example of this? <!-- html -->
<div id="app"></div>
<ul id="list"></ul> o = Jadelet.Observable
t = Jadelet.exec
app = document.getElementById("app")
lst = document.getElementById("list")
tpl1 = t(`ul
li @a
li @b
li @c`)
tpl2 = t(`Fragment
li @a
li @b
li @c`)
x = o(1)
obj = {
a: o(() => x() + 1),
b: o(() => x() + 2),
c: o(() => x() + 3)
}
app.appendChild(tpl1(obj))
lst.appendChild(tpl2(obj))
/*
html result:
<div id="app">
<ul>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</div>
<ul id="list">
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
*/
x(5)
/*
html result:
<div id="app">
<ul>
<li>6</li>
<li>7</li>
<li>8</li>
</ul>
</div>
<ul id="list">
<li>6</li>
<li>7</li>
<li>8</li>
</ul>
*/ |
Try this one: tpl = t(`Fragment
@a
li @b
li @c`) |
I've got the following result with the same HTML. tpl1 = t(`li @d`)
tpl2 = t(`Fragment
@a
li @b`)
x = o(1)
objA = {
d: o(() => x() + 1)
}
obj = {
a: o(() => tpl1(objA)),
b: o(() => x() + 2)
}
lst.appendChild(tpl2(obj))
/*
html result:
<div id="app"></div>
<ul id="list">
<li>2</li>
<li>3</li>
</ul>
*/
x(5)
/*
html result:
<div id="app"></div>
<ul id="list">
<li>6</li>
<li>7</li>
</ul>
*/ So far, it looks like it's working. tpl3 = t(`ul
@a
li @b`)
app.appendChild(tpl3(obj))
/*
html result:
<div id="app">
<ul>
<li>6</li>
<li>7</li>
</ul>
</div>
<ul id="list">
<li>7</li>
</ul>
*/ So, the I changed the HTML a bit to check if this is the issue with the <div id="app"></div>
<div id="app2"></div> app.appendChild(tpl3(obj))
/*
html result
<div id="app">
<ul>
<li>2</li>
<li>3</li>
</ul>
</div>
<div id="app2"></div>
*/
app2.appendChild(tpl3(obj))
/*
html result
<div id="app">
<ul>
<li>3</li>
</ul>
</div>
<div id="app2">
<ul>
<li>2</li>
<li>3</li>
</ul>
</div>
*/ I thought this might be connected to the observable functions, so I tried the code without them, and the result is the same as above. objA = { d: x() + 1 }
obj = { a: tpl1(objA), b: x() + 2 } Even without the observables, the behavior stays the same. objA = { d: 1 }
obj = { a: tpl1(objA), b: 2 } Did I miss something in my code? |
I'm still not sure about the disappearing subtemplate, but here is the working example. o = Jadelet.Observable
t = Jadelet.exec
app = document.getElementById("app")
lst = document.getElementById("list")
x = o(1)
function objA() {
var tpl = t(`li @d`),
obj = {d: o(() => x() + 1)}
return tpl(obj)
}
function obj() {
return {
a: objA(),
b: o(() => x() + 2)
}
}
function elm1() {
var tpl = t(`ul
@a
li @b`)
return tpl(obj())
}
function elm2() {
var tpl = t(`Fragment
@a
li @b`)
return tpl(obj())
}
function test() {
app.appendChild(elm1())
lst.appendChild(elm2())
}
test()
/*
<div id="app">
<ul>
<li>2</li>
<li>3</li>
</ul>
</div>
<ul id="list">
<li>2</li>
<li>3</li>
</ul>
*/
x(5)
/*
<div id="app">
<ul>
<li>6</li>
<li>7</li>
</ul>
</div>
<ul id="list">
<li>6</li>
<li>7</li>
</ul>
*/ |
I've been thinking... There may be a way to do this by instead of tracking the parent node, to track the first child in a fragment and the number of sibling nodes added. This assumes that other nodes aren't inserted in between nodes controlled by Jadelet. In this case wrapping the entire template in a document fragment by default would allow for root level siblings and should still be able to handle reactively inserting/removing. One additional edge case would be to track the parent/previous sibling where the template is appended to the DOM to handle the case where zero nodes are added, or to insert a comment node as a placeholder. |
This PR adds support for a fragments.
Currently Jadelet requires a single root element (e.g., HTML element) in the template.
However, there are cases where HTML root element can be redundant, or might produce invalid HTML.
For example, there might be a case where we would like to output the content in the existing
<ul>
element.With this PR, we would be able to use a (reserved) word "Fragment" or a symbol
<>
in the template string.