HTML In V-Slot Not Rendering As HTML



  • I’m still a beginner, and I’ve been piecing together code from various places to accomplish my goals. Currently, I’m working on a test file to display customer data narrowed down with autocomplete. My customer data includes phone numbers and email addresses, which I’m trying to format to have links for dialing the number or generating an email.

    For the most part, the page works properly. However, for some reason the link info I’m passing through my phone and email methods is not rendering as HTML but is merely rendering as text that shows the <a href … > code. The code I’m using does have v-slots, which is why I thought the code would render similar to using v-html.

    Here is my code. Could someone tell me how to make the phone numbers and email links here render as HTML?

    <template>
      <div id="q-app">
        <div class="q-pa-md">
          <q-table
            auto-width
            grid
            grid-header
            title="Customers"
            :data="data"
            :columns="columns"
            row-key="id"
            :filter="filter"
            hide-header
          >
            <template v-slot:top-right>
              <q-input filled dense debounce="300" v-model="filter" placeholder="Search">
                <template v-slot:append>
                  <q-icon name="search"></q-icon>
                </template>
              </q-input>
            </template>
    
            <template v-slot:body-cell-landline></template>
            <template v-slot:body-cell-mobile_phone></template>
            <template v-slot:body-cell-email_address></template>
    
          </q-table>
        </div>
        <logout-mixin/>
      </div>
    </template>
    
    <script>
    import axios from 'axios'
    import { mapActions,mapGetters } from 'vuex'
    export default {
      data () {
        return {
          filter: '',
          columns: [
            {
              name: 'first_name',
              required: true,
              label: 'First Name',
              align: 'left',
              field: row => row.first_name,
              sortable: true
            },
            {
              name: 'last_name',
              required: true,
              label: 'Last Name',
              align: 'left',
              field: row => row.last_name,
              sortable: true
            },
            { name: 'address', label: 'Address', field: 'address', align: 'left' },
            { name: 'city', label: 'City', field: 'city', align: 'left' },
            { name: 'landline', label: 'Phone', field: 'landline', align: 'left', format: landline => this.formatPhone(landline) },
            { name: 'mobile_phone', label: 'Cell', field: 'mobile_phone', align: 'left', format: mobile_phone => this.formatPhone(mobile_phone) },
            { name: 'email_address', label: 'Email', field: 'email_address', align: 'left', format: email_address => this.formatEmail(email_address) }
          ],
          data: [ {} ]
        }
      },
      created() {
        this.databaseServer()
      },
      components: {
        'logout-mixin' : require('components/Auth/LogoutMixin.vue').default
      },
      methods: {
        ...mapActions('moduleAuth', ['logoutUser']),
        formatEmail (email) {
          if(email == ""){
            return "none"
          } else {
            return '<a href="mailto:' + email + '">' + email + '</a>'
          }
        },
        formatPhone (phone) {
          var thisPhone
          if(phone == 0 || phone == null){
            return "none"
          } else {
            thisPhone = phone.substring(0,3) + '-' + phone.substring(3,6) + '-' + phone.substring(6,10)
            return '<a href="tel:+1' + phone + '">' + thisPhone + '</a>'
          }
        },
        databaseServer () {
          this.renderComponent++
          axios
          .post("/api/customer/read.php", {
            username: this.getterUser.username,
            password: this.getterUser.password
          })
          .then(response=> {
              var reData = response.data
              reData.forEach(customer => customer.show = false)
              this.data = reData
          })
          .catch(function(error) {
            console.log("Axios Error: ",error)
          })
        }
      },
      computed: {        
        ...mapGetters('moduleAuth', ['getterUser'])
      }
    }
    </script>
    
    <style>
    </style>
    


  • Don’t use format option for that. I would rather render the html into the scoped slot:

    <q-table
       ...
     >
     <template v-slot:body-cell-landline="props">
      <q-td :props="props">
        <a v-if="props.row.landline" href="mailto:{{props.row.landline}}">{{props.row.landline}}</a>
        <template v-else>none</template>
      </q-td>
    </template>
    ...
    </q-table>
    

    Note that I used a template tag for the v-else, but you can use any other tag/component, for ex, a q-badge to make it look nicer.



  • You should put your html in the template slots (as tof06 just pointed out). Formatting is just for the content of the data. So, if you put HTML in there, you’ll only get it as a text output (as you are getting).

    You can also try a computed property instead of the method to create your thisPhone version of the phone number.

    Scott



  • @tof06 @s-molinari
    I tried that. Unfortunately, that gets me:

    Errors compiling template:

    href=“mailto:{{props.row.landline}}”: Interpolation inside attributes has been removed. Use v-bind or the colon shorthand instead. For example, instead of <div id="{{ val }}">, use <div :id=“val”>.



  • Like I said, probably need a computed property to build the href. You can do computed properties and add HTML. 😄

    If you put together a codesandbox or codepen, I’ll show you what I mean.

    Scott



  • @s-molinari
    I’d never set up a codepen before, so it took me a few tries. Here it is:
    https://codepen.io/vahost/pen/GRJLRaW?editors=1111



  • Oh. I completely overlooked you are using grid-mode. Those slots won’t work in grid mode and this will now only work in grid mode. Hope it helps.

    https://codepen.io/smolinari/pen/bGdJBqX?editors=1010

    Scott



  • @s-molinari Tell me more about that. Why won’t slots work in grid mode?

    Thanks for the pen, by the way! I’ll study it next.



  • Because in grid mode, those slots simply aren’t used. They are for the normal table mode only. So, if you want the formatting to happen in normal mode too, you’d need all of the slots set up for the output.

    Scott



  • @s-molinari Are you saying that there are other slots that ARE used in grid mode, or are you saying that no slots are ever used in grid mode? I’m still trying to understand what you’re saying.



  • 😁

    In normal (non-grid) mode, all the cell, body, etc. slots are put to use, if you use them. If you switch to grid mode, they are ignored. The only slot used in grid mode is the item slot.

    I hope that makes it clearer.

    Scott



  • @s-molinari Ah! Yes, that helps a lot. Thanks again!



  • @s-molinari I have a problem with your pen that I can’t resolve.

    For the first and last names, the only way the search will find them is if I start with the first letter of the first name.

    So typing the first letters of the last name fails to find the correct rows.

    But with the other fields, I can enter any few letters, even in the middle of a word, and it will keep the rows that contain that substring regardless of whether the substring starts at the beginning of the value.

    How can I change this?



  • Updated.

    https://codepen.io/smolinari/pen/bGdJBqX?editors=1010

    You should show what you attempted in your own pen (could be a fork of mine). Maybe I could have just answered with a reply to lead you in the right direction. 😄

    Scott



  • @s-molinari Will do going forward. Thanks!


Log in to reply