Hi guys, I'd like to present you a syntax I've designed to make rendering complex UIs more readable.
I'd like to ask you for three things:
- Do you think the goals I've set myself are valid (see below)?
- Did I manage to make it readable and follow those goals?
- What's your loose thoughts about the idea?
Repo: https://github.com/gluecodes/gluecodes-glue-dom
Hope you enjoy the read. ❤️
I already used the syntax to generate Virtual DOM representation of HTML in my own dev tool called GlueCodes. Here is more abut it: https://www.glue.codes and here your can see the prototype: https://ide.glue.codes
Goals
- Gradual learning curve, preferably no need to learn another templating syntax (directives etc.).
- Reads sequentially as HTML while remaining readable and maintainable.
- Isn't a mix of HTML and JavaScript drawing a clear border line between view and logic.
- Allows to format texts without writing nested inline tags.
- Makes writing dynamic texts easier with no need for checking whether variables are non-empty.
Challenge
Write a function which renders condition-based HTML. There is someCondition
prop which needs to be truthy to display a section which contains other nested conditionals. firstProgrammer
and secondProgrammer
are both optional.
Syntax Comparison
JSX
As we want to read it from top to bottom and avoid using variables, we need to heavily rely on ternary operators and logical expressions.
({
firstProgrammer,
secondProgrammer,
someCondition
}) => (
<div>
{someCondition
&& (firstProgrammer && secondProgrammer
? <p><bold>{firstProgrammer}</bold>, you're going to do pair-programming with {secondProgrammer}.</p>
: (firstProgrammer && !secondProgrammer
? <p><bold>{firstProgrammer}</bold>, you'll code this task by yourself.</p>
: <p>Hey man! Can you tell us your name before we give you job to do?</p>))
}
</div>
)
HyperScript
Similarly to JSX, to avoid using variables and read it from top to bottom, we need to use a mix of ternary operators and rest operators.
({
firstProgrammer,
secondProgrammer,
someCondition
}) => h('div', {}, [
...(someCondition ? [h('p', {}, [
...(firstProgrammer && secondProgrammer ? [
h('strong', {}, [
firstProgrammer
]),
`, you're going to do pair-programming with ${secondProgrammer}.`,
] : []),
...(firstProgrammer && !secondProgrammer ? [
h('strong', {}, [
firstProgrammer
]),
`, you'll code this task by yourself.`,
] : []),
...(!firstProgrammer && !secondProgrammer ? [
'Hey man! Can you tell us your name before we give you job to do?',
] : [])
])] : [])
])
My Way
Here you can use if
statements and calls to nested callbacks to either render a tag or text. When calling text
, all its arguments are checked whether they are truthy and only if they are, they will be concatenated and rendered. There is also a concept of formatters which are configured when initializing the top-most tag
, and they can either wrap texts inside a chosen tag and apply CSS classes on it. In this case emphasized
is configured to wrap props inside <strong/>
tag. Nesting is possible by simply nesting objects e.g. { bold: { italic: 'some text' } }
.
({
firstProgrammer,
secondProgrammer,
someCondition
}) => (
tag('div', (props, { tag }) => {
if (someCondition) {
tag('p', (props, { text }) => {
text({ bold: firstProgrammer }, ', you\'re going to do pair-programming with ', secondProgrammer, '.')
if (!secondProgrammer) {
text({ bold: { italic: firstProgrammer } }, ', you\'ll code this task by yourself.')
}
if (!firstProgrammer && !secondProgrammer) {
text('Hey man! Can you tell us your name before we give you job to do?')
}
})
}
})
)
Syntax
Nesting
tag(tagName, (props, { component, tag, text }) => {
tag(tagName, (props, { component, tag, text }) => {
tag(tagName, (props, { component, tag, text }) => {
...
})
})
})
No child elements
tag(tagName, {
[props]
})
No child elements nor props/attributes
tag(tagName)
Components (other rendering function)
component(reusableUiPiece, props)
Text
text(...[textChunk,])
-
tagName
A string that specifies the type of element to be created -
props
An object to assign element props/attributes -
component
A function to render component -
tag
A function to create an element -
text
A function to create text -
reusableUiPiece
A function returning reusable virtual DOM -
textChunk
Either a string or an object which uses text formatters. If any chunk is empty, the whole sentence won't be rendered
EDIT: to pass props, you'd be assigning to props
argument e.g.
tag('span', (props, { text }) => {
props.className = 'someClass'
text('some text')
})
Top comments (3)
I can see why, with these (arbitrary?) requirements, JSX might look problematic. But I don't know why you're trying to force the use of ternary operators. Personally, I find the following formatting to be the most easy to read. And it doesn't require learning anything new.
yeah, I'm not going to argue here. In this case declaring local var makes more sense than ternary operators.
Here is a little playground: codepen.io/Czopp/pen/qBapvKB. In upcoming days I'll release github repo and utility tool to convert HTML to the presented syntaxEDIT: here is the repo: github.com/gluecodes/gluecodes-glu...
and HTML to GlueDOM conversion: codepen.io/Czopp/pen/zYKRrXo