atomicojs / atomico

Atomico a micro-library for creating webcomponents using only functions, hooks and virtual-dom.
https://atomicojs.dev
MIT License
1.19k stars 43 forks source link

Template syntax alternative #89

Open TheJaredWilcurt opened 2 years ago

TheJaredWilcurt commented 2 years ago

Is your feature request related to a problem? Please describe.

JSX/Lit/Uhtml/etc all look and act basically the same. For those that care about the very very subtle differences between them, it is nice to have those options, but another template syntax should be supported that differs more greatly to appeal to those coming from frameworks other than React.

Describe the solution you'd like

Dynamic attributes:

Dynamic attributes, or props, should be discerned by prefixing the attribute with a colon.

<a :href="someValue">Text</a>
<img
  :alt="'Avatar for ' + user.name"
  :src="'/avatars/' + user.id + '.jpg'"
/>

Boolean Attributes

Some HTML attributes are considered boolean attributes. Meaning they don't require a value to be valid, and their existence represents value. For example disabled, required, checked, etc.

In these cases, the syntax should accept a variable, and if truthy, it will append the attribute in the DOM.

<input :required="isRequired">
<!-- will produce one of the following -->
<input required>
<input>

Special attribute syntax for "class"

The most commonly used attribute in all of HTML, by a landslide, is class. Because it is so common, and has some common dynamic uses, we should support a few special syntax's to make life more convenient.

<!-- Works the same as all other dynamic attributes -->
<div :class="'card ' + favoriteColor"></div>
<div :class="success ? 'green' : 'red'"></div>

<!-- If an object is passed in, the key is considered a class, and only applied if the value is truthy -->
<div :class="{ 'is-active': someBoolean }"></div>

<!-- If an array is passed in, each item is evaluated to a class name, falsy items are filtered out -->
<div :class="[ 'card', favoriteColor, 'pt-lg', { 'is-active': isOnline } ]"></div>

Event Bindings:

Dynamic event bindings should be discerned by replacing the "on" prefix with an at-sign.

<button @click="someFunction">Click me</button>

Event Modifiers:

Several event modifiers should be supported to improve code readability. At the very least prevent and stop. For example

BAD:

<button @click="handleClick">Text</button>
function handleClick (evt) {
  evt.preventDefault();
  loadUserAvatar();
}

Can become the far more readable: GOOD:

<button @click.prevent="loadUserAvatar">Text</button>

In Vue, "Handle X" is considered an anti-pattern, because the framework can handle most events for you, and it obscures the actual functionality being performed.

Inline expressions

Double curlies should be used to indicate a text node be created with the evaluated result of an expression.

<div>{{ someValue.toUpperCase() }}</div>

Two-Way data binding (text)

Two-way data binding is a common feature of framworks, like Polymer, Vue, and many others. This could be handled via an a-model attribute.

<input a-model="someValue">

This will set the value attribute of the input any time the someValue variable is modified. It would also add an event listener to the element for input events, retrieve the event.currentTarget.value and use that to update the value of the someValue variable.

This approach would be the same as doing:

<input :value="someValue" @input="(evt) => { someValue = evt.currentTarget.value }">

This matches the intuitive approach used by Vue 2. In Vue 3 it was changed to a less intuitive approach, in which it sets modelValue instead of value and expects update:modelValue instead of input. This change was made to allow applying multiple v-model's on an element. Something not possible in Vue 2, but also, not really needed, as you could emit an object if needed.

This would work on <input> elements with no type attribute (since it defaults to text) and most other input types (like type="email" or type="password", etc).

Two-way data binding (Boolean)

<input v-model="someValue" type="checkbox"> would be equivalent to

<input
  type="checkbox"
  :checked="someValue"
  @input="(evt) => { someValue = evt.currentTarget.checked }"
>

The 5 basic directives

You should be able to add a simple directive to your template to control common flow logic.

Examples:

I probably should have started this ~wishlist~ issue with "Dear Santa,".

UpperCod commented 2 years ago

Thank you for your feature, your goals are well documented, I will try to match the scope so that in the future we can create a prototype.

Some time ago I experimented creating a render based on directives https://github.com/UpperCod/dollars.js

The development of a template engine that considers a new syntax is complex, do you happen to know of a compiler that interprets the majority of the objective? I have been seeing this library to apply in the prototype https://www.npmjs.com/package/sax-wasm

TheJaredWilcurt commented 2 years ago

Most of what I've outlined is based on Vue's template syntax. There is the official Vue-Template-Compiler, which does have an option to return an AST. But there are many other libraries around Vue-like syntax. Though I haven't evaluated them to know the pros/cons of these options. It may be easiest to take the officially maintained compiler and add a transform at the end of it's AST to map to what this project's needs are. Then you only need to maintain a high level translation layer, instead of the syntax itself.

This means that you'd be stuck with v-if instead of a-if. Similarly, for things like 2-way data binding being more complex in Vue 3, you wouldn't be able to use the simpler Vue-2 style. But alternatively it means that you could just link to Vue's official docs for documentation on the syntax, so that is beneficial in a sense. It's literally the same syntax, not a similar, but custom alternative. Though technically in Vue, you can create and globally install your own custom directives, (a-if, a-model, etc). So if you do want to alias the Vue directives, or create your own, that may be possible.

TheJaredWilcurt commented 9 months ago

found another related project, https://github.com/stalniy/react-template-compiler