Today, we will learn how to create a dynamic React table and add and delete rows using SharePoint Framework (SPFx). React table has options to customize the row limit based on your business needs. React table is a reusable component that can be used by changing only the column properties. I am using "Office UI Fabric React" controls to style the button controls.
Code Usage
The "ReactTable.tsx" reusable component can be written as below.
-
- import * as React from 'react';
- import { IHelloWorldProps } from '../IHelloWorldProps';
- import { DefaultButton, PrimaryButton, DialogType, Dialog, DialogFooter,Icon,Label, TextField, ActionButton,autobind } from 'office-ui-fabric-react';
-
-
-
- const tableColProps=[{
- id:1,
- NounName:"",
- CurrentPN:"",
- NewPart:"",
- Option:"",
- Supplier:"",
- EJR:""
- }]
-
-
-
-
- export interface IReactTableState {
- tableHeaders:any;
- rows:any;
- rowLimit:number;
- addButtonText:string;
- removeButtonText:string;
- }
-
-
- export default class ReactTable extends React.Component<IHelloWorldProps,IReactTableState> {
-
-
- constructor(props: IHelloWorldProps) {
- super(props);
- this.state = {
- tableHeaders:["Noun Name", "Current PN", "New Part", "Option#", "Supplier", "EJR"],
- rows:this.props.renderTable,
- rowLimit:20,
- addButtonText :"Add New Row",
- removeButtonText : "Delete Row"
- };
- }
-
-
-
- handleChange = (index) => evt => {
- try {
- var item = {
- id: evt.target.id,
- name: evt.target.name,
- value: evt.target.value
- };
- var rowsArray = this.state.rows;
- var newRow = rowsArray.map((row, i) => {
- for (var key in row) {
- if (key == item.name && row.id == item.id) {
- row[key] = item.value;
- }
- }
- return row;
- });
- this.setState({ rows: newRow });
- this.props.tableData(rowsArray);
- } catch (error) {
- console.log("Error in React Table handle change : " + error);
- }
- };
-
-
-
-
- handleAddRow = () => {
- try {
- var id = (+ new Date() + Math.floor(Math.random() * 999999)).toString(36);
- const tableColProps = {
- id: id,
- NounName: "",
- CurrentPN: "",
- NewPart: "",
- Option: "",
- Supplier: "",
- EJR: ""
- }
- if (this.state.rows.length < this.state.rowLimit) {
- this.state.rows.push(tableColProps);
- this.setState(this.state.rows);
- } else {
- alert('Add row limit exceeds');
- }
- } catch (error) {
- console.log("Error in React Table handle Add Row : " + error)
- }
- };
-
-
-
-
- handleRemoveRow = () => {
- try {
- var rowsArray = this.state.rows;
- if (rowsArray.length > 1) {
- var newRow = rowsArray.slice(0, -1);
- this.setState({ rows: newRow });
- }
- this.props.tableData(newRow);
- } catch (error) {
- console.log("Error in React Table handle Remove Row : " + error);
- }
- };
-
-
-
-
- handleRemoveSpecificRow = (idx) => () => {
- try {
- const rows = [this.state.rows]
- if(rows.length > 1){
- rows.splice(idx, 1);
- }
-
- this.setState({ rows });
- } catch (error) {
- console.log("Error in React Table handle Remove Specific Row : " + error);
- }
- }
-
-
-
- componentWillReceiveProps(nextProps) {
- try{
- if (nextProps.renderTable.length > 0) {
- this.setState({ rows: nextProps.renderTable });
- }else{
- this.setState({ rows: tableColProps });
- }
-
- }catch(error){
-
- console.log("Error in React Table component will receive props : " + error);
-
- }
- }
-
- render() {
-
- let list = this.state.rows.map((item,idx) =>{
- return (
- <tr key={idx}>
- <td>
- <input
- type="text"
- name="NounName"
- value={this.state.rows[idx].NounName}
- onChange={this.handleChange(idx)}
- id={this.state.rows[idx].id}
- />
- </td>
-
- <td>
- <input
- type="text"
- name="CurrentPN"
- value={this.state.rows[idx].CurrentPN}
- onChange={this.handleChange(idx)}
- id={this.state.rows[idx].id}
- />
- </td>
-
- <td>
- <input
- type="text"
- name="NewPart"
- value={this.state.rows[idx].NewPart}
- onChange={this.handleChange(idx)}
- id={this.state.rows[idx].id}
- />
- </td>
-
- <td>
- <input
- type="text"
- name="Option"
- value={this.state.rows[idx].Option}
- onChange={this.handleChange(idx)}
- id={this.state.rows[idx].id}
- />
- </td>
-
- <td>
- <input
- type="text"
- name="Supplier"
- value={this.state.rows[idx].Supplier}
- onChange={this.handleChange(idx)}
- id={this.state.rows[idx].id}
- />
- </td>
-
- <td>
- <input
- type="text"
- name="EJR"
- value={this.state.rows[idx].EJR}
- onChange={this.handleChange(idx)}
- id={this.state.rows[idx].id}
- />
- </td>
- </tr>
- );
-
- });
-
- return (
- <div className="container mainDynTable">
- <table id="dynVPITable">
- <thead>
- <tr>
- {
-
- this.state.tableHeaders.map(function (headerText) {
-
- return <th> {headerText} </th>
-
- })
-
- }
-
- </tr>
-
- </thead>
-
- <tbody>
-
- {list}
-
- </tbody>
-
- </table>
-
- <div className="main-width add-remove-icons">
-
- <span id="add-row" onClick={this.handleAddRow}
-
- className="document-icons-area">
-
- <Icon iconName="CalculatorAddition" className="ms-IconExample" /></span>
-
- <span id="delete-row" onClick={this.handleRemoveRow}
-
- className="document-icons-area">
-
- <Icon iconName="CalculatorSubtract" className="ms-IconExample" /></span>
-
- </div>
-
- </div>
-
- );
-
- }
- }
Integrate "ReactTable.tsx" child component with your "Parent" webpart.
Declare your state variable as shown below.
- export interface ISpFxRichTextEditorState {
- ReactTableResult:any;
- }
Declare the tableData property in your props.ts file, as shown below.
- export interface ISpFxRichTextEditorProps {
- tableData?:any;
- renderTable?:any;
- }
Import ReactTable.
- import * as React from 'react';
- import ReactTable from './ReactTable/ReactTable';
Declare your state variable in your parent component.
- constructor(props: ISpFxRichTextEditorProps, state: ISpFxRichTextEditorState) {
- super(props);
- this.state = {
- ReactTableResult:[]
- };
- }
Handle callback event to capture events from child to parent callback and store in your state variable as shown below
- handleTableData = (tableRowColl) => {
- this.setState({ ReactTableResult: tableRowColl });
- }
Add your component in your Render function as shown below.
- <ReactTable tableData={this.handleTableData} renderTable={this.state.ReactTableResult} ></ReactTable>
Note
The values will be stored as JSON array. You can store the values in your SharePoint list (Multiple lines of text (plain text)) and retrieve the same.
Install SpFx helper node module and Import SPFXHelper as shown below
- import {SPListOperations} from 'spfxhelper';
Define SPListOperations.
-
- private get oListOperation(): SPListOperations {
- return SPListOperations.getInstance(this.state.HTTPClient as SPHttpClient, this.state.SPSiteURL);
- }
Add React Table values to SharePoint List.
-
- private AddItems(workordertype: string): void {
- try {
- var dynamicTableData='';
- var workOrderList='Your List Name';
- var resTable = this.ValidateDynamicTable(this.state.ReactTableResult);
- if(resTable.length > 0){
- dynamicTableData = JSON.stringify(resTable);
- }
-
- const bodyforadding: string = JSON.stringify({
- __metadata: { 'type': "SP.Data.SharePointListItem" },
- TableContent: dynamicTableData
- });
-
-
- this.AddListItems(this.props.ctx.spHttpClient, this.props.siteUrl, workOrderList, bodyforadding);
-
- } catch (error) {
- console.log("Error in addItems : " + error);
- }
- }
-
-
- public ValidateDynamicTable(vpiReactTableResult){
- try {
- var stateReactArr;
- if (vpiReactTableResult == "" || vpiReactTableResult == null) {
- stateReactArr = [];
- } else {
- if (vpiReactTableResult.length > 0) {
- stateReactArr = vpiReactTableResult;
- } else {
- stateReactArr = [];
- }
- }
- return stateReactArr;
- } catch (error) {
- console.log("Error in ValidateDynamicTable : " + error);
- }
- }
-
-
- public AddListItems(spHttpClientVPI,siteURL,sharepointList,body):void{
- try{
- this.spHttpClient.post(this.siteUrl + "/_api/web/lists/getbytitle('" + sharepointList + "')/items",
- SPHttpClient.configurations.v1,
- {
- headers: {
- 'Accept': 'application/json;odata=verbose',
- 'Content-type': 'application/json;odata=verbose',
- 'odata-version': ''
- },
- body: body
- }).then(
- (): void => {
- alert("Item Added Successfully!!!");
- }
- );
- }catch(error){
- console.log("Error in AddListItems : " + error);
- }
- }
Retrieve React Table values from SharePoint List.
-
- private async GetItem(itemID: string) {
- try {
- var workOrderList = '<your sharepoint list name>';
- var redirectionEmailURL = this.props.siteUrl + "/_api/web/lists/getbytitle('" + workOrderList + "')/Items?$filter=Id eq '" + itemID + ";
- const responseEmail = await this.props.ctx.spHttpClient.get(redirectionEmailURL, SPHttpClient.configurations.v1);
- const responseEmailJSON = await responseEmail.json();
-
- this.oListOperation.getListItemByID(workOrderList, itemID)
- .then(async value => {
- var responseData = $.parseJSON(value.responseJSON);
- var responseJSON = responseData.result;
- this.setState({ ReactTableResult: responseJSON.TableContent == "" ? "" : JSON.parse(responseJSON.TableContent) });
- });
- } catch (error) {
- console.log("Error in GetItem : " + error);
- }
- }
CSS
-
- #dynVPITable {
- border-collapse: collapse;
- width: 100%;
- margin-bottom: 10px;
- }
-
- #dynVPITable td, #dynVPITable th {
- border: 1px solid #ddd;
- padding: 8px;
- }
-
- #dynVPITable th {
- padding-top: 12px;
- padding-bottom: 12px;
- background-color: #f1f1f1;
-
- }
-
- #dynVPITable input {
- border: 1px solid #596259;
- width: 100%;
- height: 32px;
- }
-
- .mainDynTable{
- width:100%;
- }
-
- .divDynamicTable{
- width:100%;
- }
Button CSS
-
-
- .add-remove-icons .document-icons-area {
- background: #f1f1f1;
- border: 1px solid #ddd;
- padding: 7px;
- border-radius: 5px;
- cursor: pointer;
- color: #000;
- }
- .add-remove-icons {
- float: right;
- width: auto;
- }
Output
Please feel free to share your comments.
Hope this helps !!!!!