Rustで簡単なCRUDをやってみる(フロントエンド編1)

プログラミング
この記事は約50分で読めます。

こんにちは!大垣です!
前回に引き続き、RustでCRUD処理を作って行きたいと思います。
今回はフロントエンド(UI)の基礎部分を作っていきます。

使用するクレート

・dioxus
・dioxus_router
・dioxus_web
・gloo
・gloo_events
・gloo_net
・serde
・serde_json
・web_sys
・wasm_bindgen
・wasm_bindgen_futures

プロジェクト作成

バックエンドと別プロジェクトとして作成する為、
rustsourceフォルダ内にfrontフォルダを作成し、
その中にプログラムを作成します。
コマンドプロンプトを開き、以下のコマンドを入力します。

docker ps
※rust-rust-appとなっている左のIDを確認する

docker exec -it ↑で確認したID bash
cargo new front

クレートの追加

プロジェクトに必要なクレートを追加するために、Cargo.tomlを編集します。
[dependencies]以降を以下の通りに記述します。

[dependencies]
dioxus = "0.3.2"
dioxus-router = "0.3.0"
dioxus-web = "0.3.1"
gloo = "0.8"
gloo-events = "0.1.2"
gloo-net = "0.2.6"
serde = { version = "1.0.152", features=["derive"] }
serde_json = "1.0.93"
web-sys = "0.3.61"
wasm-bindgen = "0.2.84"
wasm-bindgen-futures = "0.4.34"

ファイル構成

今回は、以下のファイル構成にします。

📁front
├📁src
│├📁components
││├📝error404.rs
││├📝mod.rs
││├📝task.rs
││└📝top.rs
│└📝main.rs
├📝.gitignore
├📝Cargo.toml
└📝index.html

index.htmlの作成

index.htmlを作成し、以下の内容を記述します。

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <link rel="stylesheet" href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" />
    </head>
    <body>
        <div id="main"></div>
    </body>
</html>

src/main.rsの編集

src/main.rsファイルを編集し、以下の内容で自動作成されるプログラムをすべて削除します。

fn main() {
  println!("Hello, world!");
}

削除後、以下の内容を記述します。

// コンポーネント読み込み
mod components;

// クレート宣言
use components::*;
use dioxus::prelude::*;
use dioxus_router::{Route, Router};
use dioxus_web::launch;

/// メイン処理
fn main() {
    // アプリ起動
    launch(app);
}

/// アプリコンポーネント
fn app(cx: Scope) -> Element {
    // レンダリング
    cx.render(rsx! {
        // ルーティング
        Router {
            Route { to: "/task", task::task_list {} },
            Route { to: "/", top::top {} },
            Route { to: "", error404::error404 {} },
        }
    })
}

src/components/mod.rsの作成

srcフォルダ内に、componentsフォルダを作成します。
作成したフォルダ内に、mod.rsファイルを作成し、以下の内容を記述します。

// 各ファイル読み込み
pub mod error404;
pub mod task;
pub mod top;

src/components/error404.rsの作成

src/components/error404.rsファイルを作成し、以下の内容を記述します。

// クレート宣言
use dioxus::prelude::*;

// 404エラーコンポーネント
pub fn error404(cx: Scope) -> Element {
    // レンダリング
    cx.render(rsx! {
        div {
            // コンポーネント全体のクラス
            class: "h-full",
            p {
                class: "justify-center w-full py-10 text-4xl",
                "404 Not Found!"
            }
        }
    })
}

src/components/top.rsの作成

src/components/top.rsファイルを作成し、以下の内容を記述します。

// クレート宣言
use dioxus::prelude::*;
use dioxus_router::Link;

/// トップページ
pub fn top(cx: Scope) -> Element {
    // レンダリング
    cx.render(rsx! {
        div {
            class: "h-full",
            div {
                class: "text-center bg-yellow-500 text-black text-4xl",
                "タスク管理システム"
            }
            div {
                class: "flex items-center justify-center w-full p-5",
                ul {
                    li {
                        class: "text-center py-8",
                        Link { 
                            class: "text-2xl inline-flex items-center px-3 py-2 font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300",
                            to: "/task", "タスク一覧"
                        }
                    }
                }
            }
        }
    })
}

src/components/task.rsの作成

src/components/task.rsファイルを作成し、以下の内容を記述します。

// クレート宣言
use dioxus::prelude::*;
use dioxus_router::Link;
use gloo_events::EventListener;
use gloo_net::http::Request;
use serde::{Deserialize, Serialize};
use wasm_bindgen::{JsCast, JsValue};
use wasm_bindgen_futures::*;
use web_sys::{window, HtmlInputElement, HtmlTextAreaElement};

/// タスク構造体
#[derive(Serialize, Deserialize)]
struct Task {
    taskid: String,
    userid: String,
    title: String,
    description: String,
}

// モード列挙型
enum Mode {
    Add,
    Update,
    Delete
}

/// タスク一覧
pub fn task_list(cx: Scope) -> Element {

    // APIのURL
    const API_URL: &str = "http://localhost:9000/task";

    // データロード
    fn load_task() {
        // 非同期処理
        spawn_local(async move {
            // 取得結果を格納
            let task_data = Request::get(API_URL)
                .send()
                .await
                .unwrap()
                .text()
                .await
                .unwrap();

            // 取得結果をJsonにパース
            let task_json: Vec<Task> = serde_json::from_str(&task_data).unwrap();

            // 出力先の要素を取得
            let document = window().unwrap().document().unwrap();
            let tbody = document.get_element_by_id("tasklist").unwrap();
            let remove_element = document.query_selector_all("#tasklist tr").unwrap();

            // 既存の行要素を削除
            for i in 0..remove_element.length() {
                let remove_item = remove_element.item(i);
                if let Some(..) = remove_item {
                    tbody.remove_child(&remove_item.unwrap()).unwrap();
                }
            }

            // 取得データ数分繰り返す
            for task in &task_json {
                // 各データを退避
                let task_id_del = task.taskid.clone();
                let user_id_del = task.userid.clone();
                let title_del = task.title.clone();
                let description_del = task.description.clone();
                let task_id_mod = task.taskid.clone();
                let user_id_mod = task.userid.clone();
                let title_mod = task.title.clone();
                let description_mod = task.description.clone();

                // 要素生成
                let tr = document.create_element("tr").unwrap();
                let taskid = document.create_element("td").unwrap();
                let userid = document.create_element("td").unwrap();
                let title = document.create_element("td").unwrap();
                let mod_column = document.create_element("td").unwrap();
                let del_column = document.create_element("td").unwrap();
                let mod_button = document.create_element("button").unwrap();
                let del_button = document.create_element("button").unwrap();

                // 値をテキストとして各要素にセット
                taskid.set_text_content(Some(&task.taskid));
                userid.set_text_content(Some(&task.userid));
                title.set_text_content(Some(&task.title));
                mod_button.set_text_content(Some("変更"));
                del_button.set_text_content(Some("削除"));

                // クラスをセット
                mod_button.set_class_name("text-2xl inline-flex items-center px-3 py-2 text-sm font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:ring-blue-300");
                del_button.set_class_name("text-2xl inline-flex items-center px-3 py-2 text-sm font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:ring-blue-300");

                // 行要素を組み立てる
                mod_column.append_child(&mod_button).unwrap();
                del_column.append_child(&del_button).unwrap();
                tr.append_child(&taskid).unwrap();
                tr.append_child(&userid).unwrap();
                tr.append_child(&title).unwrap();
                tr.append_child(&mod_column).unwrap();
                tr.append_child(&del_column).unwrap();

                // 変更ボタンイベント
                let on_click_mod_modal = EventListener::new(&mod_button, "click", move |_event| {
                    // モーダル表示
                    modal_show(
                        Mode::Update,
                        Task { 
                            taskid: task_id_mod.clone(), 
                            userid: user_id_mod.clone(), 
                            title: title_mod.clone(), 
                            description: description_mod.clone() 
                        }
                    );
                });

                // 削除ボタンイベント
                let on_click_del_modal = EventListener::new(&del_button, "click", move |_event| {
                    // モーダル表示
                    modal_show(
                        Mode::Delete,
                        Task {
                            taskid: task_id_del.clone(),
                            userid: user_id_del.clone(),
                            title: title_del.clone(),
                            description: description_del.clone()
                        }
                    );
                });

                // ボタンイベント有効化
                on_click_mod_modal.forget();
                on_click_del_modal.forget();

                // テーブルに行要素を登録
                tbody.append_child(&tr).unwrap();
            }
        });
    }

    // 初回のデータロード
    load_task();

    // 新規登録ボタンイベント
    let on_click_add_modal = move |_: MouseEvent| {
        // モーダル表示
        modal_show(
            Mode::Add,
            Task { 
                taskid: String::from(""), 
                userid: String::from(""), 
                title: String::from(""), 
                description: String::from("") 
            }
        );
    };

    // 登録ボタンイベント
    let on_click_add = move |_: MouseEvent| {
        // 画面から入力データ取得
        let input_data = get_input_data();

        // 登録メッセージ表示
        set_message(Mode::Add, input_data.taskid.clone());

        // APIコール
        spawn_local(async move {
            Request::put(API_URL)
                .body(JsValue::from_str(&format!(
                    "{}{}{}{}{}{}{}{}",
                    "taskid=",
                    &input_data.taskid,
                    "&userid=",
                    &input_data.userid,
                    "&title=",
                    &input_data.title,
                    "&description=",
                    &input_data.description,
                )))
                .send()
                .await
                .unwrap();
            // 画面リロード
            load_task();
        });
        // モーダル非表示
        modal_hide();
    };

    // 更新ボタンイベント
    let on_click_mod = move |_: MouseEvent| {
        // 画面から入力データ取得
        let input_data = get_input_data();

        // 登録メッセージ表示
        set_message(Mode::Update, input_data.taskid.clone());

        // APIコール
        spawn_local(async move {
            Request::post(API_URL)
                .body(JsValue::from_str(&format!(
                    "{}{}{}{}{}{}",
                    "taskid=", &input_data.taskid, "&title=", &input_data.title, "&description=", &input_data.description,
                )))
                .send()
                .await
                .unwrap();
            // 画面リロード
            load_task();
        });
        // モーダル非表示
        modal_hide();
    };

    // 削除ボタンイベント
    let on_click_del = move |_: MouseEvent| {
        // 画面から入力データ取得
        let input_data = get_input_data();

        // 登録メッセージ表示
        set_message(Mode::Delete, input_data.taskid.clone());

        // APIコール
        spawn_local(async move {
            Request::delete(API_URL)
                .body(JsValue::from_str(&format!("{}{}", "taskid=", &input_data.taskid,)))
                .send()
                .await
                .unwrap();
            // 画面リロード
            load_task();
        });
        // モーダル非表示
        modal_hide();
    };

    // 閉じるボタンイベント
    let on_click_close = move |_: MouseEvent| {
        // モーダル非表示
        modal_hide();
    };

    // メッセージ表示
    fn set_message(mode: Mode, task_id: String){
        // 要素を取得
        let document = window().unwrap().document().unwrap();
        let message = document.get_element_by_id("message").unwrap();

        // モードで分岐
        match mode {
            // 新規登録
            Mode::Add => {
                // 登録メッセージ表示
                message.set_text_content(Some(&format!("{}{}{}", "タスク:", &task_id, "が登録されました")));
            },
            // 変更
            Mode::Update => {
                // 更新メッセージ表示
                message.set_text_content(Some(&format!("{}{}{}", "タスク:", &task_id, "が更新されました")));
            },
            // 削除
            Mode::Delete => {
                // 削除メッセージ表示
                message.set_text_content(Some(&format!("{}{}{}", "タスク:", &task_id, "が削除されました")));
            }
        }
    }

    // モーダル表示処理
    fn modal_show(mode: Mode, default_val: Task){
        // 要素を取得
        let document = window().unwrap().document().unwrap();
        let mod_modal = document.get_element_by_id("mod_modal").unwrap();
        let mod_modal_title = document.get_element_by_id("mod_modal_title").unwrap();
        let add_task = document.get_element_by_id("add_task_button").unwrap();
        let mod_task = document.get_element_by_id("mod_task_button").unwrap();
        let del_task = document.get_element_by_id("del_task_button").unwrap();
        let message = document.get_element_by_id("message").unwrap();
        let task_id = document
            .get_element_by_id("taskid")
            .unwrap()
            .dyn_into::<HtmlInputElement>()
            .unwrap();
        let user_id = document
            .get_element_by_id("userid")
            .unwrap()
            .dyn_into::<HtmlInputElement>()
            .unwrap();
        let title = document
            .get_element_by_id("title")
            .unwrap()
            .dyn_into::<HtmlInputElement>()
            .unwrap();
        let description = document
            .get_element_by_id("description")
            .unwrap()
            .dyn_into::<HtmlTextAreaElement>()
            .unwrap();

        // メッセージ非表示
        message.set_text_content(Some(""));

        // 値をセット
        task_id.set_value(&default_val.taskid);
        user_id.set_value(&default_val.userid);
        title.set_value(&default_val.title);
        description.set_value(&default_val.description);

        // モーダル表示
        mod_modal.set_class_name("flex h-full w-full z-[1] top-0 bottom-0 left-0 absolute bg-black bg-opacity visible");

        // ボタン表示制御
        add_task.set_class_name("m-5 text-2xl inline-flex item-center px-3 py-2 font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 invisible");
        mod_task.set_class_name("m-5 text-2xl inline-flex item-center px-3 py-2 font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 invisible");
        del_task.set_class_name("m-5 text-2xl inline-flex item-center px-3 py-2 font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 invisible");        

        // モードで分岐
        match mode {
            // 新規登録
            Mode::Add => {
                // 登録ボタン有効化
                add_task.set_class_name("m-5 text-2xl inline-flex item-center px-3 py-2 font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 visible");
                // モーダルのタイトルセット
                mod_modal_title.set_text_content(Some("新規登録"));
                // 操作可否制御
                task_id.remove_attribute("disabled").unwrap();
                user_id.remove_attribute("disabled").unwrap();
                title.remove_attribute("disabled").unwrap();
                description.remove_attribute("disabled").unwrap();
            },
            // 変更
            Mode::Update => {
                // 更新ボタン有効化
                mod_task.set_class_name("m-5 text-2xl inline-flex item-center px-3 py-2 font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 visible");
                // モーダルのタイトルセット
                mod_modal_title.set_text_content(Some("変更"));
                // 操作可否制御
                task_id.set_attribute("disabled", "disabled").unwrap();
                user_id.set_attribute("disabled", "disabled").unwrap();
                title.remove_attribute("disabled").unwrap();
                description.remove_attribute("disabled").unwrap();
            },
            // 削除
            Mode::Delete => {
                // 削除ボタン有効化
                del_task.set_class_name("m-5 text-2xl inline-flex item-center px-3 py-2 font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 visible");
                // モーダルのタイトルセット
                mod_modal_title.set_text_content(Some("削除"));
                // 操作可否制御
                task_id.set_attribute("disabled", "disabled").unwrap();
                user_id.set_attribute("disabled", "disabled").unwrap();
                title.set_attribute("disabled", "disabled").unwrap();
                description.set_attribute("disabled", "disabled").unwrap();
            }
        }
    }

    // モーダル非表示処理
    fn modal_hide() {
        // 要素を取得
        let document = window().unwrap().document().unwrap();
        let mod_modal = document.get_element_by_id("mod_modal").unwrap();
        let add_task = document.get_element_by_id("add_task_button").unwrap();
        let mod_task = document.get_element_by_id("mod_task_button").unwrap();
        let del_task = document.get_element_by_id("del_task_button").unwrap();

        // モーダル非表示
        mod_modal.set_class_name(
            "flex h-full w-full z-[1] top-0 bottom-0 left-0 absolute bg-black bg-opacity invisible",
        );

        // ボタン表示制御
        add_task.set_class_name("m-5 text-2xl inline-flex item-center px-3 py-2 font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 invisible");
        mod_task.set_class_name("m-5 text-2xl inline-flex item-center px-3 py-2 font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 invisible");
        del_task.set_class_name("m-5 text-2xl inline-flex item-center px-3 py-2 font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 invisible");
    }

    // 画面要素から入力データ取得
    fn get_input_data() -> Task {
        // 要素を取得
        let document = window().unwrap().document().unwrap();
        let task_id = document
            .get_element_by_id("taskid")
            .unwrap()
            .dyn_into::<HtmlInputElement>()
            .unwrap().value();
        let user_id = document
            .get_element_by_id("userid")
            .unwrap()
            .dyn_into::<HtmlInputElement>()
            .unwrap().value();
        let task_title = document
            .get_element_by_id("title")
            .unwrap()
            .dyn_into::<HtmlInputElement>()
            .unwrap().value();
        let task_description = document
            .get_element_by_id("description")
            .unwrap()
            .dyn_into::<HtmlTextAreaElement>()
            .unwrap().value();
        
        // タスクデータを返す
        Task {
            taskid: task_id,
            userid: user_id,
            title: task_title,
            description: task_description
        }
    }

    // レンダリング
    cx.render(rsx! {
        div {
            class: "h-full",
            div {
                class: "text-center bg-yellow-500 text-black text-4xl",
                "タスク一覧"
            }
            div {
                id: "message",
                class: "text-red-500 text-2xl text-center",
            }
            div {
                class: "w-full",
                div {
                    class: "overflow-y-scroll top-0",
                    style: "height:80vh",
                    table {
                        class: "border-2 w-full text-2xl text-gray-500 text-center",
                        thead {
                            class: "border-2 top-0 w-full sticky bg-blue-400",
                            tr {
                                th { class: "px-6 py-3", "タスクID" },
                                th { class: "px-6 py-3", "ユーザID" },
                                th { class: "px-6 py-3", "タイトル" },
                                th { class: "px-6 py-3", "変更" },
                                th { class: "px-6 py-3", "削除" }
                            }
                        },
                        tbody { 
                            id: "tasklist",
                            class: "w-full",
                        }
                    }
                }
            }
            div {
                class: "text-center w-full bottom-0 absolute",
                Link {
                    class: "justify-cennter mx-5 text-2xl inline-flex items-center px-3 py-2 font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300",
                    to: "/", "戻る"
                }
                div {
                    class: "justify-cennter mx-5 text-2xl inline-flex items-center px-3 py-2 font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300",
                    onclick: on_click_add_modal,
                    "新規登録"
                }
            }
        }
        // 登録・変更モーダル
        div {
            id: "mod_modal",
            class: "flex h-full w-full z-[1] top-0 bottom-0 left-0 absolute bg-black bg-opacity-50 invisible",
            div {
                class: "w-1/2 z-[2] h-4/5 mt-5 bg-white justify-between container mx-auto",
                div {
                    id: "mod_modal_title",
                    class: "h-8 bg-blue-700 text-2xl text-white text-center",
                    "登録"
                }
                div {
                    class: "grid gap-6 mb-2",
                    div {
                        class: "justify-center text-center",
                        span {
                            class: "block text-xl text-gray-900",
                            "タスクID"
                            input {
                                "type": "text",
                                id: "taskid",
                                class: "bg-gray-50 mt-4 m-1 border border-gray-300 text-gray-900 text-xl rounded-lg focus:ring-blue-500 p-2.5",
                                value: ""
                            }
                        }
                    }
                }
                div {
                    class: "grid gap-6 mb-2",
                    div {
                        class: "justify-center text-center",
                        span {
                            class: "block text-xl text-gray-900",
                            "ユーザID"
                            input {
                                "type": "text",
                                id: "userid",
                                class: "bg-gray-50 mt-4 m-1 border border-gray-300 text-gray-900 text-xl rounded-lg focus:ring-blue-500 p-2.5",
                                value: ""
                            }
                        }
                    }
                }
                div {
                    class: "grid gap-6 mb-2",
                    div {
                        class: "justify-center text-center",
                        span {
                            class: "block text-xl text-gray-900",
                            "件名"
                            input {
                                "type": "text",
                                id: "title",
                                class: "bg-gray-50 mt-4 m-1 border border-gray-300 text-gray-900 text-xl rounded-lg focus:ring-blue-500 p-2.5",
                                value: ""
                            }
                        }
                    }
                }
                div {
                    class: "grid gap-6 mb-2",
                    div {
                        class: "justify-center text-center",
                        span {
                            class: "block text-xl text-gray-900",
                            "内容"
                            textarea {
                                id: "description",
                                class: "bg-gray-50 mt-4 m-1 border border-gray-300 text-gray-900 text-xl rounded-lg focus:ring-blue-500 p-2.5",
                                value: ""
                            }
                        }
                    }
                }
                div {
                    class: "justify-center text-center",
                    button {
                        class: "justify-cennter mx-5 text-2xl inline-flex items-center px-3 py-2 font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300",
                        onclick: on_click_close,
                        "閉じる"
                    }
                    button {
                        id: "del_task_button",
                        class: "justify-cennter mx-5 text-2xl inline-flex items-center px-3 py-2 font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300",
                        onclick: on_click_del,
                        "削除"
                    }
                    button {
                        id: "mod_task_button",
                        class: "justify-cennter mx-5 text-2xl inline-flex items-center px-3 py-2 font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300",
                        onclick: on_click_mod,
                        "更新"
                    }
                    button {
                        id: "add_task_button",
                        class: "justify-cennter mx-5 text-2xl inline-flex items-center px-3 py-2 font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300",
                        onclick: on_click_add,
                        "登録"
                    }
                }
            }
        }
    })
}

動作確認

ここまで作成できたら、コマンドプロンプトを開き、以下のコマンドを実行します。
コマンドを実行することで、作成したWeb画面が起動します。

docker ps
※rust-rust-appとなっている行のCONTAINER IDを確認する

docker exec -it ↑で確認したID bash

cd front

trunk serve --port 9001 --address 0.0.0.0

初回起動時は、各クレートがダウンロードされるため、少し時間がかかります。
「server listening at http://0.0.0.0:9001」と表示されると、起動は成功です。
途中でエラーが出た場合は、プログラムに入力間違い等がないか確認しましょう。
起動完了後、Webブラウザを起動し、「http://localhost:9001」にアクセスします。
以下のような画面が表示されれば、アクセスに成功しています。

新規登録の動作確認

ブラウザに表示された画面から、タスク一覧ボタンを押します。
以下のような画面が表示されるので、新規登録ボタンを押します。

以下のような画面が表示されるので、入力項目をすべて埋めて、登録ボタンを押します。

以下のように表示されれば、登録処理が正常に行われています。

変更の動作確認

先ほど登録を行ったタスクの変更ボタンを押します。

件名、内容を変更し、更新ボタンを押します。
変更の場合は、タスクID、ユーザIDは変更できないようになっています。

以下のように表示されれば、変更処理が正常に行われています。

削除の動作確認

先ほど変更を行ったタスクの削除ボタンを押します。

削除ボタンを押します。
削除の場合はすべての項目が編集できないようになっています。

以下のように表示されれば、削除処理が正常に行われています。

まとめ

以上で、データの登録、変更、削除を行う画面ができました。
次回は、この画面にログインの機能や、入力チェックの機能などを追加してみたいと思います。

次回に続く。

コメント

タイトルとURLをコピーしました