0

I have an app with a child component that makes a call to an api for a player's season stats. You click on the players name and I emit click event to child from Parent component. The problem is when you click on Players name from parent all instances of the child component are revealed. I just want the one player. I thought because I have a showComponent instance for each child by toggling this.showComponent in child would get my expected behavior but no. Code: Parent-

methods: {
        emitPlayerSeasonStatsClicked: function(event) {
        const target = event.target;
        EventBus.$emit("showPlayerTemplateClicked", target);
      }
    },
    template: `
                

    

                                                        <div v-for="playerStats in props_box_game_scores[index].data.gameboxscore.awayTeam.awayPlayers.playerEntry">
                                                                <tr v-if="playerStats.player.Position === 'P'" class="d-flex" v-bind:data-player-id="playerStats.player.ID">
                                                                    <td class="col-4 justify-content-center" scope="row" title="Click for Season Stats">
                                                                        {{playerStats.player.FirstName}} {{playerStats.player.LastName}} 
                                                                    <span v-if="playerStats.stats.Wins['#text'] === '1'">(W)</span> 
                                                                    <span v-else-if="playerStats.stats.Losses['#text'] === '1'">(L)</span> 
                                                                    <span v-else-if="playerStats.stats.Saves['#text'] === '1'">(S)</span>  
                                                                    </td>
                                                                    
                                                                    <td class="col-2 justify-content-center" justify-content="center">
                                                                        {{playerStats.stats.InningsPitched['#text']}}</td>
                                                                    <td class="col-2 justify-content-center">{{playerStats.stats.RunsAllowed['#text']}}</td>
                                                                    <td class="col-2 justify-content-center">{{playerStats.stats.PitcherStrikeouts['#text']}}</td>
                                                                    <td class="col-2 justify-content-center">{{playerStats.stats.EarnedRunAvg['#text']}}
                                                                    </td>
                                                                </tr>

                                                                <pitcher-season-stats v-bind:props_player_id="playerStats.player.ID"></pitcher-season-stats>
                                                                
                                                        </div>

Child-

cumlativeStats: Vue.component("player-season-stats", {
    props: ["props_player_id"],
    data: function() {
      return {
        Hits: "",
        HR: "",
        RBI: "",
        BattingAvg: "",
        showComponent: false
      };
    },
    mounted: function() {
      EventBus.$on("showPlayerTemplateClicked", function(data) {
        this.showComponent = !this.showComponent;
      });
    },
    methods: {
      retrievePlayerStats: function(playerId) {
        const url = `https://api.mysportsfeeds.com/v1.2/pull/mlb/2019-regular/cumulative_player_stats.json?player=`;
        const params = {
          playerstats: "AB,H,HR,RBI,AVG",
          force: true
        };

...
template: `
      <tr class="d-flex" v-if:showComponent>
        <td @click="retrievePlayerStats(props_player_id)" class="col-4 justify-content-center" scope="row">
            Season Stats</td>
        </td>
        <td class="col-2 justify-content-center" justify-content="center">
          {{ Hits }}</td>
        <td class="col-2 justify-content-center">{{ HR }}</td>
        <td class="col-2 justify-content-center"> {{ BattingAvg }}</td>
        <td class="col-2 justify-content-center">{{ RBI }}</td>
      </tr>
    ` // End template
  })

Any suggestions welcome. Sorry for the formatting.

1 Answer 1

1

The code provided doesn't actually call emitPlayerSeasonStatsClicked but I assume that's supposed to go on the <td> that includes the name.

If you write the click listener like this:

<td
  class="col-4 justify-content-center"
  scope="row"
  title="Click for Season Stats"
  @click="emitPlayerSeasonStatsClicked(playerStats.player.ID)"
>

Then include the id as part of the event emitted by the event bus:

emitPlayerSeasonStatsClicked: function(playerId) {
  EventBus.$emit("showPlayerTemplateClicked", playerId);
}

Listening for this in the mounted would be:

mounted: function() {
  EventBus.$on("showPlayerTemplateClicked", this.onShowPlayerTemplateClicked);
},

with method:

methods: {
  onShowPlayerTemplateClicked: function(playerId) {
    if (playerId === this.props_player_id) {
      this.showComponent = !this.showComponent;
    }
  }
}

Assuming the player ids are unique that should be enough to get it working.

However...

  1. The choice of an event bus to pass data to a child seems a poor one. There are several ways this could be done. One way would be to only create it when it's showing (external v-if). Another would be to use props. Yet another would be to use refs to call a method on the child.
  2. I don't understand how your code ever toggled anything. The this value for the listener in mounted will not be the component. I've fixed that by moving it to a method, which Vue will bind correctly. Another reason to move this to a method is that it allows you to remove the listener when the component is destroyed.
  3. v-if:showComponent is not a thing. I assume that should be v-if="showComponent".
  4. Your <tr> elements seem to be immediate children of <div> elements. That isn't correct HTML for tables.
Sign up to request clarification or add additional context in comments.

3 Comments

Appreciate the detailed answer. I will go thru remedies and let u know. I agree that the event bus is not ideal. The problem I had was that the click event was on a parent element and I needed a way to pass this to the child and parent->child communication seemed to be done thru $emit as shown in documentation.
@Alan I'm not aware of any documentation that recommends using $emit to communicate parent to child. Typically $emit is used to communicate child to parent. Most references to using an event bus in the official documentation have been removed as its use is discouraged. It is still mentioned here, vuejs.org/v2/style-guide/…, but only to recommend against it.
Sorry for the delay thanks for the solution. Now working as expected. The only thing I did was move the @click event handler to <table> element to take advantage of event propagation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.