Toggle active class between child components VueJS

752 Views Asked by At

I have an explore page in VueJS(see below) and this contains child components called toggle-tiles and these lay out like grids. What I want to do is when one of these "toggle-tiles" is clicked it has an active class applied which you can see from my code below but where I am hitting a problem.

If one has an active class I want the rest of them to have the active class removed. So only one can be active at any one time. Currently they all set to active as I click through.

Please see my code below.

<template>
  <div>
    <section class="explore">
      <div class="explore__banner">
        <img src="/static/img/placeholder-banner.jpg" alt="" />
      </div>
      <div class="in">
        <div class="explore__intro">
          <div class="in">
            <h2 class="heading heading--uppercase heading--borderbottom">EXPLORE</h2>
            <p>Once-in-a-lifetime experiences, art exhibitions, culinary adventures and family fun. Stay inspired and discover
              your own Algarve.</p>
          </div>
        </div>
        <div class="explore__filters">
          <div class="in">
            <ul class="list list--inline list--uppercase">
              <li>
                <a href="#">all</a>
              </li>
              <li>
                <a href="#">experiences</a>
              </li>
              <li>
                <a href="#">what's on</a>
              </li>
              <li>
                <a href="#">family</a>
              </li>
              <li>
                <a href="#">food</a>
              </li>
              <li>
                <a href="#">hotel amenities</a>
              </li>
            </ul>
          </div>
        </div>
        <div class="explore__blocks">
          <div class="in">
            <toggle-tile @toggle-active="toggleTile" :showtile="isActive"></toggle-tile>
            <toggle-tile @toggle-active="toggleTile" :showtile="isActive"></toggle-tile>
            <toggle-tile @toggle-active="toggleTile" :showtile="isActive"></toggle-tile>
          </div>
        </div>
      </div>
    </section>
    <room-builder></room-builder>
    <offer-carousel :offers="offers"></offer-carousel>
  </div>
</template>

<script>
  import OfferCarousel from '@/components/OfferCarousel'
  import RoomBuilder from '@/components/RoomBuilder'
  import ToggleTile from '@/components/ToggleTile'
  export default {
    name: "Explore",
    components: {
      'offer-carousel': OfferCarousel,
      'room-builder': RoomBuilder,
      'toggle-tile': ToggleTile,
    },
    data() {
      return {
        isActive: false,
        offers: [{
            image: '/static/img/placeholder-offer.jpg',
            active: true,
            captionText: 'SPECIAL OFFER HEADING',
            buttonText: 'read more',
            buttonUrl: '#',
            opacity: 1,
            id: 1
          },
          {
            image: '/static/img/placeholder-offer.jpg',
            active: false,
            captionText: 'SPECIAL OFFER HEADING',
            buttonText: 'read more',
            buttonUrl: '#',
            opacity: 0,
            id: 2
          }
        ]
      }
    },
    methods: {
      toggleTile: function () {
        console.log("clicked");
      }
    }
  }

</script>


<template>
  <div class="block" v-on:click="toggleState" v-bind:class="{active: showBlock }">

    <div class="in">
      <div class="block__image">
        <img src="/static/img/block-placeholder.jpg" alt="" />
        <div class="block__image--hover">
          <img src="/static/img/svg/open.svg" alt="" />
        </div>
      </div>
      <div class="block__title">
        <div class="in">
          <h6 class="heading heading--uppercase">EXPERIENCES</h6>
          <h3>SUMMER TREATMENTS AT CONRAD SPA</h3>
        </div>
      </div>
    </div>
    <div class="block__panel">
      <div class="in">
        <div class="panel__close">
          <img src="/static/img/svg/cross.svg" alt="" />
        </div>
        <div class="panel__gallery">
          <basic-carousel :images="images"></basic-carousel>
        </div>
        <div class="panel__content">
          <h6 class="heading heading--uppercase">RIA FORMOSA NATURAL PARK</h6>
          <p>Spend the day with a local clam picker. A real life adventure on a traditional fisherman’s wooden boat, discovering
            ancient clam picking secrets. You’ll also visit an oyster bank with oyster degustation and local sparkling wine.
          </p>
          <h6 class="heading heading--uppercase">WHEN?</h6>
          <p>Daily, depending on the tide. Check when booking.
          </p>
          <h6 class="heading heading--uppercase">HOW LONG WILL IT TAKE?
          </h6>
          <p>Approximately 3 hours including transfer time.</p>
          <h6 class="heading heading--uppercase">HOW MUCH WILL IT COST?</h6>
          <p>€250,00 for up to 4 guests. €50 per additional guest. Free for children up to 6 years old. Maximum 12 guests. Return
            transfers to Faro beach: €62.50 for up to four guests. €87.50 for 5-8 guests. Must be booked in advance.
          </p>
          <h6 class="heading heading--uppercase">WHAT’S INCLUDED?</h6>
          <p>Clam picking equipment, waterproof boots (on request, depending on sizes), oyster tasting & sparkling wine. Don’t
            forget to bring sunscreen, swimming costumes and flip flops.
          </p>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
  import BasicCarousel from '@/components/BasicCarousel'
  export default {
    name: 'ToggleTile',
    components: {
      'basic-carousel': BasicCarousel,
    },
    props: ['showtile'],
    data() {
      return {
        showBlock: false,
        images: [{
            image: '/static/img/meetings-slide.jpg',
            active: true,
            opacity: 1,
            id: 1
          },
          {
            image: '/static/img/meetings-slide.jpg',
            active: false,
            opacity: 0,
            id: 2
          }
        ],
      }
    },
    methods: {
      toggleState: function () {
        this.$emit('toggle-active');
        this.showBlock = this.showtile;
        this.showBlock = true;
      }
    }
  }

</script>

Thanks

1

There are 1 best solutions below

0
Cassio Cabral On

That's where Vue shines. For that you need to ID your tiles so you would know which one isActive at the moment. Something that would differentiate one tile from another.

The best way I can see this problem is. Instead of having isActive: false in Explore it would be better to have activeTile: 1 or activeTile: 'FooTile' and a prop for ToggleTile(id or name, to follow this example, but could be anything)

And in your ToggleTile component emit the event with that prop like:

this.$emit('toggle-active', this.name)
or
this.$emit('toggle-active', this.id)

Back to Explore change the toggleTile function

toggleTile: function (nameOrId) {
  this.activeTile = nameOrId
}


<toggle-tile @toggle-active="toggleTile" :showtile="activeTile === 'FooTile'"></toggle-tile>

<toggle-tile @toggle-active="toggleTile" :showtile="activeTile === 1"></toggle-tile>

The "magic" here is that whenever activeTile change that prop showTile will change showing the active tile and hiding the others.

This is a common way of thinking about when developing in Vue. Say when that condition would be true instead of making the changes when clicking or something like with JQuery.