Markdwonメモアプリ作成(TypeScript)(第四回)

タグの登録と表示

今回はマークダウンの編集ページを作成します。

前回の内容は以下を参照してください。

Markdwonメモアプリ作成(TypeScript)(第一回)
Markdwonメモアプリ作成(TypeScript)(第二回)
Markdwonメモアプリ作成(TypeScript)(第三回)

今回の手順は以下のとおりです。

FlaskのAPI改修

過去に作成した内容とほぼ同じです。登録と更新のAPIを追加しています。

    
@app.route('/markdown', methods=["POST"])
def post_markdown():
    url = request.json["url"]
    title = request.json["title"]
    body = request.json["body"]

    conn = db_connection()
    with conn.cursor() as cur:
        cur.execute(
            f"INSERT INTO markdown (url, title, body) values('{url}','{title}','{body}')")
        conn.commit()

    return RESULT_CODE_SUCESS


@app.route('/markdown<path:path>', methods=["PUT"])
def put_markdown(path):
    title = request.json["title"]
    body = request.json["body"]

    conn = db_connection()
    with conn.cursor() as cur:
        cur.execute(
            f"UPDATE markdown SET title = '{title}', body = '{body}' WHERE url = '{path}' ")
        conn.commit()

    return RESULT_CODE_SUCESS

    
  

post_markdown()とput_markdownの関数を追加します。

マークダウンページの更新

マークダウンページ(Markdown.tsx)のファイルを更新します。

  1. import { FC, useEffect, useState } from "react";
  2. import { useLocation } from "react-router-dom";
  3. import axios from "axios";
  4. import { MarkdownType } from "../../types/MarkdownType.type";
  5. import { EditTemplate } from "../templates/EditTemplate";
  6. import { ViewTemplate } from "../templates/ViewTemplate";
  7. export const Markdown: FC = () => {
  8. const [editMode, setEditMode] = useState<boolean>(false);
  9. const [markdown, setMarkdown] = useState<MarkdownType>({ url: '', title: '', body: '' });
  10. const [insertMode, setInsertMode] = useState<boolean>(false);
  11. const location = useLocation();
  12. useEffect(() => {
  13. axios.get<MarkdownType>("http://localhost:5000/markdown" + location.pathname)
  14. .then(res => {
  15. if (!Object.keys(res.data).length) {
  16. setMarkdown({ url: decodeURI(location.pathname), title: '', body: '' });
  17. setEditMode(true);
  18. setInsertMode(true);
  19. } else {
  20. setMarkdown(res.data);
  21. }
  22. })
  23. }, [])
  24. const onClickSwitchMode = () => { setEditMode(!editMode) }
  25. return (
  26. <>
  27. {editMode ?
  28. <EditTemplate markdown={markdown} setMarkdown={setMarkdown} editMode={editMode} insertMode={insertMode} setInsertMode={setInsertMode} onClickSwitchMode={onClickSwitchMode} /> :
  29. <ViewTemplate markdown={markdown} setMarkdown={setMarkdown} editMode={editMode} insertMode={insertMode} setInsertMode={setInsertMode} onClickSwitchMode={onClickSwitchMode} />
  30. }
  31. </>
  32. );
  33. };

EditTemplateのコンポーネントの記述を修正します。

ViewTemplateと同じようにパラメータを渡します。

新規登録用にinsertModeを追加します。

編集テンプレートの更新

編集テンプレート(EditTemplate.tsx)ファイルを更新します。

  1. import { ThemeProvider } from "@emotion/react";
  2. import { Container } from "@mui/material";
  3. import { Box } from "@mui/system";
  4. import { FC, useEffect } from "react";
  5. import ReactMarkdown from "react-markdown";
  6. import remarkGfm from "remark-gfm";
  7. import { MarkdownType } from "../../types/MarkdownType.type";
  8. import { MarkdownEditTextFiled } from "../uilibs/atoms/MarkdownEditTextFiled";
  9. import { HeaderNavigation } from "../uilibs/organisms/HeaderNavigation";
  10. import { SubHeaderNavigation } from "../uilibs/organisms/SubHeaderNavigation";
  11. import { Theme } from "../uilibs/theme/Theme";
  12. interface Props {
  13. markdown: MarkdownType;
  14. setMarkdown: React.Dispatch<React.SetStateAction<MarkdownType>>;
  15. editMode: boolean;
  16. insertMode: boolean;
  17. setInsertMode: React.Dispatch<React.SetStateAction<boolean>>;
  18. onClickSwitchMode: (event: React.MouseEvent<HTMLButtonElement>) => void;
  19. };
  20. export const EditTemplate: FC<Props> = (props) => {
  21. const { markdown, setMarkdown, editMode, insertMode, setInsertMode, onClickSwitchMode } = props;
  22. const StyleTemplateBox = { display: "flex", flexDirection: "column", height: "100vh" };
  23. const StyleMainBox = { display: "flex", flexDirection: "row", height: "100vh", width: '100%' };
  24. const StyleLeftBox = { height: "100vh", width: '50%', backgroundColor: "#121212" };
  25. const StyleRightBox = { height: "100vh", width: '50%' };
  26. const onChangeValue = (event: React.ChangeEvent<HTMLTextAreaElement>) => { setMarkdown({ url: markdown.url, title: markdown.title, body: event.target.value }); };
  27. useEffect(() => {
  28. }, [markdown.body]);
  29. return (
  30. <ThemeProvider theme={Theme}>
  31. <Box sx={StyleTemplateBox}>
  32. <HeaderNavigation />
  33. <SubHeaderNavigation markdown={markdown} setMarkdown={setMarkdown} editMode={editMode} insertMode={insertMode} setInsertMode={setInsertMode} onClickSwitchMode={onClickSwitchMode}/>
  34. <Box sx={StyleMainBox}>
  35. <Box sx={StyleLeftBox}>
  36. <Container >
  37. <MarkdownEditTextFiled markdown={markdown} onChangeValue={onChangeValue} />
  38. </Container >
  39. </Box>
  40. <Box sx={StyleRightBox}>
  41. <Container className="markdown-view" >
  42. <ReactMarkdown remarkPlugins={[remarkGfm]}>{markdown.body}</ReactMarkdown>
  43. </Container >
  44. </Box>
  45. </Box>
  46. </Box>
  47. </ThemeProvider>
  48. );
  49. };

サブヘッダーナビゲーション部品の作成

サブヘッダーナビゲーション部品(SubHeaderNavigation.tsx)ファイルを更新します。

  1. import React, { FC } from "react";
  2. import axios from "axios";
  3. import {
  4. Box,
  5. Toolbar,
  6. } from "@mui/material";
  7. import { MarkdownType } from "../../../types/MarkdownType.type";
  8. import { SwitchTextFieldTypography } from "../molecules/SwitchTextFieldTypography";
  9. import { SwitchModeButtonNavi } from "../molecules/SwitchModeNavi";
  10. import { SwitchInsertUpdateNavi } from "../molecules/SwitchInsertUpdateNavi";
  11. interface Props {
  12. markdown: MarkdownType;
  13. setMarkdown: React.Dispatch>React.SetStateAction>MarkdownType<<;
  14. editMode: boolean;
  15. insertMode: boolean;
  16. setInsertMode: React.Dispatch>React.SetStateAction>boolean<<;
  17. onClickSwitchMode: (event: React.MouseEvent>HTMLButtonElement<) =< void;
  18. };
  19. export const SubHeaderNavigation: FC>Props< = (props) =< {
  20. const { markdown, setMarkdown, editMode, insertMode, setInsertMode, onClickSwitchMode } = props;
  21. const onChangeURL = (event: React.ChangeEvent>HTMLTextAreaElement<) =< { setMarkdown({ url: event.target.value, title: markdown.title, body: markdown.body }) };
  22. const onChangeTitle = (event: React.ChangeEvent>HTMLTextAreaElement<) =< { setMarkdown({ url: markdown.url, title: event.target.value, body: markdown.body }) };
  23. const onClickSaveMarkdown = () =< {
  24. axios.post(`http://localhost:5000/markdown`, markdown)
  25. .then(res =< {
  26. console.log(res.data);
  27. setInsertMode(false);
  28. });
  29. };
  30. const onClickUpdateMarkdown = () =< {
  31. axios.put(`http://localhost:5000/markdown` + markdown.url, markdown)
  32. .then(res =< {
  33. console.log(res.data);
  34. });
  35. };
  36. const StyleToolBar = { backgroundColor: "#1976d2", display: "flex", justifyContent: "space-between" };
  37. const StyleRightBox = { display: "flex", flexDirection: "row" };
  38. return (
  39. >Box<
  40. >Toolbar sx={StyleToolBar}<
  41. >SwitchTextFieldTypography editMode={editMode} label='url' value={markdown.url} onChangeValue={onChangeURL} text={markdown.url} /<
  42. >SwitchTextFieldTypography editMode={editMode} label='title' value={markdown.title} onChangeValue={onChangeTitle} text={markdown.title} /<
  43. >Box sx={StyleRightBox}<
  44. >SwitchInsertUpdateNavi editMode={editMode} insertMode={insertMode} onClickSaveMarkdown={onClickSaveMarkdown} onClickUpdateMarkdown={onClickUpdateMarkdown} /<
  45. >SwitchModeButtonNavi editMode={editMode} onClickSwitchMode={onClickSwitchMode} /<
  46. >/Box<
  47. >/Toolbar<
  48. >/Box<
  49. );
  50. };

登録、更新用のAPIを呼び出しています。

登録と更新の切り替えアイコン部品作成

登録と更新の切り替えアイコン部品(SwitchInsertUpdateNavi.tsx)ファイルを更新します。

  1. import React, { FC } from "react";
  2. import { BottomNavigation, BottomNavigationAction } from "@mui/material";
  3. import SaveIcon from '@mui/icons-material/Save';
  4. interface Props {
  5. editMode: boolean;
  6. insertMode: boolean;
  7. onClickSaveMarkdown: (event: React.MouseEvent<HTMLButtonElement>) => void;
  8. onClickUpdateMarkdown: (event: React.MouseEvent<HTMLButtonElement>) => void;
  9. };
  10. export const SwitchInsertUpdateNavi: FC<Props> = (props) => {
  11. const { editMode, insertMode, onClickSaveMarkdown, onClickUpdateMarkdown } = props;
  12. return (
  13. <BottomNavigation showLabels sx={{ backgroundColor: "#1976d2" }}>
  14. {editMode ?
  15. insertMode ?
  16. <BottomNavigationAction label="新規登録" icon={<SaveIcon />} onClick={onClickSaveMarkdown} /> : <BottomNavigationAction label="更新" icon={<SaveIcon />} onClick={onClickUpdateMarkdown} /> :
  17. <></>
  18. }
  19. </BottomNavigation>
  20. );
  21. };

登録と編集を切り替えるモードを追加しています。

画面確認

docker-compose upを使って各コンテナを起動します。

docker-compose.ymlファイルがある場所で以下のコマンドを実行します。

  1. docker-compose up

起動したら以下のURLをブラウザに入力します。

http://localhost:3000

以下ブラウザの表示です。

markdownページの編集モードです。

markdown-homepage

まとめ

Markdwonメモアプリ作成(TypeScript)でマークダウンの編集ページを作成しました。

コメント

0 件のコメント:

コメントを投稿

コメントをお待ちしています。