ale_aalt
ale_aalt

Reputation: 93

How to hide elements that do not match the current value and compare with prop

I have different groups of words on a single page, let's say nouns verbs and adjectives. The way to describe each group is with its 'part-of-speech'. This 'part-of-speech' is being printed inside a little box. So you have the 'part-of-speech' of the group, noun, in the little box, and I want to achieve that when I click on that box I hide verbs and adjectives. If I were to click on verb I would hide nouns and adjetives, and so on. Right now the 'part-of-speech' of each group is being passed in as a prop.

The problem is that I'd like to compare parts of speech that exist on the current page with the clicked part of speech, but I cannot manage to differentiate it.

In my template I've got:

<div class="part-of-speech">
    <p class="pos">{{ pos }}</p>
</div>

and this { pos } is coming from my props

props: {
    pos: {
        type: String,
        required: false,
        default: "na"
    }
},
mounted() {
   console.log(this.pos)
}

This gives me all the parts-of-speech that are being printed on the page (take into account that this is a child-component of something else and these groups of words are printing as many times as there are groups). So I though that adding a method to detect the clicked part-of-speech would help.

<div class="part-of-speech" @click="handleSelectedPos(pos)">
    <p class="pos">{{ pos }}</p>
</div>

props: {
    pos: {
        type: String,
        required: false,
        default: "na"
    }
},
methods: {
    handleSelectedPos(pos) {
        console.log(pos);
        console.log(this.pos);
    }
}

When I click on the current item, I get the current part-of-speech, and as you can see this.pos in this context is no longer the list of parts-of-speech that are on the page but it has become the currently clicked part-of-speech. My idea was to make the comparison somehow between pos and this.pos, but they are now identical.

How to make the comparison to say: If there are parts-of-speech that are not equal to the one currently clicked, take some action (add a class or wtv) to hide the element.

Upvotes: 2

Views: 437

Answers (1)

muka.gergely
muka.gergely

Reputation: 8329

If I understand well, what you would like to achieve, then some of the events shouldn't be handled by the subcomponents, but by the parent component.

Vue.component('partOfSpeech', {
  props: ['pos', 'text'],
  template: `<div :class="pos" @click='emitEvent'>{{text}}</div>`,
  methods: {
    emitEvent() {
      this.$emit('emitpos', this.pos)
    }
  }
})

new Vue({
  el: '#app',
  data: {
    words: [{
        pos: 'noun',
        text: 'noun1',
        compare: false
      },
      {
        pos: 'verb',
        text: 'verb1',
        compare: false
      },
      {
        pos: 'adjective',
        text: 'adjective1',
        compare: false
      },
      {
        pos: 'noun',
        text: 'noun2',
        compare: false
      },
      {
        pos: 'verb',
        text: 'verb2',
        compare: false
      },
      {
        pos: 'adjective',
        text: 'adjective2',
        compare: false
      },
      {
        pos: 'verb',
        text: 'verb3',
        compare: false
      },
      {
        pos: 'noun',
        text: 'noun3',
        compare: false
      },
      {
        pos: 'adjective',
        text: 'adjective1',
        compare: false
      },
    ]
  },
  methods: {
    filterWords(val) {
      this.words.forEach(e => {
        if (e.pos === val) {
          e.compare = true
        } else {
          e.compare = false
        }
      })
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <part-of-speech :key="i" v-for="(word, i) in words" :pos="word.pos" :text="word.text" v-on:emitpos="filterWords" :style="{ color: !word.compare ? 'black' : 'red'}" />
</div>

In the snippet above you can see that every

  1. data is passed down to the child component as prop (except for compare - that's not needed there)
  2. a click event is set up on each of the child components, that (@click) $emit() an event and their prop back to the parent
  3. the parent has a v-on: for the event emitted, and executes a function on ALL the parts of speech (words in my app; the function colors words red that have the same pos as the clicked one).

MORE INFO

The problem is that sibling elements do not know anything about each other: they’re not supposed to share any information with each other.

A component can share its own unique state information with sibling components either via their common parent (by emitting an event (with a data payload)) or by using some form of state management solution (event bus or Vuex store are the most common in Vue - the latter is for serious cases, the former is for occasions that require more than simple event emitting, but doesn’t require anything really complicated).

Custom events in Vue: https://v2.vuejs.org/v2/guide/components-custom-events.html

Event bus: https://alligator.io/vuejs/global-event-bus/

Vuex state management: https://vuex.vuejs.org/

Upvotes: 1

Related Questions