user14745028
user14745028

Reputation:

Pulling player stats using NHL api

I was looking for some help getting player stats from this NHL api for each player clicked like this: player stats. Ive been stuck on this for 2 weeks now, so any help would be so greatly appreciated.

I have it doing what I want if i pipe the teams roster link as the endpoint in the fetch, but not if i pass a teams link through my function.

When the teams link is passed, this is the error i get: GET https://statsapi.web.nhl.comundefined/ net::ERR_NAME_NOT_RESOLVED

And this is my code in question:

function displayPlayerStats(e, teamLink) {
  fetch(`https://statsapi.web.nhl.com${teamLink}`) // /api/v1/teams/14/roster
    .then(res => res.json())
    .then(data => {
    //console.log(data);
    if (e.target.id == 'player-name') {
      let name = e.target.text.trim();
      console.log(name);
      data.roster.map(player => {
        if (name == player.person.fullName) {
          getPlayerStatsByID(player.person.id);
        }
      });
    }
  });
}

If you replace ${teamLink} in the fetch with the roster link commented out next to it, it works for the Tampa Bay Lightning.

My fiddle of the full project is here if it helps: jsfiddle

Upvotes: 0

Views: 903

Answers (2)

Mr. Polywhirl
Mr. Polywhirl

Reputation: 48693

You could utilize async/await to create a form that populates drop-downs in a synchronized way.

const api = {
  baseUrl: 'https://statsapi.web.nhl.com/api/v1'
};

const triggerEvent = (el, eventName) => {
  const event = document.createEvent('HTMLEvents');
  event.initEvent(eventName, true, false);
  el.dispatchEvent(event);
};

const emptySelect = (select) => {
  for (let i = select.options.length - 1; i >= 0; i--) {
    select.remove(i);
  }
}

const populateSelect = (select, data, keyFn, textFn) => {
  emptySelect(select);
  data.forEach(item =>
    select.add(new Option(textFn(item), keyFn(item))));
  return select;
};

const populateForm = (form, fields) =>
  Object.entries(fields).forEach(([key, value]) =>
    form[key].value = value);

const populateSeasons = (select) => {
  const year = new Date().getUTCFullYear();
  const seasons = new Array(50).fill('').map((_, index) => ({
    start: year - index - 1,
    end: year - index
  }));
  return populateSelect(select, seasons,
    season => `${season.start}${season.end}`,
    season => `${season.start} – ${season.end}`);
}

const fetchTeams = async() => {
  const response = await fetch(`${api.baseUrl}/teams`);
  const json = await response.json();
  return json.teams.sort((a, b) =>
    a.name.localeCompare(b.name));
}

const fetchRoster = async(teamId) => {
  const response = await fetch(`${api.baseUrl}/teams/${teamId}/roster`);
  const json = await response.json();
  return json.roster.sort((a, b) => {
    const n1 = a.jerseyNumber ? parseInt(a.jerseyNumber, 10) : 100;
    const n2 = b.jerseyNumber ? parseInt(b.jerseyNumber, 10) : 100;
    const res = n1 - n2;
    if (res !== 0) return res;
    return a.person.fullName
      .localeCompare(b.person.fullName, 'en', { sensitivity: 'base' });
  });
}

const fetchPlayer = async(playerId) => {
  const response = await fetch(`${api.baseUrl}/people/${playerId}`);
  const json = await response.json();
  const [player] = json.people;
  return player;
}

const fetchStats = async(playerId, seasonId) => {
  const response = await fetch(`${api.baseUrl}/people/${playerId}/stats?stats=statsSingleSeason&season=${seasonId}`);
  const json = await response.json();
  let seasonStats;
  try {
    const { stats: [{ splits: [{ stat }] }] } = json;
    seasonStats = stat;
  } catch (e) {
    seasonStats = {};
  }
  const season = seasonId.match(/^(\d{4})(\d{4})$/).slice(1).join(' – ');
  const { shots, assists, goals, timeOnIce } = seasonStats;
  return { season, shots, assists, goals, timeOnIce };
}

const formatPlayerOptionText = (player) =>
  `${player.jerseyNumber || ''} – ${player.person.fullName} (${player.position.abbreviation})`

const onTeamChange = async(e) => {
  const teamId = e.target.value;
  const roster = await fetchRoster(teamId);
  const playerSelect = document.querySelector('select[name="roster"]');

  populateSelect(playerSelect, roster,
    player => player.person.id,
    formatPlayerOptionText);

  triggerEvent(playerSelect, 'change');
};

const onPlayerChange = async(e) => {
  const seasonSelect = document.forms['nhl']['season'];
  const playerId = e.target.value;
  const player = await fetchPlayer(playerId);

  populateForm(document.forms['player-info'], {
    'first-name': player.firstName,
    'last-name': player.lastName,
    'birth-date': player.birthDate,
    'current-team': player.currentTeam.name,
    'primary-number': player.primaryNumber,
    'primary-position': player.primaryPosition.type
  });

  triggerEvent(seasonSelect, 'change');
};

const onSeasonChange = async(e) => {
  const playerSelect = document.querySelector('select[name="roster"]');
  const seasonId = e.target.value;
  const stats = await fetchStats(playerSelect.value, seasonId);

  populateForm(document.forms['player-stats'], {
    'stat-season': stats.season,
    'stat-shots': stats.shots || 'N/A',
    'stat-assists': stats.assists || 'N/A',
    'stat-goals': stats.goals || 'N/A'
  });
}

const main = async() => {
  const nhl = document.forms['nhl'];
  const teamSelect = nhl['teams'];
  const playerSelect = nhl['roster'];
  const seasonSelect = nhl['season'];

  populateSeasons(seasonSelect);

  const teams = await fetchTeams();

  populateSelect(teamSelect, teams, team => team.id, team => team.name);

  teamSelect.addEventListener('change', onTeamChange);
  playerSelect.addEventListener('change', onPlayerChange);
  seasonSelect.addEventListener('change', onSeasonChange);

  teamSelect.value = 14;           // Tampa Bay
  seasonSelect.value = '20192020'; // 2019-2020

  triggerEvent(teamSelect, 'change');
};

main();
:root {
  --background-color: #111;
  --background-color-alt: #222;
  --primary-font-color: #DDD;
  --input-background-color: #222;
  --input-font-color: #EEE;
}

body {
  background: var(--background-color);
  color: var(--primary-font-color);
  padding: 0.5em;
}

input,
select {
  background: var(--input-background-color);
  color: var(--input-font-color);
}

input[type="text"]:disabled {
  background: inherit;
  border: none;
}

h1 {
  text-align: center;
}

hr {
  border-color: var(--background-color-alt);
}

.entry {
  margin-bottom: 0.25em;
  padding: 0.25em;
}

.entry:nth-child(even) {
  background: var(--background-color-alt);
}

.entry label {
  display: inline-block;
  width: 8em;
  font-weight: bold;
}

.results {
  display: grid;
  grid-auto-flow: column;
  grid-column-gap: 1em;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css" rel="stylesheet" />
<h1>NHL Player Stats</h1>
<form name="nhl">
  <div class="entry">
    <label>Team</label>
    <select name="teams"></select>
  </div>
  <div class="entry">
    <label>Player</label>
    <select name="roster"></select>
  </div>
  <div class="entry">
    <label>Season</label>
    <select name="season"></select>
  </div>
</form>
<hr />
<div class="results">
  <div>
    <h2>Info</h2>
    <form name="player-info">
      <div class="entry">
        <label>First Name</label>
        <input type="text" name="first-name" autocomplete="off" disabled />
      </div>
      <div class="entry">
        <label>Last Name</label>
        <input type="text" name="last-name" autocomplete="off" disabled />
      </div>
      <div class="entry">
        <label>Date of Birth</label>
        <input type="text" name="birth-date" autocomplete="off" disabled />
      </div>
      <div class="entry">
        <label>Current Team</label>
        <input type="text" name="current-team" autocomplete="off" disabled />
      </div>
      <div class="entry">
        <label>Jersey Number</label>
        <input type="text" name="primary-number" autocomplete="off" disabled />
      </div>
      <div class="entry">
        <label>Position</label>
        <input type="text" name="primary-position" autocomplete="off" disabled />
      </div>
    </form>
  </div>
  <div>
    <h2>Stats</h2>
    <form name="player-stats">
      <div class="entry">
        <label>Season</label>
        <input type="text" name="stat-season" autocomplete="off" disabled />
      </div>
      <div class="entry">
        <label>Shots</label>
        <input type="text" name="stat-shots" autocomplete="off" disabled />
      </div>
      <div class="entry">
        <label>Assists</label>
        <input type="text" name="stat-assists" autocomplete="off" disabled />
      </div>
      <div class="entry">
        <label>Goals</label>
        <input type="text" name="stat-goals" autocomplete="off" disabled />
      </div>
    </form>
  </div>
</div>

Upvotes: 1

Zac Anger
Zac Anger

Reputation: 7787

Based on your error, your variable is undefined. Looking through your full code, it looks like you're only passing one argument to displayPlayerStats but it's expecting two. This should work for the case when you're calling the function in getRosterByID: function displayPlayerStats(teamLink) {. You're also calling it in a click handler, but there's no teamLink` available at that part of the code, so that one will fail.

Upvotes: 0

Related Questions