Vue 3 render function no class reactivity

46 Views Asked by At

i need to use hyperscript from vue (h function) but i have a problem with conditionnal class.

Here a reproduction link https://codesandbox.io/p/sandbox/quizzical-albattani-hxk5dd?file=%2Fsrc%2Fcomponents%2Finput.vue%3A14%2C20

in the input.vue view I display the props invalid (next to the input) and in the input itself I declare conditionnal class

:class="{ '--invalid': invalid }"

if i start writing in the input the props displayed change as expected, but the conditionnal class is not updated.

Any idea on how to correct this ?

Here my input component

    <template>
  <div>
    invalid: {{ invalid }}
    <input
      class="theinput"
      :class="{ '--invalid': invalid }"
      @input="updateValue"
      v-model="value"
    />
  </div>
</template>

<script setup lang="ts">
import { ref } from "vue";
const props = defineProps(["invalid"]);
const value = ref("");
const emit = defineEmits(["update:value"]);

const updateValue = (event: Event) => {
  emit("update:value", (event.target as HTMLInputElement).value);
};
</script>

<style>
.theinput:focus {
  outline: none;
}
.--invalid {
  border: 1px solid red;
}
</style>

here how i call i with render function

    import { h, defineEmits } from "vue";

import theinput from "./input.vue";

const render = (details: any, invalid: any) => {
  const emit = defineEmits(["update:value"]);
  let copy = { ...details };
  copy.invalid = invalid;
  return h(theinput, {
    ...copy,
    "update:value": (value: any) => emit("update:value", value),
  });
};

export { render };

and the app.vue

    <template>
  <!-- <theinput :invalid="invalid" @update:value="validate" /> -->
  <render-input @update:value="validate"></render-input>
</template>

<script setup>
import theinput from "./components/input.vue";
import { render } from "./components/input.ts";
import { ref, onBeforeMount } from "vue";

let invalid = ref(true);
const validate = (value) => {
  invalid.value = value.length < 3;
  console.log(invalid.value);
  console.log(renderInput.value);
};

onBeforeMount(() => {
  defineComponent();
});
let renderInput = ref(null);
const defineComponent = () => {
  renderInput.value = render({}, invalid);
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

enter image description here

When the invalid change to false the red border would disapear

EDIT

I fund the solution,in my component declaration

  <div>
invalid: {{ invalid }}
<input
  class="theinput"
  :class="{ '--invalid': invalid }"
  @input="updateValue"
  v-model="value"
/>

the invalid: {{ invalid }} mustn't have .value but the :class="{ '--invalid': invalid }" need it. Both are in template so i don't know if its a normal behaviour. I asked on the offical git repo vue.

1

There are 1 best solutions below

0
Asif Ali On

Using the : syntax for the class binding in the input.vue component:

<template>
  <div>
    invalid: {{ invalid }}
    <input
      class="theinput"
      :class="{ '--invalid': invalid }"
      @input="updateValue"
      v-model="value"
    />
  </div>
</template>

<script setup lang="ts">
import { ref, watch, defineProps, defineEmits } from "vue";

const props = defineProps<{
  invalid: boolean;
}>();

const emit = defineEmits<{
  (e: "update:value", value: string): void;
}>();

const value = ref(props.invalid ? "" : null);

watch(
  () => props.invalid,
  (newValue) => {
    if (newValue) {
      value.value = "";
    }
  }
);

const updateValue = (event: Event) => {
  emit("update:value", (event.target as HTMLInputElement).value);
};
</script>

<style>
.theinput:focus {
  outline: none;
}

.--invalid {
  border: 1px solid red;
}
</style>

In this updated version, we are using the : syntax to bind the class attribute, and we are using the watch function to update the value reactive reference when the invalid prop changes.

Here's the updated render function in the input.ts file:

import { h, defineEmits, defineProps } from "vue";

import Theinput from "./input.vue";

const render = (value: any, invalid: any) => {
  const emit = defineEmits<{
    (e: "update:value", value: string): void;
  }>();

  let copy = { ...value };
  copy.invalid = invalid;
  return h(Theinput, {
    value: value,
    invalid: copy,
    "update:value": (v: string