21个版本 (9个重大更改)
0.10.0 | 2023年6月24日 |
---|---|
0.9.0 | 2023年1月22日 |
0.8.1 | 2022年11月26日 |
#520 in 模板引擎
每月49次下载
在yew-datepicker中使用
58KB
925 行
Yew-Template
一个用于将单独的HTML文件作为Yew对象使用,并支持无缝本地化的crate。
入门
Hello World
<div>
<p>Hello {{name}}!</p>
</div>
let html = template_html!("templates/hello.html", name="World");
上面的代码实际上会编译成以下代码
let html = yew::html! {
<div>
<p>{"Hello World!"}</p>
</div>
};
用法
变量
let name = "World";
let html = template_html!("templates/hello.html", name);
编译成
let name = "World";
let html = yew::html! {
<div>
<p>{"Hello "}{name}{"!"}</p>
</div>
};
当您的变量名与模板中的名称不同时,您可以使用以下语法
let other_name = "Yew";
let html = template_html!("templates/hello.html", name=other_name);
属性
<div style={{style}}>
<p>Hello {{name}}!</p>
</div>
let html = template_html!(
"templates/hello.html",
name="Yew",
style="color: red;"
);
Yew-template支持在属性中使用类似于format!
的语法,允许您执行以下操作
<div style="background-color: {{bg_color}}; color: {{text_color}};">
Yew is cool
</div>
结构字段
有时您想将许多结构字段作为变量传递给模板,但解构结构会太冗长。与使用实际的yew宏一样,您只需传递结构即可从模板中访问其字段
<div>
<p>Hello {{person.first_name}} {{person.last_name}}!</p>
</div>
struct Person {
first_name: String,
last_name: String,
}
let person = Person {
first_name: "Edouard".to_string(),
last_name: "Foobar".to_string()
};
let html = template_html!("templates/fields.html", person);
表达式
let name_reversed = String::from("dlroW");
let html = template_html!(
"templates/hello.html",
name = {
let mut name = name_reversed.into_bytes();
name.reverse();
let name = String::from_utf8(name).unwrap();
name
}
);
这将显示Hello World!
作为Yew代码输出如下
let name_reversed = String::from("dlroW");
let html = yew::html! {
<div>
<p>
{"Hello "}{{
let mut name = name_reversed.into_bytes();
name.reverse();
let name = String::from_utf8(name).unwrap();
name
}}{"!"}
</p>
</div>
};
请注意,表达式周围需要大括号。
使用Yew回调的示例
<div onclick={{onclick}}>
<p>Hello {{name}}!</p>
</div>
let link = ctx.link();
let html = template_html!(
"templates/hello.html",
name="World",
onclick={link.callback(|_| Msg::AddOne)}
);
组件
虽然yew-template只能与原始HTML一起使用,但您也可以在模板中使用Yew组件。它们不遵循Yew的html宏中的相同语法,并且需要使用comp
或component
标签名称显式标记为组件。
<comp name="SearchBar"/>
<!-- Which is equivalent to -->
<component name="SearchBar"/>
<!-- Or even -->
<Component name="SearchBar"/>
如您所见,组件的rust标识符作为属性传递。
只要你的组件支持,其他属性甚至子元素都可以通过常规方式传递。
<comp name="SearchBar" placeholder="Search..." onclick={{onclick}}>
<span>child 1</span>
<span>child 2</span>
</comp>
可选变量
可选变量以opt_
前缀或_opt
后缀标记,由你选择。它们的值预期为Option<T>
。
可选变量与可选HTML元素一起使用。使用opt
属性标记一个元素以使其可选。只有当包含在其中的所有可选变量都是Some
时,可选元素才会渲染。请注意,包含在较小可选元素中的变量不受此要求限制。
<div>
<p>Hello {{name}}!</p>
<div opt>
<h2>Age</h2>
<p>You are {{opt_age}} years old!</p>
</div>
</div>
在上面的例子中,如果opt_age
是None
,则不会显示div
块。
让我们看看可选元素可以如何嵌套。
<div>
<p>Hello {{name}}!</p>
<div opt>
<h2>Age</h2>
<p>You are {{opt_age}} years old!</p>
<p opt>And you are born in {{opt_birth_city}}.</p>
</div>
</div>
在这里,opt_age
和opt_birth_city
都是可选的。opt_age
将会显示,即使opt_birth_city
是None
。但是,如果opt_age
是None
,则无论opt_birth_city
的值如何,都不会显示。
从Rust的角度来看,没有使用上的差异。注意,目前需要使用花括号。
let opt_age: Option<u8> = Some(20);
let opt_birth_city: Option<String> = None;
let html = template_html!(
"templates/opt.html",
name="John",
opt_age,
opt_birth_city
);
在生成的Yew代码中,使用if let
表达式。因此,基于表达式的可选变量行为不同,因为它们只为每个使用的可选元素计算一次。
可选元素
有时可选变量不适合使元素可选。你可能需要一个比仅仅检查变量是Some
还是None
更复杂的逻辑。在这种情况下,你可以使用可选元素。
元素可以赋予一个present-if
属性。该值将在运行时作为布尔表达式计算。如果表达式为true
,则元素将被渲染。否则,它将被跳过。
<div present-if={{condition}}>
<p>1+1 = 3</p>
</div>
<div present-if=!{{condition}}> <!-- Negation is supported -->
<p>1+1 != 3</p>
</div>
let html = template_html!("templates/present_if.html", condition={ 1+1==3 });
迭代器
迭代器的工作方式与可选变量类似。迭代器变量以iter_
前缀或_iter
后缀标记,由你选择。循环HTML元素以iter
属性标记。该元素将根据其依赖的迭代器之一为空而重复。
<div>
<h2>Contributors:</h2>
<ul>
<li iter>{{contributors_iter}} ({{commits_iter}} commits)</li>
</ul>
</div>
let contributors = vec!["John", "Jane", "Jack"]; // Owned values need to be declared as `let` or they would be freed before the template is rendered.
let html = template_html!(
"templates/iter.html",
contributors_iter = {contributors.iter()},
commits_iter = {[42, 21, 7].iter()}
);
上面的代码将作为以下Yew代码工作
let contributors = vec!["John", "Jane", "Jack"];
let html = yew::html! {
<div>
<h2>{"Contributors:"}</h2>
<ul>
{{
let mut contributors_iter = { contributors.iter() };
let mut commits_iter = { [42, 21, 7].iter() };
let mut fragments = Vec::new();
while let (Some(contributor), Some(commits)) = (contributors_iter.next(), commits_iter.next()) {
fragments.push(html! { <li>{contributor}{" ("}{commits}{" commits)"}</li> });
}
fragments.into_iter().collect::<Html>()
}}
</ul>
</div>
};
到目前为止,不支持Yew列表中的项目引用。这将在Yew文档建议的情况下在未来实现,尽管在大多数情况下已经发现其性能影响可以忽略不计。
减少冗余
使用此crate的全部目的是使你的代码比直接使用Yew时更易于阅读。然而,你仍然会发现自己在编写不具有太多意义的代码行。我们已经看到variable_ident=variable_ident
可以缩短为variable_ident
。但它甚至可以完全省略!在宏调用末尾添加...
以告诉未定义的变量应从具有相同名称的局部变量中检索。以“Hello world”示例
<div>
<p>Hello {{name}}!</p>
</div>
let name = "World";
let html = template_html!("templates/hello.html", ...);
默认情况下,此行为被禁用,因为缺失的变量通常是由于错误。如果您想在不需要在每个宏调用中添加 ...
的情况下启用它,请将您的 配置 中的 auto_default
设置为 true。
虚拟元素
Yew-template 通常要求您在 HTML 元素上添加属性,例如 iter
、opt
或 present-if
。在罕见的情况下,您可能没有任何合适的元素可以添加这些属性,添加包装元素会破坏您的 CSS。在这种情况下,您可以使用虚拟元素。虚拟元素标签将在最终的 HTML 中被删除,但它允许您在需要的地方添加特殊属性。
<virtual opt>
{{opt_name}}
</virtual>
let opt_name = Some("John".to_string());
let html = template_html!("templates/virtual.html", opt_name);
在 Yew 方面,这将被视为
let opt_name = Some("John".to_string());
let html = yew::html! {
<>
if let Some(opt_name) = opt_name { {opt_name} }
</>
};
Yew 将生成以下 HTML
John
本地化
Yew-template 支持本地化。它能够从 .po
文件中提取本地化数据,并将其自动嵌入生成的代码中。启用此功能就像将 .po
文件放入目录中一样简单。
需要启用 i18n
cargo 功能(默认情况下已启用)。
默认情况下,地区目录设置为 locales
。您可以通过在您的 配置 中设置 locale_directory
来更改此设置。Yew 模板将自动在目录中生成一个最新的 .pot
文件。将其用作翻译软件中的模板来生成 .po
文件。
翻译完成后,将您的 .po
文件放入地区目录。将自动启用对添加的地区支持。
为了在运行时选择要渲染的地区,您需要将一个 locale
变量传递给模板-html 宏调用。该变量将与地区目录中 .po
文件的文件名进行匹配(不包括 .po
扩展名)。如果没有找到匹配项,字符串将保留在模板中出现的原样。
除了使用 locale
变量之外,您还可以决定评估任何 Rust 表达式。请参阅 配置 部分的 locale_code
选项。
Yew-template 阻止本地化字符串中的代码注入。这是通过转义双引号和反斜杠来实现的。将翻译委托给未知同伴是 安全的。然而,这些字符串可以包含变量引用,如果引用的变量未定义,则可能会破坏编译。Yew-template 将在未来处理这个问题。
配置
您可以在 crate 根目录的 yew-template.toml
文件中指定各种设置。这需要启用 config
cargo 功能(默认情况下已启用)。
这是默认配置
# Whether to attempt to capture local variables instead of aborting when arguments required by the template are missing.
auto_default = false
# Where to look for templates (relative to crate root)
template_directory = './'
# Where to look for locales (relative to crate root)
locale_directory = './locales/'
# Rust code to evaluate as locale. Should evaluate to a &str.
# If will be inserted in generated code like this: `match locale_code {`.
locale_code = 'locale.as_str()'
# Two strings marking the beginning and end of a variable in a template.
variable_separator = ["{{", "}}"]
功能
所有功能默认启用。目前有两个功能
安全注意事项
- 安全地显示各种字符串。它们将被适当地转义,防止 HTML 和 Rust 注入。
- 本地化字符串在生成的代码中是无害的,但它们可能会破坏编译。
- 不要使用不受信任的模板文件。
- 不要使用不受信任的
yew-template.toml
文件。
许可证:MIT
依赖关系
~2.4–9MB
~85K SLoC