I was thinking about a small improvement, I am not sure this is something the Polymer Team is interested about but I thought I would share because that's one of my hopes.
Let's think about this function
export const cookieClicker = () => {
let template
let clickCount = 0
const onClick = () => {
clickCount++
template.update() // feature request
}
template = html`
<style>
#cookie {
width: 100px;
height: 100px;
background-image: url(./cookie.png)
}
</style>
<div id="cookie" @click="${onClick}"></div>
<div>count : ${clickCount}</div>
`
return template
}
In short
export const cookieClicker = () => {
let clickCount = 0
const template = html`
<style>
#cookie {
width: 100px;
height: 100px;
background-image: url(./cookie.png)
}
</style>
<div id="cookie" @click="${_ => {clickCount++; template.update()}}"></div>
<div>count : ${clickCount}</div>
`
return template
}
Now I can use the function to make a cookie clicker and pass it in the render function.
Later on I don't have to worry where the template is rendered, I can just call the update function.
This way the function used to generate the template acts like a small industry on its own.
This is what lit-element provides
@CaptainCodeman Correct me if I am wrong but lit-element requires a end compilation step for production. Correct me again if I am wrong but If I type <my-element></my-element><my-element></my-element> I would have two different instances of my-element, therefore If I want to seamlessly synchronise data between the two I would need to implement a solution like redux which is another extra step (and not a small one). Plus lit-element uses shadow-doms.
whereas with lit-html I can type :
const cookieClicker1 = cookieClicker()
render(cookieClicker1, menuBottom)
render(cookieClicker1, mainPage)
/* and later */
cookieClicker1.update()
That would update the same industry in the application.
I need a quick solution with global stylings, and lit-html is great for that.
Take a look at Gluon https://www.npmjs.com/package/@gluon/gluon It's a tiny
footprint.
Ok so I realized one thing, TemplateResult stores the values of the template as-is in the values property. So technically this wouldn't be possible to implement as it doesn't keep track of the closure variables.
It is very possible to implement something that looks _very_ similar to your example:
class CookieFactory {
constructor() {
this.renderTargets = [];
this.clickCount = 0;
}
addRenderTarget(target) {
this.renderTargets.push(target);
}
get template() {
return html`
<style>
#cookie {
width: 100px;
height: 100px;
background-color: red;
}
</style>
<div id="cookie" @click="${this.click}"></div>
<div>count : ${this.clickCount}</div>
`
}
click() {
this.clickCount += 1;
this.update();
}
update() {
this.renderTargets.forEach(target => {
render(this.template, target, {eventContext: this} )
})
}
}
const thing = new CookieFactory();
thing.addRenderTarget(document.getElementById('one'));
thing.addRenderTarget(document.getElementById('two'));
thing.update();
Is this a good idea? I don't know. It works in this toy example, but when the content becomes more complex it will quickly become pretty fragile. I'd recommend using some base class like Gluon or LitElement. You can find two different implementations here: https://codepen.io/ruphin/pen/PvoebW?editors=1010 (as you can see, no compilation steps)
@ruphin Thanks a lot for your insight, this is very interesting solution which I am implementing for my small app.
@vdegenne
Correct me if I am wrong but lit-element requires a end compilation step for production.
Not at all. LitElement works with plain JavaScript. The examples on the site are often in TypeScript, because that allows you to use class fields and decorators, but you can write plain JavaScript with the static properties field.
Correct me again if I am wrong but If I type
I would have two different instances of my-element
Absolutely, and it really can't be any other way. The browser ensures that two elements are actually two instances.
If I want to seamlessly synchronise data between the two I would need to implement a solution like redux which is another extra step (and not a small one).
You don't need Redux, basically ever. You can achieve the same thing with any kind of registration/dispatch system. You can have a function in your element's module that registers instances and another that sends messages to them.
Plus lit-element uses shadow-doms.
As should you ;) (most of the time)
To add another point specifically about TemplateResult. A TemplateResult is simply a description of what you want the DOM to be. It's a trivial wrapper around the values and a reference to a template, and only captures the _current_ value of some state, and should never be mutated.
Another approach, if you really wanted something like a component model without web components, would be to return a DocumentFragment:
const cookieCutter = () => {
const fragment = document.createDocumentFragment();
render(html`...`, fragment);
return fragment;
}
document.body.append(cookieCutter());
Thanks for taking the time to explain @justinfagnani . But most importantly thanks a lot for developing and sharing with us this amazing tool that makes web coding even more enjoyable and effective 馃挴