😽

Dioxus (Rustでfrontend) 入門2

2022/10/14に公開

RSX入門

基本的な書式

ドキュメントは下記にあります。

https://dioxuslabs.com/guide/elements/vnodes.html

以下サンプル

rsx!(
    div {
        button {
            onclick: move |e| todos.write().new_todo(),
            "Add todo"
        }
        ul {
            class: "todo-list",
            todos.iter().map(|(key, todo)| rsx!(
                li { 
                    class: "beautiful-todo"
                    key: "f"
                    h3 { "{todo.title}" }
                    p { "{todo.contents}"}
                }
            ))
        }
    }
)
<div>
    <button onclick="XXX"> Add todo </button>
    <ul class="todo-list">
    <li class="beautiful-todo" key="f" > 
        <h3>$todo.title</h3>
        <p> $todo.contents </p>
    </li>
</div>

などと変換されます。 (繰り返しは省略ですw)
書式的に難しいところは特にありませんので、詳しい説明は省略させていただきます。

デフォルトで利用できるタグ

ここに列挙するとものすごい量になるので以下を参照してください。

https://github.com/DioxusLabs/dioxus/blob/master/packages/rsx/src/elements.rs

利用可能なイベント

ここに列挙するとすごい量になるので以下を参照してください。

https://github.com/DioxusLabs/dioxus/blob/b32fd2d2cddce8a1fe79fe83c7575d0699665d34/packages/html/src/events.rs

条件分岐

マニュアルには rsx内部で書く場合には thenを利用したものしか載っておりませんが、 ifもmatchも利用できます。
rsxの外に書く場合は公式マニュアルがわかりやすいのでそちらを参照してください。

https://dioxuslabs.com/guide/elements/conditional_rendering.html

match文をrsx内で利用したい!

{ match 10 { 
    10 => rsx!{"10!!"},
    20 => rsx!{"20!!"},
    _ => rsx!{"default!!"},
}}

if をrsx内で利用したい!

{if 1 == 2 { rsx!{"aaa"} } else { rsx!{"bbb"} }}

ループ

マニュアルはここです。
https://dioxuslabs.com/guide/elements/lists.html

これに関して特に補足することはありませんが、 for文をrsx内で利用する方法を知りたいなら 続きを読んでください。

        ul {
            class: "todo-list",
            todos.iter().map(|(key, todo)| rsx!(
                li { 
                    class: "beautiful-todo"
                    key: "f"
                    h3 { "{todo.title}" }
                    p { "{todo.contents}"}
                }
            ))
        }

このような感じで todos (Vec<Todo>) の中身をループで取り出せます。

通常の用途であれば iter().map で事足りますが、 なんとしても for文を使いたい場合(あまり想像できないですがw)

下記の様に書けば for文をrsx!内で利用できます。

{
    let mut tmp: Vec<LazyNodes> = Vec::new();
    for i in 0..10 {
        let str = format!("counter: {}", i);
        tmp.push(rsx!{ div{"{str}"}});
    }
    tmp
}

カスタムHTMLを入れるには

let custom_elem = LazyNodes::new(move |f| {        
	f.raw_element("hogege", None, &[], &[Attribute{name: "hoge", value: "moge", is_static: true,is_volatile:false, namespace:None}],&[],None) 
        
});

などとすると

	<hogege hoge="moge"></hoge>

というタグを作り出すことができる。 また、イベントリスナも足すことができるため、
rsxでは事前定義されていない属性やイベント・タグなども追加することができます。

生htmlタグを出力したい

	div {
            dangerous_inner_html: "<ul><li>aaa</li><li>bbb</li></ul>",
        }

dangerous_inner_html属性に文字列を渡せばhtmlタグをそのままレンダリングすることができます。
ただクロスサイトスクリプティングに弱くなりますので、渡す内容は問題ないものに限定しなければなりません。

prevent_defaultでformによる自動ナビゲーションの停止

例えば下記のようなrsx!を考えてみます。

	form {
            onsubmit: move |e| {submit_event(e)},

            oninput: move |ev:FormEvent| {onchange_ev(ev);},
            input { r#type: "text", name: "username" }
            input { r#type: "text", name: "full-name" }
            input  { r#type: "submit", value: "","aaa" }
        }

このときsubmitボタンを押すと
http://localhost:8080/form_test?username=aaa&full-name=vvv
といった感じで現在のページに対してGETが走り、wasmやjsなどが再ロードされてしまいます。

それでは困るので、サブミットボタンをクリックしてもナビゲーションされないようにしてみます。
それには上記のrsxを下記のように書き直します。

	form {
            onsubmit: move |e| {submit_event(e)},
            prevent_default: "onsubmit",

            oninput: move |ev:FormEvent| {onchange_ev(ev);},
            input { r#type: "text", name: "username" }
            input { r#type: "text", name: "full-name" }
            input  { r#type: "submit", value: "","aaa" }
            button { r#type: "submit", value: "submit"}
        }

prevent_default: "onsubmit" prevent_defaultに 抑制したいイベント名を書きます。
そうすることで、自動ナビゲーションを無効にすることができます。
これによって、GETが走ることはなくなるため、 jsやwasmやcssなどが再ロードされることはなくなります。

ちなみに formのsubmitイベントを利用すると

       let submit_event = move |ev: FormEvent| {
        ev.cancel_bubble();
        console::warn_1(&format!("Submitted {:?}", ev.values).into())
    };

ev.valuesに Hash<String,String> を得ることができます。キーは form内の intputタグのnameが入り、値にはvalueが入ります。

inputイベントではないので、submitした瞬間にvalidateを一気にかけたり、 Hash<String,String>から任意のstructに変換する TryFromなどを用意することで、変換することもできます。

また、入力ごとに再レンダリングが走ることもないため、それらが不要な場合には便利な方法です。
入力されるごとに、validateを走らせたい場合などでは、formの oninputイベントを利用することもできます。

Discussion