Markdwonメモアプリ作成(TypeScript)(第四回)
8:04
今回はマークダウンの編集ページを作成します。
前回の内容は以下を参照してください。
今回の手順は以下のとおりです。
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)のファイルを更新します。
import { FC, useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
import axios from "axios";
import { MarkdownType } from "../../types/MarkdownType.type";
import { EditTemplate } from "../templates/EditTemplate";
import { ViewTemplate } from "../templates/ViewTemplate";
export const Markdown: FC = () => {
const [editMode, setEditMode] = useState<boolean>(false);
const [markdown, setMarkdown] = useState<MarkdownType>({ url: '', title: '', body: '' });
const [insertMode, setInsertMode] = useState<boolean>(false);
const location = useLocation();
useEffect(() => {
axios.get<MarkdownType>("http://localhost:5000/markdown" + location.pathname)
.then(res => {
if (!Object.keys(res.data).length) {
setMarkdown({ url: decodeURI(location.pathname), title: '', body: '' });
setEditMode(true);
setInsertMode(true);
} else {
setMarkdown(res.data);
}
})
}, [])
const onClickSwitchMode = () => { setEditMode(!editMode) }
return (
<>
{editMode ?
<EditTemplate markdown={markdown} setMarkdown={setMarkdown} editMode={editMode} insertMode={insertMode} setInsertMode={setInsertMode} onClickSwitchMode={onClickSwitchMode} /> :
<ViewTemplate markdown={markdown} setMarkdown={setMarkdown} editMode={editMode} insertMode={insertMode} setInsertMode={setInsertMode} onClickSwitchMode={onClickSwitchMode} />
}
</>
);
};
EditTemplateのコンポーネントの記述を修正します。
ViewTemplateと同じようにパラメータを渡します。
新規登録用にinsertModeを追加します。
編集テンプレートの更新
編集テンプレート(EditTemplate.tsx)ファイルを更新します。
import { ThemeProvider } from "@emotion/react";
import { Container } from "@mui/material";
import { Box } from "@mui/system";
import { FC, useEffect } from "react";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { MarkdownType } from "../../types/MarkdownType.type";
import { MarkdownEditTextFiled } from "../uilibs/atoms/MarkdownEditTextFiled";
import { HeaderNavigation } from "../uilibs/organisms/HeaderNavigation";
import { SubHeaderNavigation } from "../uilibs/organisms/SubHeaderNavigation";
import { Theme } from "../uilibs/theme/Theme";
interface Props {
markdown: MarkdownType;
setMarkdown: React.Dispatch<React.SetStateAction<MarkdownType>>;
editMode: boolean;
insertMode: boolean;
setInsertMode: React.Dispatch<React.SetStateAction<boolean>>;
onClickSwitchMode: (event: React.MouseEvent<HTMLButtonElement>) => void;
};
export const EditTemplate: FC<Props> = (props) => {
const { markdown, setMarkdown, editMode, insertMode, setInsertMode, onClickSwitchMode } = props;
const StyleTemplateBox = { display: "flex", flexDirection: "column", height: "100vh" };
const StyleMainBox = { display: "flex", flexDirection: "row", height: "100vh", width: '100%' };
const StyleLeftBox = { height: "100vh", width: '50%', backgroundColor: "#121212" };
const StyleRightBox = { height: "100vh", width: '50%' };
const onChangeValue = (event: React.ChangeEvent<HTMLTextAreaElement>) => { setMarkdown({ url: markdown.url, title: markdown.title, body: event.target.value }); };
useEffect(() => {
}, [markdown.body]);
return (
<ThemeProvider theme={Theme}>
<Box sx={StyleTemplateBox}>
<HeaderNavigation />
<SubHeaderNavigation markdown={markdown} setMarkdown={setMarkdown} editMode={editMode} insertMode={insertMode} setInsertMode={setInsertMode} onClickSwitchMode={onClickSwitchMode}/>
<Box sx={StyleMainBox}>
<Box sx={StyleLeftBox}>
<Container >
<MarkdownEditTextFiled markdown={markdown} onChangeValue={onChangeValue} />
</Container >
</Box>
<Box sx={StyleRightBox}>
<Container className="markdown-view" >
<ReactMarkdown remarkPlugins={[remarkGfm]}>{markdown.body}</ReactMarkdown>
</Container >
</Box>
</Box>
</Box>
</ThemeProvider>
);
};
サブヘッダーナビゲーション部品の作成
サブヘッダーナビゲーション部品(SubHeaderNavigation.tsx)ファイルを更新します。
import React, { FC } from "react";
import axios from "axios";
import {
Box,
Toolbar,
} from "@mui/material";
import { MarkdownType } from "../../../types/MarkdownType.type";
import { SwitchTextFieldTypography } from "../molecules/SwitchTextFieldTypography";
import { SwitchModeButtonNavi } from "../molecules/SwitchModeNavi";
import { SwitchInsertUpdateNavi } from "../molecules/SwitchInsertUpdateNavi";
interface Props {
markdown: MarkdownType;
setMarkdown: React.Dispatch>React.SetStateAction>MarkdownType<<;
editMode: boolean;
insertMode: boolean;
setInsertMode: React.Dispatch>React.SetStateAction>boolean<<;
onClickSwitchMode: (event: React.MouseEvent>HTMLButtonElement<) =< void;
};
export const SubHeaderNavigation: FC>Props< = (props) =< {
const { markdown, setMarkdown, editMode, insertMode, setInsertMode, onClickSwitchMode } = props;
const onChangeURL = (event: React.ChangeEvent>HTMLTextAreaElement<) =< { setMarkdown({ url: event.target.value, title: markdown.title, body: markdown.body }) };
const onChangeTitle = (event: React.ChangeEvent>HTMLTextAreaElement<) =< { setMarkdown({ url: markdown.url, title: event.target.value, body: markdown.body }) };
const onClickSaveMarkdown = () =< {
axios.post(`http://localhost:5000/markdown`, markdown)
.then(res =< {
console.log(res.data);
setInsertMode(false);
});
};
const onClickUpdateMarkdown = () =< {
axios.put(`http://localhost:5000/markdown` + markdown.url, markdown)
.then(res =< {
console.log(res.data);
});
};
const StyleToolBar = { backgroundColor: "#1976d2", display: "flex", justifyContent: "space-between" };
const StyleRightBox = { display: "flex", flexDirection: "row" };
return (
>Box<
>Toolbar sx={StyleToolBar}<
>SwitchTextFieldTypography editMode={editMode} label='url' value={markdown.url} onChangeValue={onChangeURL} text={markdown.url} /<
>SwitchTextFieldTypography editMode={editMode} label='title' value={markdown.title} onChangeValue={onChangeTitle} text={markdown.title} /<
>Box sx={StyleRightBox}<
>SwitchInsertUpdateNavi editMode={editMode} insertMode={insertMode} onClickSaveMarkdown={onClickSaveMarkdown} onClickUpdateMarkdown={onClickUpdateMarkdown} /<
>SwitchModeButtonNavi editMode={editMode} onClickSwitchMode={onClickSwitchMode} /<
>/Box<
>/Toolbar<
>/Box<
);
};
登録、更新用のAPIを呼び出しています。
登録と更新の切り替えアイコン部品作成
登録と更新の切り替えアイコン部品(SwitchInsertUpdateNavi.tsx)ファイルを更新します。
import React, { FC } from "react";
import { BottomNavigation, BottomNavigationAction } from "@mui/material";
import SaveIcon from '@mui/icons-material/Save';
interface Props {
editMode: boolean;
insertMode: boolean;
onClickSaveMarkdown: (event: React.MouseEvent<HTMLButtonElement>) => void;
onClickUpdateMarkdown: (event: React.MouseEvent<HTMLButtonElement>) => void;
};
export const SwitchInsertUpdateNavi: FC<Props> = (props) => {
const { editMode, insertMode, onClickSaveMarkdown, onClickUpdateMarkdown } = props;
return (
<BottomNavigation showLabels sx={{ backgroundColor: "#1976d2" }}>
{editMode ?
insertMode ?
<BottomNavigationAction label="新規登録" icon={<SaveIcon />} onClick={onClickSaveMarkdown} /> : <BottomNavigationAction label="更新" icon={<SaveIcon />} onClick={onClickUpdateMarkdown} /> :
<></>
}
</BottomNavigation>
);
};
登録と編集を切り替えるモードを追加しています。
画面確認
docker-compose upを使って各コンテナを起動します。
docker-compose.ymlファイルがある場所で以下のコマンドを実行します。
docker-compose up
起動したら以下のURLをブラウザに入力します。
http://localhost:3000
以下ブラウザの表示です。
markdownページの編集モードです。
まとめ
Markdwonメモアプリ作成(TypeScript)でマークダウンの編集ページを作成しました。
0 件のコメント:
コメントを投稿
コメントをお待ちしています。