#html #yew #variables #localization #seamless #object #attributes

yew-template

一个用于将单独的HTML文件作为Yew对象使用,并支持无缝本地化的crate。

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中使用

MIT许可证

58KB
925

Yew-Template

Crates.io GitHub last commit GitHub GitHub closed issues docs.rs

一个用于将单独的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宏中的相同语法,并且需要使用compcomponent标签名称显式标记为组件。

<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_ageNone,则不会显示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_ageopt_birth_city都是可选的。opt_age将会显示,即使opt_birth_cityNone。但是,如果opt_ageNone,则无论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 元素上添加属性,例如 iteroptpresent-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 = ["{{", "}}"]

功能

所有功能默认启用。目前有两个功能

  • config:允许您使用 yew-template.toml 设置
  • i18n:启用对本地化的支持

安全注意事项

  • 安全地显示各种字符串。它们将被适当地转义,防止 HTML 和 Rust 注入。
  • 本地化字符串在生成的代码中是无害的,但它们可能会破坏编译。
  • 不要使用不受信任的模板文件。
  • 不要使用不受信任的 yew-template.toml 文件。

许可证:MIT

依赖关系

~2.4–9MB
~85K SLoC