Search code examples
vue.jsvue-component

Vue Component data object property's behavior not as expected


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.


Solution

  • 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.