question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Events interoperability proposal

See original GitHub issue

After some discussion/back and forth in Discord, I thought it might be a better idea to express y concerns/ideas here in a more descriptive way; this is a continuation of the following message from your Discord server.

For brevity and for the sake of specificity, this proposal and its examples will be based around Vue (and v3 to be more specific).

Problem

As we know Mitosis allows us to write component in the “lower common denominator” for frameworks such as React, Vue, Svelte, etc. This is such a power that we didn’t have before. Nonetheless, I think that there’s some room for improvement regarding how Vue components are generated in general (and maybe this related to other frameworks?).

It’s not uncommon for a Design System to have components such as modals or more involved input elements with different elements inside them, (div + label + possible error message) so that it’s easier for developer to use those buildings blocks as a whole. Nonetheless, right now a component like the following written in Mitosis:

import { Show } from "@builder.io/mitosis";

interface Props {
  name: string;
  label: string;
  onChange: (event) => void;
  errorMessage?: string;
}

export default function SuperInputField(props: Props) {
  return (
    <div class="my-super-duper-input-field">
      <label for={props.name}>{props.label}</label>
      <input
        id={props.name}
        name={props.name}
        onChange={(event) => props.onChange(event)}
      />
      <Show when={props.errorMessage}>
        <p class="form-message danger">{props.errorMessage}</p>
      </Show>
    </div>
  );
}

Gets converted to this equivalent Vue 3 (composition API):

<template>
  <div class="my-super-duper-input-field">
    <label :for="name">{{ label }}</label>
    <input :id="name" :name="name" @input="onChange($event)" />

    <template v-if="errorMessage">
      <p class="form-message danger">{{ errorMessage }}</p>
    </template>
  </div>
</template>

<script setup>
const props = defineProps(["name", "label", "onChange", "errorMessage"]);
</script>

Which looks fine at first glance but presents a few problems:

  • In frameworks similar to React (Solid, etc) it’s common to pass functions to be executed by the child components “whenever”. But this is not the case with Vue. Vue standard way of doing this is through events between parent and child. Meaning that the correct code for this specific output should be:
<template>
  <div class="my-super-duper-input-field">
    <label :for="name">{{ label }}</label>
    <input :id="name" :name="name" @input="emit('change', $event)" />

    <template v-if="errorMessage">
      <p class="form-message danger">{{ errorMessage }}</p>
    </template>
  </div>
</template>

<script setup>
const props = defineProps(["name", "label", "errorMessage"]);
const emit = defineEmits(['change']);
</script>

NOTE the new const emit = defineEmits(['change']); and the @input event in the template (which shouldn’t it be @change; since that was the one we listened to in the original Mitosis component?

  • Furthermore, with Vue we have things like v-model which allows for two way data binding which is really common on form elements and what not. The problem with the current approach is that this would work on the majority of frameworks that follow the “React” way, but not for those that communicate between parent-child components via events such as Vue. For Vue and more specific Vue 3, to make a “custom component” work with the v-model directive, that component must accept modelValue as a prop and emit update:modelValue this allows for this usage: <SuperInputField v-model="value" /> which gets expanded by Vue to: <SuperInputField :model-value="value" @update:modelValue="newValue => value = newValue" />
    • Similar to this, Vue allows to have multiple v-model directives right now with v-model:title="title" as long as the component accepts a prop named title and emits an event named update:title with the new value for it.

The problems mentioned above might be “too specific” to Vue or similar frameworks like Svelte which also allows for custom event dispatching. But I really think that nailing this or a subset of this in Mitosis can go a long way for the adoption of the tool to write performant and seamless design systems that just work with it. And specially more so with Vue as it’s one of the “main” frameworks out there.

Proposed solutions/ideas

I would propose to tackle this issue in a two way approach maybe?

Let me elaborate a little bit into what I mean by this.

  1. My first try would be to map the events in props like onClick, onInput, onSomething and automatically convert them to listeners like @click, @input, @something as they should. This would get us the majority of the way there in terms of pairity with Vue.
  2. My second try would be taking a stab at the v-model implementation, but I would leave this out of the “main compiler” and expose it as a plugin (maybe?) that people can use with useMetadata passing some configuration that then the plugin would map correctly to the different outputs, specially Vue and its variants.

Example of point #2

Given this component:

import { Show, useMetadata } from "@builder.io/mitosis";

interface Props {
  name: string;
  label: string;
  value: any;
  onUpdateValue: (event) => void;
  errorMessage?: string;
}

useMetadata({
  vModel: {
    modelValue: 'value',
    events: {
      'update:modelValue': 'onUpdateValue',
    },
  },
})

export default function SuperInputField(props: Props) {
  return (
    <div class="my-super-duper-input-field">
      <label for={props.name}>{props.label}</label>
      <input
        id={props.name}
        name={props.name}
        value={props.value}
        onChange={(event) => props.onUpdateValue(event.target.value)}
      />
      <Show when={props.errorMessage}>
        <p class="form-message danger">{props.errorMessage}</p>
      </Show>
    </div>
  );
}

And a config (mitosis.config.js) file like:

const { vModelPlugin } = require('@builder.io/mitosis/vue'); // <- or whatever

module.exports = {
  // ...rest configuration
  plugins: [
    // ... rest of plugins
    vModelPlugin
  ];
};

Mitosis would generate the following output for Vue:

<template>
  <div class="my-super-duper-input-field">
    <label :for="name">{{ label }}</label>
    <input :id="name" :name="name" :value="value" @input="emit('update:modelValue', $event.target.value)" />

    <template v-if="errorMessage">
      <p class="form-message danger">{{ errorMessage }}</p>
    </template>
  </div>
</template>

<script setup>
const props = defineProps(["name", "label", "value", "errorMessage"]);
const emit = defineEmits(['update:modelValue']);
</script>

With point #1 we would allow better parity with Vue out of the box. And with point #2 we would make the “feature/implementation” an opt-in for the developers based on their targets and actual requirements.

Closing

As mentioned before, having the #1 point working out of the box with Mitosis I think should be a “must” as that falls (IMHO) under the “lowest common denominator” category; and the vModelPlugin would be a layer on top of that.

I would be more than happy to try and discuss this further and even try and help with moving this forward in terms of implementation.

Thank you! 🙏 💪 🚀

Issue Analytics

  • State:open
  • Created a year ago
  • Reactions:5
  • Comments:7 (5 by maintainers)

github_iconTop GitHub Comments

3reactions
kingzezcommented, Nov 15, 2022

I’m interested in this proposal, it’s really necessary in vue, looking forward to this proposal will implement 💪

0reactions
bjufrecommented, Dec 16, 2022

@samijaber I know you’re super busy. But is there anything else I can do to continue the conversation?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Interoperability Showcase | HIMSS Global Health Conference
The HIMSS Interoperability Showcase™ demonstrates the power of standards-based interoperability by showcasing how multiple stakeholders connect health IT to ...
Read more >
Policies and Technology for Interoperability and Burden ...
This proposed rule emphasizes the need to improve health information exchange to achieve appropriate and necessary access to complete health ...
Read more >
Federal Register :: Medicare and Medicaid Programs; Patient ...
We are proposing a new Electronic Prior Authorization measure for eligible hospitals and CAHs under the Medicare Promoting Interoperability ...
Read more >
HHS' proposed interoperability rules: A timeline of key events
Feb. 25, 2019: HHS says it will formally publish CMS and ONC's interoperability rule proposals on March 4 as well as open a...
Read more >
ONC unveils 10-year plan for healthcare interoperability
The Office of the National Coordinator for Health IT outlined a 10-year plan to develop an interoperable health IT ecosystem that can simultaneously...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found