Rails渲染邮件模板为字符串时无法解析样式表,如何嵌入CSS内容?
我之前在Rails里用外部API发送邮件,需要手动把HAML模板渲染成HTML字符串,但遇到了一个麻烦:生成的HTML里保留了<link>标签,却没有把styles.css的实际内容嵌入进去。试了直接在模板里写:css = MyApp::Application.assets["styles.css"].to_s.html_safe,结果这段代码直接被原样输出,根本没得到解析;用ApplicationController.render(template_path)也没能解决问题。
下面分享几个能解决这个问题的方法:
方法一:手动获取CSS内容,通过局部变量传入模板渲染
这个方法的核心是先拿到CSS的文本内容,再把它作为变量传给HAML模板,替换原来的<link>标签为<style>标签:
- 修改你的模板渲染方法,加入获取CSS和传递变量的逻辑:
def html_for_template(template_name) template_root = "app/views/emails" template_path = "#{template_root}/#{template_name}.haml" haml_content = File.read(template_path) # 从资产管道获取styles.css的原始内容 css_content = MyApp::Application.assets["styles.css"].to_s # 渲染HAML时,把css_content作为局部变量传入 html = Haml::Engine.new(haml_content).render(Object.new, css: css_content) # 如果模板里还有ERB语法,再用ERB处理(建议统一用HAML,这里可以去掉) # content = ERB.new(html).result(binding) html end
- 修改HAML模板,把原来的
<link>标签替换为嵌入CSS的<style>标签:
%html %head %meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"} %style{type: "text/css"} = css
这样渲染出来的HTML就会把styles.css的内容直接放到<style>标签里,而不是保留外部引用的<link>。
方法二:借助ActionView的渲染机制,利用资产辅助方法
如果想更贴近Rails的原生渲染流程,可以用ActionView::Base来渲染模板,这样还能使用Rails的资产辅助方法,同时处理CSS嵌入:
def html_for_template(template_name) # 初始化一个ActionView实例 view = ActionView::Base.new(ActionController::Base.view_paths, {}) # 引入必要的辅助方法 view.class_eval do include Rails.application.routes.url_helpers include ActionView::Helpers::AssetTagHelper end # 获取CSS内容,替换link标签为style标签 css_tag = view.stylesheet_tag("styles.css", media: "all") inline_css = css_tag.gsub(%r{<link[^>]+>}) do css_content = Rails.application.assets["styles.css"].to_s "<style type='text/css'>#{css_content}</style>" end # 渲染模板,传入inline_css变量 view.render(template: "emails/#{template_name}", locals: { inline_css: inline_css }) end
然后在模板里直接使用这个变量:
%html %head %meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"} = inline_css
为什么之前的方法无效?
之前在模板里直接写:css = MyApp::Application.assets["styles.css"].to_s.html_safe没生效,是因为HAML模板在渲染时的上下文默认无法直接访问这段代码,它被当成了普通字符串输出。只有通过渲染时的locals参数或者binding把变量传入模板,才能让模板正确解析并插入内容。
另外要注意:如果是生产环境,确保你的assets已经预编译完成,否则可能无法正确获取到CSS内容。
内容的提问来源于stack exchange,提问作者Snowman

