Ayudh
Ayudh

Reputation: 1763

VueJS v-for with object not displaying keys

The issue is that I'm selecting options, storing them in an object and using v-for to iterate over the keys of the object and display the selected options. Here's my code:

data() {
    return {
      points: 100 as number,
      categories: [{name: "Fitness", items: "6"}, {name: "Electronics", items: "10"},
                   {name: "Fitness", items: "6"}, {name: "Electronics", items: "10"},
                   {name: "Fitness", items: "6"}, {name: "Electronics", items: "10"}] as Array<Record<string, string>>,
      filters: {} as Record<string, number>
    };
  }

and the html

<div id="filterContainer" class="d-flex flex-column">
      <div class="d-flex flex-row justify-content-between">
        <p id="recent">All Categories</p>
        
        
        <div class="ellipsesDropdown">
            <div class="d-flex flex-column">
              <FilterIcon id="filterIcon" />
              <div v-if="filterKeys() > 0" class="filterDot align-self-end"></div>
            </div>
              <div class="dropdown-content">
                  <p v-for="(cat, ind) in categories" :key="ind" @click="filters[cat.name] = 1">{{cat.name}} &#40;{{cat.items}}&#41;</p>
              </div>
          </div>
      </div>

      <div class="d-flex flex-row flex-wrap" style="height: 32px;">
        <div v-for="(val, key, ind) in filters" :key="ind" class="filterSelection d-flex flex-row">
          {{key}}
          <CloseIcon @click="delete filters[key]" class="closeIcon" />
        </div>

        <p v-if="filterKeys() > 0" @click="filters = {}" class="clearAllText">Clear all</p>
      </div>
    </div>

There aren't any errors in the console. I've logged the filters data object on click and values do get added to it. What am I missing?

EDIT: Here's a link to a fiddle that I created: https://jsfiddle.net/ayudh37/2uqm9nar/3/

EDIT 2: Read about the issue I encountered here: https://v2.vuejs.org/v2/guide/reactivity.html

Upvotes: 0

Views: 420

Answers (2)

TehEbil
TehEbil

Reputation: 88

I think the more elegant solution would be if you (if you can) set your data in the data object:

...
data: {
  filters: {
    "apples": null,
    "oranges": null,
    "pears": null
  },
  categories: ["apples", "oranges", "pears"]
},
...

This way, you tell vue to watch all this data. This might not always be possible (e.g. in that example case you given) but often, this is enough.

It's a combination of how javascript works and how VueJS handles it.

E.g. if you have a variable:

let foo = null

deep in machine code, foo points to a memory address, but since it is null, it doesn't show yet. if you set it the first time:

foo = 'bar'

foo shows to that memory address and vue-view, e.g. {{foo}} updates it.

If you now change it:

foo = 'test'

foo will point to 'test' if you console.log() it, but VueJS still shows 'bar'. With $set you force that behaviour, but you'd do so also by setting it within data.

It's a simple explanation and not 100% true and gets more complicated with object and arrays (because these are list of pointers), but it'll help you a lot in future if you read through all these stuff.

Upvotes: 1

Pratik Patel
Pratik Patel

Reputation: 6978

Use $set and it works. It's just reactivity issue.

this.$set(this.filters,cat,1);

fiddle - https://jsfiddle.net/Lejxtbm0/6/

Upvotes: 2

Related Questions