Home > Software design >  v-bind reactivity inside a template literal?
v-bind reactivity inside a template literal?

Time:01-12

I'm generating a template literal html that feeds into the v-tooltip. I'm trying to bind the src of the images inside each li to react to changes in other computed properties but it doesn't seem to work. Is it even possible to v-bind an attribute inside a template literal?

I have the following template:

<template>
  <div v-tooltip="passwordAssistant">
  </div>
</template>

and the following properties-

  props: {
    modelValue: {
      type: String,
      default: '',
    },
  computed: {
    passwordAssistant() {
      return `<ul style="list-style: none;">
      <li> <img :src=contains_eight_characters>8 characters</li>
      <li> <img :src="contains_number">1 number</li>
      <li> <img :src="contains_uppercase">1 upper case</li>
      <li> <img :src="contains_lowercase">1 lower case</li>
      <li> <img :src="contains_special_character">1 special character</li>
      </ul>`
    },
    iconDeterminator(value) {
      return value ? require('@/assets/images/v-icon.svg') : require('@/assets/images/x-icon.svg');
    },
    contains_eight_characters() {
      console.log(this.modalValue.length);
      return this.iconDeterminator(this.modelValue.length >= 8);
    },

CodePudding user response:

--- PART 1: v-tooltip ----

According to the vuetify documention on v-toolip

This is how to correctly use v-tooltip;

<template>
  <div>
    <v-tooltip top>
      <template v-slot:activator="{ on, attrs }">
        <v-btn
          color="secondary"
          dark
          v-bind="attrs"
          v-on="on"
        >
          Test Button
        </v-btn>
      </template>
      <ul style="list-style: none;">
        <li> <img :src="contains_eight_characters">8 characters</li>
        <li> <img :src="contains_number">1 number</li>
        <li> <img :src="contains_uppercase">1 upper case</li>
        <li> <img :src="contains_lowercase">1 lower case</li>
        <li> <img :src="contains_special_character">1 special character</li>
      </ul>
    </v-tooltip>
  </div>
</template>

Working demo: demo

The activator slot should contain the item which the tooltip should appear when hovering over. You need to bind the attrs and the on provided by the slot scope to whatever is inside the activator.

Make sure to replace "top" with any direction (top | bottom | left | right) that you want.

---- PART 2: Binding the image sources ----

You can use v-bind in the template. Look at the official Vue documentation to learn more.

Assuming this is some sort of password requirements tooltip (where you decide which image to show based on whether the condition is true or false);

  1. Make two variables - one for each image type
  2. Bind each image source to a computed property for the condition returning one variable or the other
  3. Profit

Variables (sorry I will be using Vue 3 syntax here I don't know if you're using Vue 2 or 3);

<script setup>
import { ref, computed } from "vue";

...

// You also need a variable containing your password field binded value
const passwordValue = ref("");

const metConditionImage = "@/assets/images/v-icon.svg";
const unmetConditionImage = "@/assets/images/x-icon.svg";

const containsNumber = computed(() => {
    ... implement here
    return INSERTYOURCONDITIONHERE ? metConditionImage : unmetConditionImage;
});

const containsEightCharacters = computed(() => {
    ... implement here
    return INSERTYOURCONDITIONHERE ? metConditionImage : unmetConditionImage;
});

const containsUppercase = computed(() => {
    ... implement here
    return INSERTYOURCONDITIONHERE ? metConditionImage : unmetConditionImage;
});

const containsLowercase = computed(() => {
    ... implement here
    return INSERTYOURCONDITIONHERE ? metConditionImage : unmetConditionImage;
});

const containsSpecialCharacter = computed(() => {
    ... implement here
    return INSERTYOURCONDITIONHERE ? metConditionImage : unmetConditionImage;
});
</script>

Binding the imageSources to the computed properties;

<li> <img :src="containsEightCharacters">8 characters</li>
<li> <img :src="containsNumber">1 number</li>
<li> <img :src="containsUppercase">1 upper case</li>
<li> <img :src="containsLowercase">1 lower case</li>
<li> <img :src="containsSpecialCharacter">1 special character</li>

CodePudding user response:

As mentioned in the answer above, v-bind cannot be used in the context of a template literal. Markup generated by Vue should be generated via the template tag.

If there is a specific reason why the HTML needs to be generated as a string, it can be done with Computed Variables. It is important to know that a Computed Variable recalculates itself when the variables used to calculate the output change. We can take advantage of this behavior by rewriting our function as follows:

    computed: {
        passwordAssistant() {
            return `
                <ul style="list-style: none;">
                    <li>
                        <img src="${this.myVueVariable}">
                    </li>
                </ul>
            `;
        },
    },
<template>
    <div v-html="passwordAssistant" />
</template>
  • Related