Pedro Sanção
Pedro Sanção

Reputation: 1368

Calling Vue filter dynamically crashes component

I am working on a Vue single file component that shows a table based on JSON object. I want to run a filter dynamically to allow each column to have it's own filters.

Inside the <td> v-for loop I want to check row.filter variable and either call the filter accordingly or not when it is undefined. My current code, however, will crash the component.

<template>
    <table>
        <thead>
            <tr>
                <th v-for="(row, rowNumber) in tableRows" v-bind:key="rowNumber">
                    {{ row.header }}
                </th>
            </tr>
        </thead>
        <tbody>
            <tr v-for="model in list" v-bind:key="model.id">
                <td v-for="(row, rowNumber) in tableRows" v-bind:key="rowNumber">
                    <!--
                    the following will crash the component
                    -->
                    {{ model[row.property] | row.filter }}
                </td>
            </tr>
        </tbody>
    </table>
</template>

<script>
export default {
    data: () => ({
        tableRows: [{
            header  : 'ID',
            property: 'id'
        },{
            header  : 'User',
            property: 'name'
        },{
            header  : 'E-mail',
            property: 'email'
        },{
            header  : 'Created at',
            property: 'created_at',
            filter  : 'formatDate' // the only column with filter
        }],
        list: [{
            id         : 1,
            name       : "Admin User",
            email      : "[email protected]",
            description: "Sample admin",
            created_at : "2020-07-05 13:12:35"
        }]
    }),
    filters: {
        formatDate (date) {
            date = new Date(date);
            return date.toLocaleString()
        }
    }
}
</script>

Upvotes: 1

Views: 254

Answers (1)

Owl
Owl

Reputation: 6853

You can use v-if v-else

<template v-if="row.filter">{{ model[row.property] | formatDate() }}</template>
<template v-else>{{ model[row.property] }}</template>

Since you want to avoid using v-if and filter is going to be deprecated in Vue 3 anyway, here's what you can do instead:

  1. Create a method to format your data:
methods: {

    // format handler
    format (value, filter) {
        if (filter === "formatDate") return this.formatDate(value);
        if (filter === "otherFormat") return this.otherFormat(value);
        return value;
        
        // or call function name dynamically
        if (filter) return this[filter](value);
        return value;
    },
    
    // all filters will be moved to methods
    formatDate (date) {
        return new Date(date).toLocaleString();
    }

    otherFormat (value) {
        return someOtherFormat(value);
    }

}
  1. Call the method from your template
<tr v-for="model in list" v-bind:key="model.id">
    <td v-for="(row, rowNumber) in tableRows" v-bind:key="rowNumber">
        {{ format(model[row.property], row.filter) }}
    </td>
</tr>

Upvotes: 1

Related Questions