LoginSignup
1
0
お題は不問!Qiita Engineer Festa 2024で記事投稿!
Qiita Engineer Festa20242024年7月17日まで開催中!

Sycamoreで生のHTMLを親の要素で包まずに返すcomponentを作った

Last updated at Posted at 2024-06-21

背景

SycamoreというRustとWebAssemblyでWebアプリを作るライブラリがあります。

Node.jsでいうReactやVueみたいなものです(言い方が雑で怒られそうかもしれない……)

このSycamoreでは、純粋に生のHTMLを扱う場合はdangerously_set_inner_htmlという属性を使うだけで十分です。

例えば

view!{
    div(
        dangerously_set_inner_html=inner_html,
    )
}

↑このようにするだけで生のHTMLをそのまま出力することができます。

一方で、この方法では、この例のdivのように、何かその生のHTMLの親にあたる要素も一緒に作らなければならないという不自由がありました。

やること

今回は、簡単そうな状況に限定して、ちょうど1つだけの要素を表現する生のHTMLを、親の要素で包まずに返すcomponentを作ることに挑戦します。

結論

こんな感じでcomponentを作りました。

#[component(inline_props)]
pub fn RawHtml<G:Html>(
	inner_html:String,
)->View<G>{
	let container:View<G>=view!{
		div(
			dangerously_set_inner_html=inner_html.clone(),
		)
	};
	let node=container
		.as_node()
		.unwrap()
		.first_child()
		.unwrap();
	View::new_node(node)
}

説明

まず順に説明します。

dangerously_set_inner_html

これは特別な属性で、Vueでいうところのv-htmlと同じような働きをします。

.first_child

これで先程divの中に入れたinner_htmlGenericNodeを実装する型にして取り出します。

View::new_node

これで先程取り出したGenericNodeな型になったinner_htmlを改めてView<G>の型にします。

実演

では具体的な最小の構成で上記コンポーネントを使う場合とそうでない場合を比較したいと思います

本記事で提案するコンポーネントを使わない場合

まず、以下に本記事で提案するコンポーネントを使わない場合の最小限の構成で生のHTMLを追加するコード例を示します。

Cargo.toml
[package]
name="sycamore-raw-html"
version="0.0.0"
edition="2021"

[dependencies.sycamore]
version="^0.9.0-beta.2"
index.html
<!doctype html>
<html>
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<link data-trunk rel="rust" href="./Cargo.toml" />
	</head>
</html>
src/main.rs
use sycamore::prelude::*;

#[component]
fn App<G:Html>()->View<G>{
	view!{
		div(
			dangerously_set_inner_html=r#"<span style="color:red;">raw html</span>"#,
		)
	}
}

fn main(){
	sycamore::render(||view!{
		App
	});
}

次のコマンドでサーバーを立ち上げてブラウザの開発者ツールで確認します。

ターミナルで実行
trunk serve

image.png

上記画像を見ると、意図したとおり生のHTMLとして入力したspanが親の要素として追加したdivで囲まれていることがわかります。

本記事で提案するコンポーネントを使う場合

では次に、以下に本記事で提案するコンポーネントを使った場合の最小限の構成で生のHTMLを追加するコード例を示します。

今度は上記コード例のうちsrc/main.rsのみ変更します。以下に全文を示します。

src/main.rs
use sycamore::prelude::*;

#[component(inline_props)]
pub fn RawHtml<G:Html>(
	inner_html:String,
)->View<G>{
	let container:View<G>=view!{
		div(
			dangerously_set_inner_html=inner_html.clone(),
		)
	};
	let node=container
		.as_node()
		.unwrap()
		.first_child()
		.unwrap();
	View::new_node(node)
}

#[component]
fn App<G:Html>()->View<G>{
	view!{
		RawHtml(
			inner_html=r#"<span style="color:red;">raw html</span>"#.to_string(),
		)
	}
}

fn main(){
	sycamore::render(||view!{
		App
	});
}

先程と同様にサーバーを立ち上げて開発者ツールで確認します

image.png

上記画像を見ると、今度は意図したとおり生のHTMLとして入力したspanbodyの直下に追加されていることがわかります。

応用について

このコンポーネントの特長は、生のHTMLを追加の親要素で囲むことなく、そのまま出力できる点です。これは、例えばCSSの要件で親要素で囲むことが適切でない場合に役立ちます。具体例としては、display: flex;の都合で親要素を追加できない場合や、 > で直接の子要素を指すセレクタを使いたい場合などが考えられます。このような場合に、本記事で提案するコンポーネントが有用であると考えられます。

心配なところ

今回はinner_htmlの中は一番祖先の要素はちょうど1つだけになる場合を扱いました。
しかし、一般には一番祖先の要素が複数ある場合や1つもない場合も考えられますが、そのような場合にもこの方法で機能するかという心配をしています。
そのような場合があれば別途チェックしようと思います。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0