Closed jorgebucaran closed 7 years ago
Something like this would be lovely.
I have a slightly different point of view on this. Creating and maintaining a new template function is a load of work. Already proven implementations already exist, we should leverage them instead.
The only thing that bother us for the moment is more syntax-related than feature-related. So I propose that hypertag
(nice name btw!) would be a wrapper to hyperx
(or equivalent) and bring syntactic stuff on the table. And nothing prevents to implement parsing later.
I think there is a typo in your example as template functions only allow to do this:
app({
view: model => html`
- <Hello name="world" />
+ ${Hello("world")}
`
})
But it highlights the fact that template functions have function purity in mind and don't care about templates composition (i.e. components). Which is perfectly ok because it fits any use cases.
Still in a real world use case, referencing other components like jsx
allows you to do is something really appreciable.
So a good first use case for hypertag
would be to add this feature and be able to do something like this:
import { app } from "hyperapp"
import { tag, html } from "hypertag"
tag('Hello', ({ name }) => html`<h1>{name}</h1>`)
app({
view: model => html`
<Hello name="world" />
`
})
Other use cases could be treated like arguments passing cascade that purity imposes. We could automatically pass model
and actions
to view by default for example.
To sump up, I would propose hypertag
= hyperx
+ syntactic sugar
than yet another template function.
@ngryman Nice writeup! 👍
EDIT: I'll still try to write the template function just for fun :)
Your example should be modified because any classic template function only allows to do this:
I'm not sure I'm following. If it doesn't support writing JSX-style components out of the box like this:
html`<MyComponent />`
I'm not sure it would be much fun to use over JSX. 🤔
@jbucaran It's because it works on any es6
environment without any tooling involved. es6
support is getting momentum and in a couple of years template literals will just work for any browser: http://kangax.github.io/compat-table/es6/#test-template_literals.
@ngryman I think we are talking about two different things.
If I ever build a template literal like hyperx, it must support:
html`<MyComponent />`
out of the box, otherwise it's a no-no.
With regards to:
works on any es6 environment without any tooling involved.
It doesn't really matter if they are natively supported or not, I don't want to ship code to production (or anywhere) that uses a template function XML parser (template literals for strings are okay maybe). In other words, you always want to compile your templates to native function calls (like in hyperapp's case your views to native h
calls).
If I ever build a template literal like hyperx, it must support: ...`
html`<MyComponent />`
As it's not possible with hyperx
out-of-the-box (or any template function I know, or I'm missing something), I thought that was a typo. In fact I don't know how you can make this work if you don't name you component somewhere. Does hyperxify
allow to achieve this?
It doesn't really matter if they are natively supported or not, I don't want to ship code to production (or anywhere) that uses a template function XML parser
Well I think we diverge here. For me it's totally ok to ship this to production when you target specific environments and you don't care about the performance cost of parsing.
For example if I develop a Chrome addon or an Electron app with just some basic interactions, I can live with shipping es6
template literals and do the parsing at runtime.
To illustrate my misunderstanding on the first point: https://github.com/trueadm/t7#components.
@ngryman Well I think we diverge here. For me it's totally ok to ship this to production when you target specific environments and you don't care about the performance cost of parsing.
Nope, I'm sure we don't diverge here. I wouldn't care either in that specific environment. Here is the thing though, I don't know if that environment exists for sure, or at least they are extremely rare. You usually do care about the cost of parsing, specially in the case of JSX/Hyperx.
Run js-framework-benchmarks
or DBMonster
, etc., but don't compile Hyperx into native function calls and see for yourself. We're not talking about 20% slower, it's probably more like 200% to 500% slower.
To illustrate my misunderstanding on the first point: https://github.com/trueadm/t7#components.
😞
So, @jbucaran, you want to create from scratch a template library like Hyperx or t7? Those are both kinda large. I'd hope it would just be an alternative to use like everything else. t7 inspired me to try my own hand at an ES6 template solution for components, that was before I stubbled upon Hyperapp on Reddit. Here's a pen of what I came up with: http://codepen.io/rbiggs/pen/pePajZ I used a loading mechanism to notify a component about child components so that I could use them as tags inside the parent. Because I had React users in mind, I based it off of a Component class. This solution allows you to create sibling elements and append them to a container without having to wrap them in an enclosing tag. I also created a DOM patcher in 1KB that is similar to Morphdom so I could update the DOM using the result for a template literal - no virtual DOM objects necessary. But that's not in this pen. I also created a style module so I could create virtual stylesheets scoped to the component, like JSX does, but for template literals. I just never came up with a model/update solution for it.
I'd hope it would just be an alternative to use like everything else.
Yes, obviously. For real projects I'd probably use only JSX.
Awesome, thanks, I've forked it and will use it while I learn how to do this.
I'm new to this too, but I made a similar thing here.
I also created a style module so I could create virtual stylesheets scoped to the component, like JSX does, but for template literals. I just never came up with a model/update solution for it.
Can you expand on this? 🤔
yeah, you just pass it an object describing your styles, and it uses the component as the scope for the style rules and then appends the stylesheet to the document. But if a stylesheet's already there, then it just adds the new rules. Was trying to be efficient at that ;-) I can make you a pen to see it in action if you want. No biggie.
By the way, with t7, if you cut out all the code for React and Mythril and just leave the universal part you can reduce it by half.
By the way, with t7, if you cut out all the code for React and Mythril and just leave the universal part you can reduce it by half.
Yeah, you're right. Maybe I should rather look into #88. I'm just leaving this open issue to see if I get around this someday. Totally not a priority.
Actually, #88 would be great. t7 is far more advanced than Hyperx. I love how t7 handles components. Maybe you could come up with a separate plugin to make t7 work with your h
function. That way i'd be a separate load from hyperapp, not affecting its overall size.
Maybe you could come up with a separate plugin to make t7 work with your h function.
Yup, yup, that's idea exactly.
t7 is far more advanced than Hyperx.
Yeah, but it's holding me back a bit. Need to study more, etc.
That way i'd be a separate load from hyperapp, not affecting its overall size.
No worries, we've changed how we deal with this from the early days (3 weeks ago lol) when hyperx was a dependency and we're not going back to that.
On a totally different note, I see you're always talking about using Codemirror for projects. I've also done some major projects with it in the past. However, I hated it, hard to customize, etc. Then I discovered Ace. Loved it. Easier to customize. By fav is Ace with Tern for code completion and Intellisense like how Visual Studio Code works.
@rbiggs I tried using Ace before CodeMirror, but couldn't get it to work (this was before HyperApp). I'd love to give it another shot.
Odd, I found it so much easier to use. I was using it with RiotJS. I started out with Codemirror, because that's what the client had. But they wanted so much customization. The code base was hard to sort out, the project lead was not friendly. Took a look at Ace, big open source group, lots of participation, easier to understand how all the parts work. Ace was easy to customize popups, etc. whereas with Coremirror you're stuck with whatever Marijn decided you needed.
Maybe I'll fork your CodeMirror example and convert it to Ace. And then add some other features. I'll put it together sometime later next week when I have time. I'll keep you informed.
Maybe I'll fork your CodeMirror example and convert it to Ace. And then add some other features. I'll put it together sometime later next week when I have time. I'll keep you informed.
Awesome! Please do :) 🙏
I got hyperx to work as desired(afaict) pretty easily:
import { h, app } from 'hyperapp';
import hyperx from 'hyperx';
const hx = hyperx((t, a, c) => h(t.match(/^[A-Z]/) ? eval(t) : t, a, c));
const state = {
count: 0
}
const actions = {
down: value => state => ({ count: state.count - value }),
up: value => state => ({ count: state.count + value })
}
const Component = ({count}) => {
return hx`<h1>${count}</h1>`;
}
const view = (state, actions) => hx`
<div>
<Component count=${state.count}></Component>
<button onclick=${() => actions.down(1)}>-</button>
<button onclick=${() => actions.up(1)}>+</button>
</div>
`;
app(state, actions, view, document.body);
That's a cool & "evil" trick @dschep! 😃
That is neat and evil, sadly. I'd be afraid to use eval
in production. Would be so nice to have a Hyperx type template literal solution that didn't require Hyperxify, it just converted to what you needed at the end automatically, sigh.
100% agree with @rbiggs.
Yeah. Being evil is fun sometimes :smiling_imp: but of course only for unimportant things. Could of course do a t7 style component lib, but then you have to register, components:
import { h, app } from 'hyperapp';
import hyperx from 'hyperx';
const hax = (() => {
const registry = new Map()
const hx = hyperx((t, a, c) => h(registry.get(t) || t, a, c));
hx.register = component => registry.set(component.name, component);
return hx;
})();
const state = {
count: 0
}
const actions = {
down: value => state => ({ count: state.count - value }),
up: value => state => ({ count: state.count + value })
}
const Component = ({count}) => {
return hax`<h1>${count}</h1>`;
}
hax.register(Component);
const view = (state, actions) => hax`
<div>
<Component count=${state.count}></Component>
<button onclick=${() => actions.down(1)}>-</button>
<button onclick=${() => actions.up(1)}>+</button>
</div>
`;
app(state, actions, view, document.body);
BTW, I'm mostly interested in this so I can use hyperapp for simple 100% client-side projects without a build step. For example I have the above as a codepen with no build step, including hyperapp & hyperx from unpkg.com & wzrd.in respectively.
@dschep Also check https://github.com/hyperapp/html.
I think we could benefit from another hyperx-like template literal. Perhaps I should look into https://github.com/hyperapp/hyperapp/issues/88 before I do this, or maybe not, programming is fun.
1
2
About JSX/Hyperx and some things that I'd like to explore:
Of course, if anyone wants to go ahead and work on this, just do it.
See Also: