简体   繁体   中英

how to listen to vue events when callback with $refs

building a rock paper scissors game and want to make the computer choose a random hand. so far so good. i want that the computer gets triggered with a event listener like v-on:humanChoosedHand="startGame()" to choose a hand, WHEN the player/human has choosed a hand as well. i can check and see the event humanChoosedHand gets fired when i choose a hand for the human, but the parent GameField.vue is not listening code looks like this:

the parent GameField.vue

<template>
  <div id="game-field">
    <button class="btn btn-primary" @click="startGame()">Start</button>

    <div class="row">
      <div class="col-md-6 pt-5">
        <Human></Human>
      </div>
      <div class="col-md-6 pt-5">
        <Computer ref="computer" v-on:humanChoosedHand="startGame()"></Computer>
      </div>
    </div>
  </div>
</template>

<script>
import Human from './Human.vue'
import Computer from './Computer.vue'
export default {
  data: () => {
    return {
      score: [
        {
          human: 0
        },
        {
          computer: 0
        }
      ]
    }
  },
  components: {
    Human,
    Computer
  },
  props: {
    difficulty: String
  },
  methods: {
    startGame () {
      console.log('Game Started')
      // this.$refs.computer.shuffleAnimation()
      this.$refs.computer.setRandomHand() //<-----
      // listen to emit here, not in html template
    },
    testFunction () {
      console.log('test')
    }
  }
}
</script>

<style lang="scss">
#game-field {
  .far {
    cursor: pointer;
    font-size: 300px;
    width: 350px;
    height: 350px;
  }

  .row {
    margin: 0 0 0 0;

    .select-hand {
      position: relative;
      left: 60px;
      .far {
        font-size: 80px;
        height: 0;
        width: 0;
      }
      .far:hover {
        color: $orange;
      }
    }
  }

  .btn-primary {
    background-color: $orange;
    border-color: $orange;
    position: relative;
    top: 50%;
    left: 50%;
    transform: translate(-50%, 30%);
  }
  .btn-primary:focus {
    background-color: $orange;
    border-color: $black !important;
    box-shadow: 0 0 0 0.2rem rgba(255, 51, 0, 0.644);
  }
}
</style>

then the child Computer.vue :

<template>
  <div id="computer">
    <div class="text-center">
      <div class="h2 mb-5">Computer</div>
      <i class="far" :class="playIcon"></i>
      <div class="hand h3 mt-4">{{ activeHand }}</div>
    </div>
  </div>
</template>

<script>
export default {
  data: () => {
    return {
      winCounter: 0,
      activeHand: 'I am ready',
      playIcon: '',
      hands: [
        {
          name: 'Rock',
          strength: 'scissor',
          weakness: 'paper',
          icon: 'fa-hand-rock'
        },
        {
          name: 'Paper',
          strength: 'rock',
          weakness: 'scissor',
          icon: 'fa-hand-paper'
        },
        {
          name: 'Scissor',
          strength: 'paper',
          weakness: 'rock',
          icon: 'fa-hand-scissors'
        }
      ]
    };
  },
  methods: {
    setRandomHand() {
      // THIS SHOULD LISTEN TO THE 'humanChoosedHand' 
      // and should gets triggered by a 'ref.computer.setRandomHand' then
      console.log('computer listens');

      let choosedHand = Math.round(Math.random() * 2);
      this.activeHand = this.hands[choosedHand].name;
      this.playIcon = this.hands[choosedHand].icon;
      this.$emit('computerChoosedHand', this.activeHand.toLowerCase());
    }
    // shuffleAnimation () {
    //   setInterval(() => {
    //     let shuffle = Math.round(Math.random() * 2)
    //     this.activeHand = this.hands[shuffle].name
    //     this.playIcon = this.hands[shuffle].icon
    //   }, 100)
    // }
  }
};
</script>

<style lang="scss">
#computer .far {
  transform: rotate(-90deg);
}
</style>

other child of GameField - Human.vue

<template>
  <div id="human">
    <div class="text-center">
      <div class="h2 mb-5">Human</div>
      <i class="far" :class="playIcon"></i>
      <div class="h3 mt-4">{{ activeHand }}</div>
      <div class="row select-hand mt-4">
        <div class="col-md-4">
          <i class="far fa-hand-rock" @click="setHand(hands[0])"></i>
        </div>
        <div class="col-md-4">
          <i class="far fa-hand-paper" @click="setHand(hands[1])"></i>
        </div>
        <div class="col-md-4">
          <i class="far fa-hand-scissors" @click="setHand(hands[2])"></i>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data: () => {
    return {
      winCounter: 0,
      activeHand: 'Choose a Hand',
      playIcon: '',
      hands: [
        {
          name: 'Rock',
          strength: 'scissor',
          weakness: 'paper',
          icon: 'fa-hand-rock'
        },
        {
          name: 'Paper',
          strength: 'rock',
          weakness: 'scissor',
          icon: 'fa-hand-paper'
        },
        {
          name: 'Scissor',
          strength: 'paper',
          weakness: 'rock',
          icon: 'fa-hand-scissors'
        }
      ]
    }
  },
  methods: {
    setHand (hand) {
      this.activeHand = hand.name
      this.playIcon = hand.icon
      this.$emit('humanChoosedHand', this.activeHand.toLowerCase())
    }
  }
}
</script>

<style lang="scss">
#human .far {
  transform: rotate(90deg);
}
</style>

Maybe it is not possible to listen to a event and then $ref.component.callFunction() ?

Gamefield is not listening properly, since you are missing some code in your tag. You need to add @humanChoosedHand in your tag:

<Human @humanChoosedHand="triggerComputer"></Human>

which now correctly listens to changes of humanChoosedHand

Now triggerComputer can trigger the computer function from Gamefield :

triggerComputer() {
  this.$refs.computer.setRandomHand();
}

therefore remove v-on:humanChoosedHand="startGame()" from the Computer tag.

Here's a SANDBOX to play with.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM