mana
mana

Reputation: 1239

How to use multiple component to filter data

What I want to happen, is when you click on the radio button on the left, then it filter the content on the right to show only has the same location.

I don't know how to emit an event when someone click on the radio buttons.

I'm using the x-template in this vue code.

const locations = {
    options: [
        {
            title: 'A',
            value: 'a'
        },
        {
            title: 'B',
            value: 'b'
        },
        {
            title: 'C',
            value: 'c'
        }]
};
const team = {
    options: [
        {
            name: 'Team A',
            location: 'a'
        },
        {
            name: 'Team B',
            location: 'b'
        },
        {
            name: 'Team C',
            location: 'c'
        }]
};

Vue.component('location-filter', {
    template: '#location-filter',
    props: {
        location: {
            type: Array,
            required: true
        }
    }

});


new Vue({
    el: '#location-finder',
    data: {
        location: locations.options,
        teams: team.options
    }
});
.page {
    padding:5px;
    border:1px solid #cccccc;
    display:flex;
    flex-flow:row;
}

.sidebar {
    flex-grow:0.6;
    border: 1px solid red;
    padding:10px;
}

.body {
    padding:10px;
    border:1px solid blue;
    flex-grow:1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

    <div class="page" id="location-finder">
    <div class="sidebar">
    <location-filter :location="location"></location-filter>
    </div>
    <div class="body">
    <div v-for="team in teams">
    <div>{{ team.name }}</div>
</div>
</div>
</div>

<script type="text/x-template" id="location-filter">
    <div>
    <div v-for="place in location">
    <label>
<input type="radio" name="place" value="place.value">{{ place.title }}
</label>
</div>
</div>
</script>

Hopefully, it's good enough my explanation and the code to give you an picture of what i'm trying to achieve.

Upvotes: 1

Views: 129

Answers (1)

Dan
Dan

Reputation: 63059

You can emit a custom event from inside of a component, such as when the user clicks on the radio label, like this:

<label @click="$emit('location', place.value)">

I named the event location. Now you can listen for an event named location in the parent's template (locationFinder) like this:

<location-filter :location="location" @location="changeLocation">

Now, locationFinder will try to call a method in its instance called changeLocation every time that location event is emitted. Here's what that method looks like:

changeLocation(location) {
    this.selected = location;
}

You can see it sets a data property called selected to the selected location. So now you'll need your teams loop to check that selected location, and only show a team if its location matches the selected one. The best way to do this is to create a computed property which will always contain a filtered version of the teams list. If there is no selected team, it returns all teams:

filtered() {
    if (this.selected) {
        return this.teams.filter(team => team.location == this.selected);
    } else {
        return this.teams;
    }
}

Finally, update the template to loop over this filtered teams list instead of the unfiltered one:

<div v-for="team in filtered">

I included an updated snippet.

const locations = {
    options: [
        {
            title: 'A',
            value: 'a'
        },
        {
            title: 'B',
            value: 'b'
        },
        {
            title: 'C',
            value: 'c'
        }]
};
const team = {
    options: [
        {
            name: 'Team A',
            location: 'a'
        },
        {
            name: 'Team B',
            location: 'b'
        },
        {
            name: 'Team C',
            location: 'c'
        }]
};

Vue.component('location-filter', {
    template: '#location-filter',
    props: {
        location: {
            type: Array,
            required: true
        }
    }

});


new Vue({
    el: '#location-finder',
    data: {
        location: locations.options,
        teams: team.options,
        selected: null
    },
    computed: {
        filtered() {
            if (this.selected) {
                return this.teams.filter(team => team.location == this.selected);
            } else {
                return this.teams;
            }
        }
    },
    methods: {
        changeLocation(location) {
            this.selected = location;
        }
    }
});
.page {
    padding:5px;
    border:1px solid #cccccc;
    display:flex;
    flex-flow:row;
}

.sidebar {
    flex-grow:0.6;
    border: 1px solid red;
    padding:10px;
}

.body {
    padding:10px;
    border:1px solid blue;
    flex-grow:1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

    <div class="page" id="location-finder">
    <div class="sidebar">
    <location-filter :location="location" @location="changeLocation"></location-filter>
    </div>
    <div class="body">
    <div v-for="team in filtered">
    <div>{{ team.name }}</div>
</div>
</div>
</div>

<script type="text/x-template" id="location-filter">
    <div>
    <div v-for="place in location">
    <label @click="$emit('location', place.value)">
<input type="radio" name="place" value="place.value">{{ place.title }}
</label>
</div>
</div>
</script>

Upvotes: 1

Related Questions