nymvno
nymvno

Reputation: 400

Passing b-icon to <td> element in VueJS

I want to pass a piece of HTML to a table-data-element using VueJS. The following demonstrates my scenario:

    <template>
      <div>
        <div v-if="someObject.properties" style="margin-top: 20px;" class="table-responsive-md">
          <table class="table table-striped">
            <thead>
            <tr>
              <th style="text-align: left" scope="col">Some icons</th>
            </tr>
            </thead>
            <tbody v-for="(property, index) in someObject.properties" :key="index">
            <tr>
              <td style="text-align: center" v-html="getIconWhenSomeRequirementIsMet(property)"/>
            </tr>
            </tbody>
          </table>
        </div>
      </div>
    </template>

    <script lang="ts">
      ...
      getIconWhenSomeRequirementIsMet (property: any): string {
        if (property.type === 'animal') return '<b-icon-check class="h3 mb-0" style="color:green;"/>'
        if (property.type === 'human') return '<b-icon-check class="h3 mb-0" style="color:yellow;"/>'
        return '<b-icon-x class="h3 mb-0" style="color:red;"/>'
      }
    </script>

The code above is a minimal example of my Vue single file component. However, this way, I get empty fields in my table instead of the actual icons. Isn't there a simple and clean approach to achieve this?

Upvotes: 0

Views: 2011

Answers (1)

Hiws
Hiws

Reputation: 10324

The reason it doesn't work is because you can't use v-html to render custom components.

Instead, here's two different ways you can do this.

The first is to pre-define your b-icon-* and use v-if, v-else-if and v-else to match which icon to show.

The second is to dynamically bind properties using v-bind, this way you can use a method to do it, like you are now, but instead return the properties based on the type.

new Vue({
  el: "#app",
  data() {
    return {
      items: [
        { type: "animal" },
        { type: "human" },
        { type: "alien" },
      ],
      fields: ['Type', 'Icon 1', 'Icon 2']
    }
  },
  methods: {
    getIconWhenSomeRequirementIsMet (type) {
      /* Default properties */
      const properties = {
        icon: 'x',
        style: 'color: red',
        class: 'h3 mb-0'
      };
      
      if (type === 'animal') {
        properties.icon = 'check';
        properties.style = 'color: green;';
      }
      else if (type === 'human') {
        properties.icon = 'check';
        properties.style = 'color: yellow;';
      }
      
      return properties;
    }
  }
})
<link href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="//unpkg.com/[email protected]/dist/bootstrap-vue.min.css" rel="stylesheet" />

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.js"></script>
<script src="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.min.js"></script>
<script src="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue-icons.min.js"></script>

<div id="app">
  <div class="table-responsive-md">
    <table class="table table-striped">
      <thead>
        <tr>
          <th v-for="field in fields" >{{ field }}</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="{ type } in items">
          <td>
            {{ type }}
          </td>
          <td>
            <b-icon-check v-if="type === 'animal'" variant="success" class="h3 mb-0">
            </b-icon-check>
            <b-icon-check v-else-if="type === 'human'" variant="warning" class="h3 mb-0">
            </b-icon-check>
            <b-icon-x v-else variant="danger" class="h3 mb-0">
            </b-icon-x>
          </td>
          <td>
            <b-icon v-bind="getIconWhenSomeRequirementIsMet(type)"></b-icon>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</div>

Upvotes: 2

Related Questions