shubhra
shubhra

Reputation: 782

VueJS - Filter data by the first letter

I'm trying to filter data by name using both an input field and an alphabetical list. The text input field filtration is working fine, however, filtering by the first letter of the string using .startsWith() is refusing to work. I'm still new to this and can't figure it out.

What am I doing wrong? Here's the fiddle.

<div id="app">
    <h1>Authors</h1>
    <div class="letters-list">
        <div class="letters-wrap" v-for="letter in letters" :key="letter">
            <div v-if="isAuthorLink(letter)" class="has-data">
                <input type="radio" :value="letter" v-model="lettersFilter">
                <label :for="letter">{{ letter }}</label>
            </div>
            <div v-else>{{ letter }}</div>
        </div>
    </div>
    <div class="search-wrapper">
        <input type="text" v-model="searchByName" placeholder="Search.." />
    </div>
    <div class="list-inner">
        <div v-for="author in authorsFilter" :key="author.id">
            <h5><a :href="author.link" v-html="author.name"></a></h5>
        </div>
    </div>
</div>

<script>
new Vue({
    el: '#app',

    data: function () {
        return {
        authors: [
    {
        "id": 77,
        "link": "http://my-site/authors/anonymous/",
        "name": "Anonymous",
        "slug": "anonymous"
    },
    {
        "id": 72,
        "link": "http://my-site/authors/ferdinand-marcos/",
        "name": "Ferdinand Marcos",
        "slug": "ferdinand-marcos"
    },
    {
        "id": 75,
        "link": "http://my-site/authors/john-f-kennedy/",
        "name": "John F. Kennedy",
        "slug": "john-f-kennedy"
    },
    {
        "id": 67,
        "link": "http://my-site/authors/john-maxwell/",
        "name": "John Maxwell",
        "slug": "john-maxwell"
    }
],
        searchByName: '',
        lettersFilter: ''
      }
    },
    computed: {
        letters() {
            let letters = []
            for(let i = "A".charCodeAt(0); i <= "Z".charCodeAt(0); i++) {letters.push(String.fromCharCode([i]))}
            return letters
        },
        authorsFilter() {
            if(this.searchByName){
                return this.authors.filter((item)=>{
                    return item.name.toLowerCase().includes(this.searchByName.toLowerCase())
                })
            } if(this.lettersFilter){
                return this.authors.filter((item)=>{
                    return item.name.toLowerCase().startsWith(this.lettersFilter.toLowerCase());
                })
            } else {
                return this.authors;
            }
        },
    },
    methods: {
        isAuthorLink(letter) {
            return this.authors.some(aut => aut.name.startsWith(letter))
        },
    }
})
</script>

Upvotes: 0

Views: 1657

Answers (2)

skirtle
skirtle

Reputation: 29132

Yom S. is correct, the main problem is that your <label> is not associated with your <input>. The <input> itself is hidden by your CSS so only the <label> is clickable. The filtering is never triggered.

An alternative to using a for/id is to put the <input> inside the <label>.

<label>{{ letter }}<input type="radio" :value="letter" v-model="lettersFilter"></label>

This creates an implicit link between the <input> and <label> such that clicking on the <label> counts as a click on the <input>.

On an unrelated note, I would advise not using v-html if you can help it.

Upvotes: 1

Yom T.
Yom T.

Reputation: 9200

You need the id attribute on the input elements for the labels to associate with; without one, the @input event won't get emitted, and the value not updated.

<div v-if="isAuthorLink(letter)" class="has-data">
  <input :id="letter" type="radio" :value="letter" v-model="lettersFilter">
  <label :for="letter">{{ letter }}</label>
</div>

Quoting the docs on <label>'s for attribute:

The id of a labelable form-related element in the same document as the <label> element. The first element in the document with an id matching the value of the for attribute is the labeled control for this label element, if it is a labelable element. If it is not labelable then the for attribute has no effect. If there are other elements which also match the id value, later in the document, they are not considered.

Upvotes: 1

Related Questions