Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

benji's avatar
Level 6

Reactive v-if template

Hello everyone. Been trying to make the button reactive, tried a few things now but can't get it to work. Is it because I'm using computed property and not declaring isFavourite in data?

new Vue({
  el: '#app'
});


Vue.component('favourite-button', {
  props: ['id', 'favourites'],
  template:
  `
          <input class="hidden" type="input" name="_method" value="{{ id }}" v-model="form.listings_id"></input>
          <button v-if="isFavourite == true" class="favourited" @click="delete(favourite)" :disabled="form.busy">
              <i class="fa fa-heart" aria-hidden="true"></i>
          </button>

          <button v-else class="not-favourited" @click="create(favourite)" :disabled="form.busy">
              <i class="fa fa-heart" aria-hidden="true"></i>
          </button>
          <pre>{{ isFavourite == true }}</pre>
      `,

  data:
  function() {
    return {
        form: new SparkForm({
            listings_id: ''
        }),
    };
  },


  created() {
      this.getFavourites();

  },

  computed: {
      isFavourite: function() {

        for (var i = 0; this.favourites.length; i++)
          {
            if (this.favourites[i].listings_id == this.id) {
            return true;

          }
        }
      },

    },

  methods: {

    getFavourites() {
        this.$http.get('/api/favourites')
          .then(response => {
            this.favourites = response.data;
          });
        },

    create() {
        Spark.post('/api/favourite', this.form)
            .then(favourite => {
            this.favourite.push(favourite);
            this.form.id = '';

            });


        },

        delete(favourite) {
            this.$http.delete('/api/favourite/' + this.id);
            this.form.id = '';

        }
      }

    });

Vue.component('listings',  {
    template: '#listing-template',

    data: function() {
      return {
            listings: [], favourites: [],
        };
    },

    created() {
        this.getListings();
    },

    methods: {
      getListings() {
          this.$http.get('/api/listings')
            .then(response => {
              this.listings = response.data;
          });
        }
      }
});
0 likes
13 replies
edoc's avatar

I don't think you can use computed props like that in your html

remove

== true

and see if it works

benji's avatar
Level 6

thanks @edoc the == true was unnecessary and I've now removed. Still not reactive though.

edoc's avatar

can u try to rewrite this

isFavourite: function() {

        for (var i = 0; this.favourites.length; i++)
          {
            if (this.favourites[i].listings_id == this.id) {
            return true;

          }
        }
      },

to

isFavorite: function(){
    return this.favourites.some(function(favourite){
    return favourite.listing_id === this.id
  })
}
benji's avatar
Level 6

Hi @edoc, I did this (we spell favourite in the UK) and got TypeError: Cannot read property 'id' of undefined (found in component: <favourite-button>.

It might be worth mentioning that the forloop and the button works it just doesn't update after the event.

  computed: {

      // isFavourite: function() {
      //
      //   for (var i = 0; this.favourites.length; i++)
      //     {
      //       if (this.favourites[i].listings_id == this.id) {
      //       return true;
      //
      //     }
      //   }
      // },

      isFavourite: function(){
        return this.favourites.some(function(favourite){
          return favourite.listings_id === this.id
        })
      }

    },
edoc's avatar

@benji sorry I forgot to bind "this"

isFavourite: function(){
        return this.favourites.some(function(favourite){
          return favourite.listings_id === this.id
        }.bind(this))
      }
benji's avatar
Level 6

@edoc interesting, it works the same as forloop but it's still not reactive :(

  computed: {

      // isFavourite: function() {
      //
      //   for (var i = 0; this.favourites.length; i++)
      //     {
      //       if (this.favourites[i].listings_id == this.id) {
      //       return true;
      //
      //     }
      //   }
      // },

      isFavourite: function(){
        return this.favourites.some(function(favourite){
          return favourite.listings_id === this.id
        }.bind(this))
      }

    },
benji's avatar
Level 6

@edoc I've put favourites in data as follows, can you advise?

  data:
  function() {
    return {
        form: new SparkForm({
            listings_id: ''
        }),
      isFavourite: ''
    };
  },
benji's avatar
Level 6

I've done a few things to try and make my component reactive as below. Unfortunately it hasn't worked. Any more tips?

  • Added favourites to data instead of props
  • Using if statement in method triggered on event instead of v-if template as before
  • Conditional class which is meant to provide feedback to user on click but still isn't reactive

(Everything renders as should on a page refresh)

new Vue({
  el: '#app'
});


Vue.component('favourite-button', {
    props: [
      'id'
    ],

  template:
  `   <input class="hidden" type="input" name="_method" value="{{ id }}" v-model="form.listings_id"></input>
      <button class="{{ isFavourite ? 'favourited' : 'not-favourited' }}" v-on:click="favToggle"><i class="fa fa-heart"></i></button>
        <pre>{{  isFavourite  }}</pre>
      `,

  data:
  function() {
    return {
        form: new SparkForm({
            listings_id: ''
        }),
        favourites: []
    };
  },

  created() {
      this.getFavourites();

  },

  computed: {

      isFavourite: function(){
        return this.favourites.some(function(favourite){
          return favourite.listings_id == this.id
        }.bind(this))
      }

    },

  methods: {

    favToggle() {
      if (this.isFavourite) {
        this.$http.delete('/api/favourite/' + this.id);
        this.form.id = '';
      } else {
        Spark.post('/api/favourite', this.form)
            .then(favourite => {
            this.favourites.push(favourite);
            this.form.id = '';

            });
      }
    },

    getFavourites() {
        this.$http.get('/api/favourites')
          .then(response => {
            this.favourites = response.data;
          });
        },

      }

    });

Vue.component('listings',  {
    template: '#listing-template',

    data: function() {
      return {
            listings: [], favourites: [],
        };
    },

    created() {
        this.getListings();
    },

    methods: {
      getListings() {
          this.$http.get('/api/listings')
            .then(response => {
              this.listings = response.data;
          });
        }
      }
});
edoc's avatar

@benji I think u forgot to bind your class using ":"

class="{{ isFavourite ? 'favourited' : 'not-favourited' }}"

rewrite the code like this

:class="[isFavourite? 'favourited ':'not-favourited']" 
benji's avatar
Level 6

@edoc thanks, I believe we've almost got it working :)

When I remove :id="listing.id below, the button becomes reactive but it doesn't pass the listings.id needed to favourite that listing for the user. So how do I now bind everything correctly so it becomes reactive but passes the data too?

<template id="listing-template">
  <div class="container">
    <div v-for="listing in listings" class="panel panel-default">
      <div class="panel-body">
        <div>@{{ listing.id }}</div>
         <favourite-button :id="listing.id"> <!-- CHANGE THIS ID CODE -->
        </favourite-button>
      </div>
    </div>
  </div>
</template>
edoc's avatar

I don't know why it becomes reactive after removing props although the computed prop depends on the prop

edoc's avatar

if you are not using Vue 2 you can always use $broadcast.But,I think it's better to know why the computed prop works without passing down the id

Please or to participate in this conversation.