例如我有:
-record(usr,{name,email}).
...
Usr1 = #usr{name="John", email="john@host.com"},
Usr2 = #usr{name="Jane", email="jane@host.com"},
Usr3 = #usr{name="Alex", email="alex@host.com"},
{ok, Result} = template_dtl:render([{users, [Usr1, Usr2, Usr3]}]),
...
我希望像以下一样使用它:
{% block content %}
{% for user in users %}
<a href="mailto:{{user.email}}">Send mail to {{user.name}}</a>
{% endfor %}
{% endblock %}
有人遇到同样的问题吗?
答案 0 :(得分:4)
记录是元组之上的语法糖。 Usr1
,Usr2
和Usr3
只是元组,正是:
Usr1 = {usr, "John", "john@host.com"},
Usr2 = {usr, "Jane", "jane@host.com"},
Usr3 = {usr, "Alex", "alex@host.com"}.
模板不知道如何解释这些记录,因为它在编译时不知道记录定义。
您的问题有三种解决方案。在所有情况下,模板都是相同的,您应该像处理user.email
和user.name
一样。
record_info
编译选项正好用于告诉erlydtl模板变量中使用的记录。
erlydtl:compile_template(Template, TemplateModuleName, [{record_info, [{usr, record_info(fields, usr)}]).
缺点是您可能不会自己致电erlydtl:compile*
,因此添加record_info
选项可能会很困难。此外,调用此函数的代码段必须知道您可能必须移动到.hrl
文件的记录定义。
这是Soup d'Campbells在评论中建议的内容。您还可以使用编译时函数record_info/2
来实现此目的。最简单的是proplist()
形式:
lists:zip(record_info(fields, usr), tl(tuple_to_list(Usr1))).
tuple_to_list(Usr1)
评估为[usr, "John", "john@host.com"]
,而record_info(fields, usr)
等于[name, email]
。
使用公共访问器(user.name
)捕获数据结构的记录并不是很好,因为它们可以更好地在本地维护给定模块,因为这样可以更轻松地进行代码更新。或者,您可以定义一个模块(称为usr
或app_user
但不是user
,因为这样一个模块已经存在),它将导出name/1
和email/1
个访问者。
这里的Erlydtl魔术是基于以前称为参数化模块的功能,更准确地说是使用元组而不是原子作为模块名称来调用函数的能力。您不需要参数化模块,只需传递与现有模块匹配的元组。
例如,您的app_user
模块可能如下所示:
-module(app_user).
-export([new/2, name/1, email/1]).
-record(?MODULE, {name :: string(), email :: string()}). % private to this module.
-type app_user() :: #?MODULE{}.
-spec new(string(), string()) -> app_user().
new(Name, Email) -> #?MODULE{name = Name, email = Email}.
-spec name(app_user()) -> string().
name(#?MODULE{name = Name}) -> Name.
-spec email(app_user()) -> string().
email(#?MODULE{email = Email}) -> Email.
(?MODULE
代替app_user
,因为只有当记录的名称与模块名称匹配时才有效,即使重命名模块,此代码仍然有效。“
然后,在您的代码中,而不是:
Usr1 = #usr{name = "John", email = "john@host.com"}
你会写:
Usr1 = app_user:new("John", "john@host.com").
Usr1只是一个记录,或者更确切地说是元组:
{app_user, "John", "john@host.com"}.
然而,Erlydtl将能够直接处理Usr1
。它会将其视为参数化模块,因为app_user
作为模块存在(它是上面的模块)。因此,在模板渲染期间,它将调用app_user:name/1
和app_user:email/1
访问器函数,并将整个记录传递给它们。