Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Template syntax alternative #89

Open
TheJaredWilcurt opened this issue Aug 23, 2022 · 3 comments
Open

Template syntax alternative #89

TheJaredWilcurt opened this issue Aug 23, 2022 · 3 comments
Labels
enhancement New feature or request

Comments

@TheJaredWilcurt
Copy link

TheJaredWilcurt commented Aug 23, 2022

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.

  • a-model
  • a-if
  • a-for
  • a-html
  • a-text
  • and a-show isn't needed but is cleaner than doing by hand

Examples:

  • <input a-model="value"> - See the two-way data binding section above
  • <div a-if="someBool"></div> - If someBool is false, this element is removed from the DOM. Can be performant by removing a large set of DOM nodes from memory when not needed.
  • <div a-show="someBool"></div> - If someBool is false, this attribute is given style="display: none". Can be more performant if toggling visibility on a lot of DOM nodes often
  • <div a-for="(item, index) in items"></div> - Creates an element (div in this case) for every item in the items array. item and index can be renamed to anything.
  • <div a-for="(value, key) in item"></div> - Creates an element for every key in the item object. value and key can be renamed to anything.
  • <div a-html="'<strong>Text</strong>'"></div> - Applies innerHTML, can be dangerous if it contains any data supplied by a user or network request, but also useful when the value is generated internally
  • <a a-text="'text'"></a> - Applies innerText, can be useful when you don't want additional whitespace that could occur when using returns and indentation in your code.

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

@TheJaredWilcurt TheJaredWilcurt added the enhancement New feature or request label Aug 23, 2022
@UpperCod
Copy link
Member

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
Copy link
Author

TheJaredWilcurt commented Aug 30, 2022

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
Copy link
Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants