Compile template tag to hyperscript
marcodpt opened this issue ยท 17 comments
I created a view engine based on the DOM template tag, which compiles to hyperscript functions:
See for example what the README.md example looks like:
<!DOCTYPE html>
<html lang="en">
<head>
<title>hyperapp + tint</title>
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico">
<script type="module">
import { h, text, app } from "https://unpkg.com/hyperapp"
import tint from 'https://cdn.jsdelivr.net/gh/marcodpt/tint/index.js'
const render = tint(h, text)
const root = document.getElementById("app")
app({
init: {
todos: [],
value: "",
AddTodo: state => ({
...state,
value: "",
todos: state.todos.concat(state.value),
}),
NewValue: (state, event) => ({
...state,
value: event.target.value,
})
},
view: state => render('my-todo', state, root.tagName, {id: 'app'}),
node: root
})
</script>
</head>
<body>
<main id="app"></main>
<template id="my-todo">
<h1>To do list</h1>
<input type="text" :value="value" :oninput="NewValue">
<ul>
<li :each="todos" :text></li>
</ul>
<button :onclick="AddTodo">New!</button>
</template>
</body>
</html>
In this example, what was achieved was the total separation of the javascript layouts, without using build steps, and only introducing as a dependency a library with ~130 lines of javascript code.
Which I find extremely useful for maintaining large projects, especially working with designers who only have knowledge of css and html.
Another great feat that was achieved is that in the examples I build the same app:
Without changing absolutely anything in the model and layouts.
I'm open to listening to suggestions, solving bugs, and helping with any problems or features that the community deems necessary.
Sorry for opening an issue, but I didn't know a better way to expose my work which I believe is extremely useful for working with frameworks based on hyperscript.
Keep up the wonderful work you guys do with the hyperapp!
Yes, you are probably right.
What should I do? Submit a pull request in this repository?
Not that I have a say, but I think you have a really cool example, so I was following along. Looks like there are guidelines: https://github.com/jorgebucaran/hyperawesome/blob/master/CONTRIBUTING.md#contribution-guidelines
(ps If you want wider exposure/usage I would consider renaming your package .. just saying)
Thanks for your comments, I will follow your suggestions.
I submitted a pull request and renamed the repository to a friendlier name.
Thanks for your help and patience :)!
I was thinking how it would look if you wrap app
:
(untested)
function tintApp(options) {
options = {
...options,
view: state => tint(h, text)(options.templateId, state, options.node.tagName, {id: options.node.id})
}
returns app(options)
}
so tintApp
accepts templateId
but does not accept view
which maybe makes the intention explicit that you render template and not view logic ...
Another thing to note is that in order for this to work you need all Actions to be part of the state (correct?), which is conceptually a bit strange and not the standard way HA apps are written .. this can have some side effects ie Actions changing other Actions which can be a bug or a feature depends how you think of it
So one options can be to separate Actions from state and combine them to generate scope, this way the state passed to actions and view does not include the actions themselves
ie: (untested)
function tintApp(options) {
options = {
...options,
view: state => tint(h, text)(options.templateId, { ...state, ...options.actions }, options.node.tagName, {id: options.node.id})
}
returns app(options)
}
then:
tintApp({
init: {
todos: [],
value: "",
},
actions: {
AddTodo: state => ({
...state,
value: "",
todos: state.todos.concat(state.value),
}),
NewValue: (state, event) => ({
...state,
value: event.target.value,
})
},
templateId: 'my-todo',
node: document.getElementById("app")
})
Your analysis is correct and brilliant.
I had noticed this problem of functions being part of the state.
My initial concern was to create a template engine that would naturally communicate with a framework in case the views became dynamic, as a significant part of my team's work is simply templates.
Thank you very much for your very well placed considerations about the api, this wrapper. Discussions about the functions signatures are the most productive. Implementations die, ideas live forever.
My understanding of the problem I'm trying to touch is as follows.
I recognize the hyperapp as the most brilliant, pure and perfect architecture of a framework.
Hyperscript is the power of a Turing Complete language when creating layouts.
But maybe you can think that you already has this power in creating and modifing the state with javascript, and layouts should not be Turing Complete, but expressed in a concise and minimalistic way.
The established standard and the way the designer thinks is in html and css.
Mustache has a very interesting philosophy. Modify your model in javascript, do not put logic in views.
I came here to try to make a connection between the elm architecture and the mustache philosophy of non-logic views.
I tried everything. Use a complete framework like vue, use views in mustache, use hyperapp only. In each solution there was a clear gain to the previous one but it could not solve in the cases of simple templates in a similar and compatible way to problems with a complete framework.
The language and the framework it has a very strong meaning, because it directs the way you reason. The day we have a framework like the hyperapp without build steps using html and css we will have what I consider a dream.
I may be heading in the wrong direction. But as I understand the problem, I'm getting closer to my dream.
My idea is to write a wrapper for each framework and import the wrapper directly.
For example if you are going to use it as a template engine:
import render from 'https://cdn.jsdelivr.net/gh/marcodpt/tint/template.js'
If using with hyperapp:
import app from 'https://cdn.jsdelivr.net/gh/marcodpt/tint/hyperapp.js'
That in case I'll add this idea of โโyours if you have no problem. (Or if you want to add a pull request I accept it immediately even for the sake of merit)
I think now is the time for me to walk on my own two feet.
Thank you very much for the reception, and for leaving this issue open. I will continue the discussion and development here:
@kofifus I've embedded your code in tint, I think it looks great.
The example with superfine
I also found very interesting.
In the end what has been presented here is a low-level library to build these wrappers.
I will pray every day that real programmers will help me finish these wrappers correctly and accurately.
I wanted to thank you again for your excellent help.
@kofifus, v2 of tint is out there.
If that's not simple and beautiful, I don't know what it is:
<!DOCTYPE html>
<html>
<head>
<script type="module">
import app from "https://cdn.jsdelivr.net/gh/marcodpt/tint/hyperapp.js"
app({
actions: {
AddTodo: state => ({
...state,
value: "",
todos: state.todos.concat(state.value),
}),
NewValue: (state, event) => ({
...state,
value: event.target.value,
})
},
init: {
todos: [],
value: ""
},
node: document.getElementById("app")
})
</script>
</head>
<body>
<main id="app">
<h1>To do list</h1>
<input type="text" :value="value" :oninput="NewValue">
<ul>
<li :each="todos" :text></li>
</ul>
<button :onclick="AddTodo">New!</button>
</main>
</body>
</html>
I did my best to simplify things a lot. I also have an awesome example with superfine.
Please consider taking a look at the documentation (it's been greatly improved). And if you think it has some value, consider showing others in the community what I've done.
I really think this approach can help hyperapp become more popular. And also migration between other hyperscript frameworks would be much easier. This tends to further help the best framework.
Looks super clean! โค๏ธ
Okay, I had left the old version of superfine in the documentation.
It was my mistake. If anyone saw it, I apologize. It has been corrected.
I need to make some more minor tweaks and I believe this library
it will be very close to being very stable.
If you look at frameworks like vue, svelte and react they make a lot of noise
with the fashion of SFC (Single file components), making a big mess with
these javascript build tools.
This in my humble personal opinion is something much better and much simpler
than these SFC.
I fully agree that hyperscript is super fast, no need to add
elements in the DOM template tag, and consequently the browser does not need to
interpret it, nor the complication of an extra template library to
turn it into a view. However, this type of operation in many cases is not
necessarily costly and creates a unique developer experience.
@marcodpt you can do elegant web components with hyperspp .. see https://tinyurl.com/4hwr78j7
lit html is good. but I wanted to use the templates in the dom. seems like the right place to put them.
and I would like to add that in the TODO app example, the template helped the hyperapp, as the vast majority of tags were already available in the dom.
you could easily use my template with a server side rendering strategy for example without any work.
Good Luck to lit html