Jabba the Hutt
Jabba the Hutt

Reputation: 664

Fetching proper route path

So I am trying to fetch and display players that belong to team. I have a dropdown button which has a selection of said teams and on click, it should render the chosen team's players. The problem I have is that on click, it renders all of the players in my seed data, irrelevant of the team it belongs to. I am not sure what I am missing but here is the code I have. In Postman when I put "http://localhost:3000/api/teams/1/players" it is returning all of the players instead of just the one that belongs to team with id 1.

Edit: I fixed the issue with filtering data as now the url "http://localhost:3000/api/teams/1/players" in Postman works correctly. However I am getting 'TypeError: this.props.players.cards.map is not a function' from the DataContainer when it tries to render the players after clicking the team on the dropdown button. All I did was change the index in my controller. Also the terminal says "ActiveRecord::RecordNotFound (Couldn't find Team with 'id'=Chaos):"

Here is my players controller:

class PlayersController < ApplicationController
before_action :set_params, only: [:show]

def index
  @players = Team.find(params[:team_id]).players
  render json: @players, status: 200
end

def show
  render json: @player, status: 200
end

private

def player_params
  params.require(:player).permit(:name, :player_type, :cost, 
  :movement_allowance, :strength, :agility, :armour_value, :skills, 
  :team_id)
end

def set_params
  @player = Player.find(params[:id])
end

My filter file which contains the dropdown button (edited):

class PlayerFilter extends Component {
constructor() {
  super();
  this.state = { data: [] };
}

async fetchButtonTeams() {
  const response = await fetch(`/api/teams`);
  const json = await response.json();
  this.setState({ data: json });
}

componentDidMount(){
  this.fetchButtonTeams()
}

handleTeamSelection = e => {
    e.preventDefault();
    const { target: { title }} = e;
    this.props.setTeam(title);
    this.props.fetchPlayers(title);
};

render() {
    return (
        <Container>
            <Row>
                <DropdownButton id="dropdown-player-button" title={this.props.team}>
                      {this.state.data.map(team => (
                        <div key={team}>
                            <Dropdown.Item onClick={this.handleTeamSelection} title={team.name}>{team.name}</Dropdown.Item>
                        </div>
                    ))}
                </DropdownButton>
            </Row>
        </Container>
      )
   }
}

const mapStateToProps = state => {
    return {
        team: state.players.team
    }
};

const mapDispatchToProps = dispatch => {
    return {
        fetchPlayers: params => dispatch(fetchPlayers(params)),
        fetchTeams: params => dispatch(fetchTeams(params)),
        setTeam: team => dispatch({ type: "SET_TEAM", team })

    }
};

export default connect(mapStateToProps, mapDispatchToProps)(PlayerFilter)

My player fetch being called in the filter (edited)

export const fetchPlayers = teamId => {
return dispatch => {
    dispatch({ type: 'LOADING_PLAYERS' });
    fetch(`/api/teams/${teamId}/players`)
        .then(res => res.json())
        .then(responseJSON => { dispatch({ type: 'ADD_PLAYERS', cards: responseJSON})
        })
}

My DataContainer:

class DataContainer extends Component {

displayCards = () => {
    switch(this.props.path) {
        case "teams":
            return (this.props.teams.cards.map(card => (
              <NavLink style={{ color: "black" }} to={`/teams/${card.id}`} key={card.id}><TeamCard info={card} /></NavLink>
          )));

        case "players":
            return (this.props.players.cards.map(card => (
              <NavLink style={{ color: "black" }} to={`/players/${card.id}`} key={card.id}><PlayerCard info={card} /></NavLink>
          )));

        default:
            return (<div>Empty</div>)
    }
};

render() {
    return (
        <CardColumns>
            {this.displayCards()}
        </CardColumns>
      )
   }
}

const mapStateToProps = state => {
    return {
        teams: state.teams,
        players: state.players
    }
};

export default connect(mapStateToProps)(DataContainer)

My app.js:

import React from 'react';
import './App.css';
import { connect } from 'react-redux'

import NavButtonsContainer  from './containers/NavButtonsContainer';
import DataContainer from "./containers/DataContainer";

import PlayerDisplay from "./components/PlayerDisplay";
import PlayerFilter from "./components/PlayerFilter";

import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

import 'bootstrap/dist/css/bootstrap.min.css';
import Container from "react-bootstrap/Container";
import Row from "react-bootstrap/Row"
import Col from "react-bootstrap/Col"

const App = props => {
  return (
    <Container>
        <Router>
            <Switch>
                <Route path="/teams/:id">
                    <TeamDisplay info={props.info} />
                </Route>

                <Route path="/players/:id">
                    <PlayerDisplay info={props.info} />
                </Route>

                <Route path="/teams">
                    <Row>
                        <Col><TeamFilter path="teams" /></Col>
                    </Row>
                    <Row>
                        <NavButtonsContainer />
                    </Row>
                    <Row>
                        <DataContainer path="teams" />
                    </Row>
                </Route>

                <Route path="/players">
                    <Row>
                        <Col><PlayerFilter path="players" /></Col>
                    </Row>
                    <Row>
                        <NavButtonsContainer />
                    </Row>
                    <Row>
                        <DataContainer path="players" />
                    </Row>
                </Route>

                <Route path="/">
                       <NavButtonsContainer />
                </Route>
            </Switch>
        </Router>
    </Container>
  );
};

const mapStateToProps = state => {
    return {
        info: state.piece
    }
};

export default connect(mapStateToProps)(App);

Upvotes: 1

Views: 425

Answers (1)

Drew Reese
Drew Reese

Reputation: 202846

It was a little confusing calling your action creator parameter params, but I believe you need to access an (some) id property from it to correctly construct your request URL path. Giving parameters better, more meaningful names goes a long way in code readability.

export const fetchPlayers = teamId => {
  return dispatch => {
    dispatch({ type: 'LOADING_PLAYERS' });
    fetch(`/api/teams/${teamId}/players`) // interpolate id into path string
      .then(res => res.json())
      .then(responseJSON => { dispatch({ type: 'ADD_PLAYERS', cards: responseJSON});
  });
}

The next part is to call this action with the correct parameter from the handler.

// Playerfilter
handleTeamSelection = e => {
  e.preventDefault();
  const { target: { title }} = e;
  this.props.setTeam(title);
  this.props.fetchPlayers(title); // This is what I'm not sure about, but I'll get to that later
};

I couldn't immediately tell from the react-bootstrap docs on Dropdown.Item if it passes extra props to children (i.e. your title prop) or what is passed with the onClick and onSelect handlers (other than an event), or if perhaps it is more like an html select element where your handler needs to be bound to the Dropdown's onSelect prop instead of on each item.

I also took a peek at your db directory to try and glean your teams schema to figure out what the id field is. I noticed there is nothing relating your teams table to your players table, i.e. the players table has a field team_id that looks like it should be a foreign key into teams, so you can do a join. Maybe this is by design and you don't need to do multi-table queries.

In Postman when I put "http://localhost:3000/api/teams/1/players" it is returning all of the players instead of just the one that belongs to team with id 1

If this is your local server not returning filtered data, then this is indicative of an issue with your server code not querying your DB correctly and returning bad results.

I think you should first sort out your DB issue(s) and ensure it is returning correct responses, then tackle your client code to ensure it is making requests correctly.

Upvotes: 1

Related Questions