4

I am working on a project, where I basically do crud using material-table interface. I am wondering is there a way if I can make fields required if I want too?

I tried researching but not much results. Please see the code below which is straight forward from https://material-ui.com/components/tables/ last example. Of course I have modified on my codebase for my personal use and everything works fine, but I am not sure how is it possible to make fields required if I want too? If yes, how would I do it? Would I pass something as a prop option on MaterialTable ?

Thank you for any suggestions.

import React from 'react';
import MaterialTable from 'material-table';

export default function MaterialTableDemo() {
  const [state, setState] = React.useState({
    columns: [
      { title: 'Name', field: 'name' },
      { title: 'Surname', field: 'surname' },
      { title: 'Birth Year', field: 'birthYear', type: 'numeric' },
      {
        title: 'Birth Place',
        field: 'birthCity',
        lookup: { 34: 'İstanbul', 63: 'Şanlıurfa' },
      },
    ],
    data: [
      { name: 'Mehmet', surname: 'Baran', birthYear: 1987, birthCity: 63 },
      {
        name: 'Zerya Betül',
        surname: 'Baran',
        birthYear: 2017,
        birthCity: 34,
      },
    ],
  });

  return (
    <MaterialTable
      title="Editable Example"
      columns={state.columns}
      data={state.data}
      editable={{
        onRowAdd: newData =>
          new Promise(resolve => {
            setTimeout(() => {
              resolve();
              const data = [...state.data];
              data.push(newData);
              setState({ ...state, data });
            }, 600);
          }),
        onRowUpdate: (newData, oldData) =>
          new Promise(resolve => {
            setTimeout(() => {
              resolve();
              const data = [...state.data];
              data[data.indexOf(oldData)] = newData;
              setState({ ...state, data });
            }, 600);
          }),
        onRowDelete: oldData =>
          new Promise(resolve => {
            setTimeout(() => {
              resolve();
              const data = [...state.data];
              data.splice(data.indexOf(oldData), 1);
              setState({ ...state, data });
            }, 600);
          }),
      }}
    />
  );
}
comsci_dude
  • 194
  • 2
  • 13
  • 2
    The [documentation](https://material-table.com/#/docs/features/editable) has an example of a custom editable component, showing a simple `input`, to which you could add [the `required` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-required). – Heretic Monkey Sep 17 '19 at 20:01
  • @HereticMonkey I looked before, and even now, I don't see the example for making inputs/fields required, only for readonly but not making editable, maybe I am missing something. Thank you. – comsci_dude Sep 17 '19 at 20:13
  • 5
    As I mentioned, use the one for a Custom Edit Component. The last one at the bottom of the screen. You see that? Look at the code. It has a property, `editComponent` that shows the use of an `input` element. it has `type="text"`. To that, you can add `required` as an attribute (as the link I provided shows). The `required` attribute is *not part of the example*. I'm telling you to add it your self. This is what programming is all about. Taking examples and going further with them. – Heretic Monkey Sep 18 '19 at 00:19
  • @HereticMonkey I completely agree about taking examples and going further with them. Thank you for being thorough. – comsci_dude Sep 18 '19 at 13:06

4 Answers4

9

Material-table has native support for validation which can trivially be used to make a field required. All you have to do is specify the validation field in the columns specification as per docs here: https://material-table.com/#/docs/features/validation.

Here's what that would look like for your code, say if you wanted to make the "Surname" required:

...
  const [state, setState] = React.useState({
    columns: [
      { title: 'Name', field: 'name' },
      {
          title: 'Surname',
          field: 'surname',
          validate: rowData => Boolean(rowData.surname),
      },
      { title: 'Birth Year', field: 'birthYear', type: 'numeric' },
      {
        title: 'Birth Place',
        field: 'birthCity',
        lookup: { 34: 'İstanbul', 63: 'Şanlıurfa' },
      },
    ],
    data: [
      { name: 'Mehmet', surname: 'Baran', birthYear: 1987, birthCity: 63 },
      {
        name: 'Zerya Betül',
        surname: 'Baran',
        birthYear: 2017,
        birthCity: 34,
      },
    ],
  });
...

p.s. there's no need to put your columns data in the state here unless it's going to change, which seems unlikely in this case.

drewsberry
  • 534
  • 5
  • 8
3

@HereticMonkey's comment essentially solves my question.

Making fields required is done through editable components as example shown by Heretic Monkey ^^.

Thank you

comsci_dude
  • 194
  • 2
  • 13
3

You need to use editComponent,TextField and validation handling on onRowAdd and onRowUpdate.

See below sample revise code.

import React from "react";
import MaterialTable from "material-table";
import TextField from "@material-ui/core/TextField";

export default function App() {

const [nameError, setNameError] = React.useState({
    error: false,
    label: "",
    helperText: "",
    validateInput: false,
});

const columnsHeader = [
    {
        title: "Name",
        field: "name",
        editComponent: (props) => (
            <TextField
                type="text"
                error={
                    !props.value &&
                    nameError.validateInput &&
                    props.rowData.submitted
                        ? nameError.error
                        : false
                }
                helperText={
                    !props.value &&
                    nameError.validateInput &&
                    props.rowData.submitted
                        ? nameError.helperText
                        : ""
                }
                value={props.value ? props.value : ""}
                onChange={(e) => {
                    if (nameError.validateInput) {
                        setNameError({
                            ...nameError,
                            validateInput: false,
                        });
                    }

                    props.onChange(e.target.value);
                }}
            />
        ),
    },
    { title: "Surname", field: "surname" },
    { title: "Birth Year", field: "birthYear", type: "numeric" },
    {
        title: "Birth Place",
        field: "birthCity",
        lookup: { 34: "İstanbul", 63: "Şanlıurfa" },
    },
    { title: "submitted", field: "submitted", hidden: true },
];

const [state, setState] = React.useState({
    data: [
        {
            name: "Mehmet",
            surname: "Baran",
            birthYear: 1987,
            birthCity: 63,
            submitted: false,
        },
        {
            name: "Zerya Betül",
            surname: "Baran",
            birthYear: 2017,
            birthCity: 34,
            submitted: false,
        },
    ],
});

return (
    <MaterialTable
        title="Editable Example"
        columns={columnsHeader}
        data={state.data}
        editable={{
            onRowAdd: (newData) =>
                new Promise((resolve, reject) => {
                    setTimeout(() => {
                        newData.submitted = true;
                        if (!newData.name) {
                            setNameError({
                                error: true,
                                label: "required",
                                helperText: "Name is required.",
                                validateInput: true,
                            });
                            reject();
                            return;
                        }
                        resolve();

                        const data = [...state.data];
                        data.push(newData);
                        setState({ ...state, data });
                    }, 600);
                }),
            onRowUpdate: (newData, oldData) =>
                new Promise((resolve, reject) => {
                    setTimeout(() => {
                        newData.submitted = true;
                        if (!newData.name) {
                            setNameError({
                                error: true,
                                label: "required",
                                helperText: "Name is required.",
                                validateInput: true,
                            });
                            reject();
                            return;
                        }
                        resolve();
                        const data = [...state.data];
                        data[data.indexOf(oldData)] = newData;
                        setState({ ...state, data });
                    }, 600);
                }),
            onRowDelete: (oldData) =>
                new Promise((resolve) => {
                    setTimeout(() => {
                        resolve();
                        const data = [...state.data];
                        data.splice(data.indexOf(oldData), 1);
                        setState({ ...state, data });
                    }, 600);
                }),
        }}
    />
);

}

Dave
  • 31
  • 1
1

just validate and use Reject() like this ( calling reject() keeps open row editable ):

 onRowAdd: (newData) =>
        new Promise((resolve) => {
          if(---!validate(newData)---) {
            // alert('required');
            reject();
          }else{ /*addRow*/ }
sina falihi
  • 21
  • 1
  • 2