Nathan Jones
Nathan Jones

Reputation: 5144

How do I dynamically insert html table cell data into a table with dynamic headers in React?

I'm creating a <table> with React 0.14.6 that has a series of column headers that are dynamically inserted into a <thead> of a <table>:

CourseTable.js:

import CourseList from './CourseList';
import GradesHeader from './GradesHeader';
import React, {Component} from 'react';

export default class CourseTable extends Component {
  /**
   * Gets all unique terms that have grades
   * @param courses {Array}
   * @return {Array}
   */
  getUniqueTerms(courses) {
    let uniqueTerms = [];
    courses.forEach(course => {
      if (course.grades) {
        Object.keys(course.grades).forEach(term => {
          if (uniqueTerms.indexOf(term) === -1) {
            uniqueTerms.push(term);
          }
        })
      }
    });
    return uniqueTerms;
  }

  createGradesHeaders(courses) {
    return this.getUniqueTerms(courses).map(term => {
      return (
        <th>
          <GradesHeader headerText={term}/>
        </th>
      );
    });
  }

  render() {
    let headers = this.createGradesHeaders(this.props.courses);
    return (
      <table className="table table-bordered table-condensed">
        <thead>
        <tr>
          <th>
            Period
          </th>
          <th>
            Class
          </th>
          <th>
            Term
          </th>
          <th>
            Date Enrolled
          </th>
          <th>
            Date Left
          </th>
          <th>
            <div>Absenses</div>
            <div>All (Excused)</div>
          </th>
          <th>
            Tardies
          </th>
          <th></th>
          {headers}
          <th>
            Teacher
          </th>
        </tr>
        </thead>
        <CourseList courseData={this.props.courses}/>
      </table>
    );
  }
}

GradesHeader.js:

import React, {Component} from 'react';

export default class GradesHeader extends Component {
  render() {
    return (
      <div>
        {this.props.headerText}
      </div>
    );
  }
}

Course.js:

import React, {Component} from 'react';

export default class Course extends Component {
  render() {
    const unexcused = !!this.props.courseData.attendance.unexcused ? this.props.courseData.attendance.unexcused : 0;
    const excused = !!this.props.courseData.attendance.excused ? this.props.courseData.attendance.excused : 0;
    const totalAbsences = unexcused + excused;
    return (
      <tr>
        <td>
          {this.props.courseData.expression}
        </td>
        <td>
          {this.props.courseData.course_name}
        </td>
        <td>
          {this.props.courseData.abbreviation}
        </td>
        <td>
          {this.props.courseData.dateenrolled}
        </td>
        <td>
          {this.props.courseData.dateleft}
        </td>
        <td>
          {totalAbsences}
          ({excused})
        </td>
          // Grades for this course go here
        <td>
        </td>
        <td>
          {this.props.courseData.lastfirst}
        </td>
      </tr>
    );
  }
}

You can see in CourseTable.js where I'm dynamically generating the headers for this section of the table's <thead>. I'm confused on how to insert/pidgeonhole the data for each grade into the corresponding column.

I think I need to somehow identify each of the <th> header elements that are dynamically generated, and reference those when I insert into those columns, but I'm not sure how.

Upvotes: 3

Views: 2374

Answers (2)

VonD
VonD

Reputation: 5155

If I understand well the question and your data structure, you could pass the grades computed with getUniqueTerms to your CourseList component.

So in CourseTable.js :

render() {
    let grades = this.uniqueTerms(this.props.courses);
    let headers = this.createGradesHeaders(grades);
    // and then in your jsx
    <CourseList courseData={ this.props.courses } grades={ grades } />
}

And in the render method of your Course component :

{
    this.props.grades.map( grade =>
        <td key={ grade }>
            // whatever data you need to print from this.props.courseData.grades[grade]
        </td>
    )
}

Hope it helps!

Upvotes: 1

Thomas Johansen
Thomas Johansen

Reputation: 15248

You should, as mentioned in a comment, use states. States are pretty useful. ReactJS states If you want to pidgeonhole new headers you could assign them with an ID, so that you could cycle through them correctly when rendering a new. As mentioned, react re-renders on state change. There are a few examples of this in facebooks tutorial page and also in the documentation.

Say you gather all your headers in an object, or perhaps they are coming from a database.

var headers = [{id: 0, name: grade}
               {id: 1, name: term}
]

So in your render method of heads you can put them in like this:

constructor(props) {
    super(props);
    this.state = {
        headers: headers,
    }
},

sortAlg(arr) {
    //Choose one, make one
},

render() {
this.sortAlg(this.state.headers);
var headerArr = [];
headers.forEach(function(header) {
    headerArr.push(<YourHeader header = {header} />
});
return (
  <table className="table table-bordered table-condensed">
    <thead>
        {headerArr}
    </thead>
    <CourseList courseData={this.props.courses}/>
  </table>
);

The <YourHeader /> is a component you make for better control. How I like doing it, atleast.

I hope this helps, good luck.

Upvotes: 1

Related Questions