前回は、GraphQLで接続したデータ一覧から、詳細画面へ遷移させました。

今回は、フロントエンドでデータを追加します。

コードは、こちらです。

フロントエンド:

バックエンド:

読書リスト画面で、新規作成ボタンをクリックすると、本のデータを作成できるようにします。

まずは、新規作成ボタンから出てくるモーダルを作ります。

componentsフォルダに、ModalBook.tsxを作成します。

モーダルの内容は、MUIのmodalを参考にします。

https://mui.com/components/modal/

import * as React from "react";
import Button from "@mui/material/Button";
import Modal from "@mui/material/Modal";

export default function BasicModal() {
  const [open, setOpen] = React.useState(false);
  const handleOpen = () => setOpen(true);
  const handleClose = () => setOpen(false);

  return (
    <div>
      <Button onClick={handleOpen} variant="contained">
        新規作成
      </Button>
      <Modal
        open={open}
        onClose={handleClose}
        aria-labelledby="modal-modal-title"
        aria-describedby="modal-modal-description"
      >
      </Modal>
    </div>
  );
}

Modalの中に、フォームを作成します。

componentsフォルファに、AddBook.tsxを作成します。

GraphQLに接続するのが目的なので、MUIの内容は割愛して、今後ご紹介します。

import * as React from "react";
import Box from "@mui/material/Box";
import TextField from "@mui/material/TextField";
import FormControlLabel from "@mui/material/FormControlLabel";
import { 
  Button, 
  FormControl, 
  FormLabel, 
  MenuItem, 
  Paper, 
  Radio, 
  RadioGroup, 
  styled 
} from "@mui/material";

const categories = [
  {
    value: 1,
    name: "文学",
  },
  {
    value: 2,
    name: "自己啓発",
  },
];

export default function AddBook() {
  const [title, setTitle] = React.useState("");
  const [author, setAuthor] = React.useState("");
  const [category, setCategory] = React.useState("");
  const [isRead, setIsRead] = React.useState(false);

  const handleChangeTitle = (event: React.ChangeEvent<HTMLInputElement>) => {
    setTitle(event.target.value as string);
  };

  const handleChangeAuthor = (event: React.ChangeEvent<HTMLInputElement>) => {
    setAuthor(event.target.value as string);
  };
  
  const handleChangeCategory = (event: React.ChangeEvent<HTMLInputElement>) => {
    setCategory(event.target.value as string);
  };

  const handleChangeIsRead = (event: React.ChangeEvent<HTMLInputElement>) => {
    const value = event.target.value === "true";
    setIsRead(value);
  };

  const handleClick = () => {console.log(title,author,category,isRead)};

  return (
    <Box
      component="form"
      sx={{
        "& > :not(style)": { m: 1, width: "200ch" },
      }}
      noValidate
      autoComplete="off"
    >
      <CContainer>
        <CPaper>
          <TextField
            id="title"
            label="タイトル"
            variant="outlined"
            value={title}
            onChange={handleChangeTitle}
            margin="normal"
            fullWidth
          />
          <TextField
            id="author"
            label="著者"
            variant="outlined"
            value={author}
            onChange={handleChangeAuthor}
            margin="normal"
            fullWidth
          />
          <TextField
            id="select-Category"
            select
            label="カテゴリ"
            variant="outlined"
            value={category}
            onChange={handleChangeCategory}
            margin="normal"
            fullWidth
          >
            {categories.map((category) => (
              <MenuItem key={category.value} value={category.value}>
                {category.name}
              </MenuItem>
            ))}
          </TextField>
          <Box>
            <FormControl component="fieldset">
              <FormLabel component="legend">読了</FormLabel>
              <RadioGroup
                aria-label="gender"
                name="controlled-radio-buttons-group"
                value={isRead}
                onChange={handleChangeIsRead}
                row
              >
                <FormControlLabel
                  value="true"                
                                    control={<Radio />}
                  label="済"
                />
                <FormControlLabel value="false" control={<Radio />} label="未" />
              </RadioGroup>
            </FormControl>
          </Box>
          <Button variant="contained" onClick={handleClick}>
            新規追加
          </Button>
        </CPaper>
      </CContainer>
    </Box>
  );
}

const CContainer = styled(Box)({
  display: "flex",
  flexDirection: "column",
  jusitifyConteng: "center",
  alignItems: "center",
  position: "absolute",
  top: "50%",
  left: "50%",
  transform: "translateX(-50%) translateY(-50%)",
});

const CPaper = styled(Paper)({
  padding: "2rem",
});

AddBook.tsxをModalBookにインポートします。

import * as React from "react";
import Button from "@mui/material/Button";
import Modal from "@mui/material/Modal";
import AddBook from "./AddBook";

export default function BasicModal() {
  const [open, setOpen] = React.useState(false);
  const handleOpen = () => setOpen(true);
  const handleClose = () => setOpen(false);

  return (
    <div>
      <Button onClick={handleOpen} variant="contained">
        新規作成
      </Button>
      <Modal
        open={open}
        onClose={handleClose}
        aria-labelledby="modal-modal-title"
        aria-describedby="modal-modal-description"
      >
        <AddBook />
      </Modal>
    </div>
  );
}

モーダルをBook.tsxにインポートします。

import * as React from "react";
import { gql, useQuery } from "@apollo/client";

import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import Title from "../components/Title";
import { Box, Button, Link, styled } from "@mui/material";
import ModalBook from "../components/ModalBook";

type TypeBook = {
  id: number;
  title: string;
  author: string;
  category: {
    name: string;
  };
  isRead: boolean;
  createdAt: string;
};

const booksData = gql`
  query allBooks {
    books {
      id
      title
      author
      category {
        name
      }
      isRead
      createdAt
    }
  }
`; 

export default function Book() {
  const { data, error, loading } = useQuery(booksData);

  if (error) return <div>データを取得することができませんでした</div>;

  if (loading) return <div>Loading...</div>;

  const {books} = data;

  return (
    <React.Fragment>
      <CBox>
        <Title>読書リスト</Title>
        <ModalBook />
      </CBox>
      <Table size="small">
        <TableHead>
          <TableRow>
            <TableCell>ID</TableCell>
            <TableCell>タイトル</TableCell>
            <TableCell>著者</TableCell>
            <TableCell>カテゴリ</TableCell>
            <TableCell>読了</TableCell>
            <TableCell>作成日</TableCell>
            <TableCell></TableCell>
          </TableRow>
        </TableHead>
        <TableBody>
          {books.map((book: TypeBook) => (
            <TableRow key={book.id}>
              <TableCell>{book.id}</TableCell>
              <TableCell>{book.title}</TableCell>
              <TableCell>{book.author}</TableCell>
              <TableCell>{book.category.name}</TableCell>
              {book.isRead ? (
                <TableCell>済</TableCell>
              ) : (
                <TableCell>未</TableCell>
              )}
              <TableCell>
                {`${new Date(Number(book.createdAt))}`
                  .split(" ")
                  .splice(1, 3)
                  .join(" ")}
              </TableCell>
              <TableCell>
                <Link href={`/book/${book.id}`} underline="hover">
                  詳細画面
                </Link>
              </TableCell>
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </React.Fragment>
  );
}

const CBox = styled(Box)({
  display: "flex",
  justifyContent: "space-between",
});

ブラウザで確認すると、

image2

image3

モーダルが完成しました。

今のところ、『新規追加』ボタンをクリックすると、console.logでGraphQLに送信したい内容が表示されます。

image4

image5

では、GraphQLに接続して、データを追加できるようにします。

まずは、カテゴリをGraphQLに接続しましょう。

CreateBook.tsxにgqlをインポートします。

import { gql } from "@apollo/client";

GraphQLを開いて、バックエンドで作成したCategoriesをコピーします。

query Categories {
  categories {
    id
    name
  }
}

コピーしたCategoriesgql内に貼り付けます。

const categoriesData = gql`
  query Categories {
    categories {
      id
      name
    }
  }
`;

useQueryをインポートします。

import { gql, useQuery } from "@apollo/client";

useQueryで、data、error、loadingを作成します

const { data, error, loading } = useQuery(categoriesData);

if (error) return <div>データを取得することができませんでした</div>;

if (loading) return <div>Loading...</div>;

const { categories } = data;

カテゴリのフィールドを作成しましょう。

型を作成します。

また、ローカルデータのcategoriesは必要無いので、削除します。

type TypeCategory = {
  id: number;
  name: string;
};
const [categoryId, setCategoryId] = React.useState(0);const handleChangeCategory = (event: React.ChangeEvent<HTMLInputElement>) => {
    setCategoryId(event.target.value as unknown as number);
  };
const handleChangeCategory = (event: React.ChangeEvent<HTMLInputElement>) => {
  setCategoryId(event.target.value as unknown as number);
};
<TextField
  id="select-Category"
  select
  label="カテゴリ"
  variant="outlined"
  value={categoryId}
  onChange={handleChangeCategory}
  margin="normal"
  fullWidth
>
  {categories.map((category: TypeCategory) => (
    <MenuItem key={category.id} value={category.id}>
      {category.name}
    </MenuItem>
  ))}
</TextField>

一度、ブラウザで確認します。

image6

カテゴリが問題なく表示されています。

次は、mutationを作成します。

GraphQLを開いて、バックエンドで作成したAddBookmutationをコピーします。

mutation AddBook($input: AddBookInput!) {
  addBook(input: $input) {
    errors {
      message 
    }
    book {
      title
      author
      category {
        name
      }
      isRead
    }
  }
}

コピーしたAddBookをgql内に貼り付けます。

const addBook = gql`
  mutation AddBook($input: AddBookInput!) {
    addBook(input: $input) {
      errors {
        message
      }
      book {
        title
        author
        category {
          name
        }
        isRead
      }
    }
  }
`;

useMutationをインポートします。

import { gql, useQuery, useMutation } from "@apollo/client";

データを作成するためのaddBookDataを設定します。

また、dataloadingも設定します。

const [ addBookData ] = useMutation(addBook);

handleClick内に、addBookDataを設定します。

追加したい項目は、title、author、categoryId、isReadなので、variablesに4項目を指定します。

const handleClick = () => {
  addBookData({
    variables: {
            input: {
          title,
          author,
          categoryId,
          isRead,
            }
    },
  });
};

一通り完成したので、ブラウザで確認します。

image7

フォーム内にデータを入力して、『新規追加』ボタンをクリックした後、リロードします。

image8

追加したデータが表示されました。

image9

GraphQLにも反映されています。

コードの全文は、こちらです。

次回は、GraphQLのデータを削除・更新します。

ブログ一覧