0

I built a vue component that get's a number value via prop from the outside laravel blade, like this:

<my-custom-template :mynumber="{{$numbervalue}}" :list:{{$alist}}></my-custom-template>

inside the template I have a v-for list and the prop:

props:{
     list:Array,
     mynumber: Number,
    [..]
}

and

<template>
    <ul>
        <li v-for="item in list">{{item}}<span v-if="item.id == mynumber">active</span></li>
    </ul>
</template>

Whenever the ID of the item is the same as the value mynumber, I want the "active" tag/span to be displayed.

Now in this template I also have a method that sends an axios request and on success it alters the value of the prop "mynumber", so the list should rerender:

axios.post('/api/someurl', this.obj)
            .then(res => {
                this.mynumber= res.data[something]; // returns a new number from the db.
                        })
                .catch(error => { [..]
                };

Issue: If I use this.mynumber in the list's v-if condition, the "active" tag is never being shown. If I use directly == mynumber then it works, but I cannot alter it with the axios response. How should I approach this correctly? How can I alter the initial prop, with the new value from the axios call?

Dominic
  • 440
  • 8
  • 22

2 Answers2

1

Don't mutate props directly. Use this.$emit (Docs: https://v2.vuejs.org/v2/guide/components-custom-events.html) instead to change the myNumber in the parent. myNumber will then automatically update in the child component.

tony19
  • 125,647
  • 18
  • 229
  • 307
discolor
  • 1,327
  • 7
  • 15
  • I see, the method emits "update" and the $updatedvalue to the parent and the parent changes the props value @update. Would that look like this in the laravel blade then: ? – Dominic May 12 '20 at 09:00
  • To be perfectly honestly I have never worked with laravel before but that's the general idea, yes. You don't want to duplicate the state in the child. It would be `@update="$numbervalue = $event"` though. The props passed to the top are always $event (In straight up Vue, no idea how laravel blade changes the $ syntax, sorry!). Once your parent changed the variable, the child will reflect those changes – discolor May 12 '20 at 10:00
  • 1
    no worries, it was helpful nonetheless :) I think I'm on the right track. – Dominic May 12 '20 at 10:12
1

First, you shouldn't be modifying props directly, as mentioned in this prior Stack Overflow post.

[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "propRoomSelected"

Second, as seen in the Vue documentation for conditionals, you do not use this within templates, the this is inferred.

Now, to get to the meat of your question, how to look at either a prop or a new value when rendering. Here's how I'd do it.

<template>
  <ul>
      <li v-for="item in list">{{item}}<span v-if="isCurrent(item.id)">active</span></li>
  </ul>
</template>

<script>
export default {
  props: ['list', 'myNumber'],
  data() {
    return {
      myNewNumber: undefined
    }
  },

  methods: {
    isCurrent(itemId) {
      return itemId == (myNewNumber || myNumber)
    }
  }
}

</script>

Edit:

Note that there is a difference between

return itemId == (myNewNumber || myNumber)

and

return (itemId == myNewNumber) || (itemId == myNumber)

The first one "short circuits" a comparison against myNumber once myNewNumber becomes anything "truthy". Read more here.

tony19
  • 125,647
  • 18
  • 229
  • 307
Lanny Bose
  • 1,811
  • 1
  • 11
  • 16
  • Interesting solution. If I check for itemId == (myNewNumber || myNumber) won't I have 2 "active" tags in the list then, the one with the initial value and the new one? I would like to have only the most recent one as "active". – Dominic May 12 '20 at 08:56
  • Nope! The `(myNewNumber || myNumber)` bit will only return the original number if `myNewNumber` remains undefined. Read my edit above – Lanny Bose May 12 '20 at 23:06
  • ah ok, sounds good to me :) But now with this I get "_vm.isCurrent is not a function" in the console. I tried in "computed: {" with "isCurrent: function(itemId)" as well as with "isCurrent(itemId)" but no luck there. In template the check is: " v-if="isCurrent(item.id)"" – Dominic May 13 '20 at 14:40
  • Ah! Sorry, it should be a method, not a computed. Fixed above. – Lanny Bose May 13 '20 at 21:18
  • Yes! That's it, thank you @Lanny Bose, it works nicely. – Dominic May 13 '20 at 23:42