【问题标题】:Using layouts in HAML files independently of Rails独立于 Rails 使用 HAML 文件中的布局
【发布时间】:2011-09-01 18:54:19
【问题描述】:

我的最终目标是创建几个静态 HTML 文件以移交给其他人。

但对于我的工作流程,我希望将 HAML 作为基本源文件。在这样做的过程中,我希望至少在我这边能干掉这个过程。

现在我有很多页面最终将共享一个通用布局,我想知道如何合并这些布局。

这是我当前的代码:

./compile.rb

#!/usr/bin/env ruby

require 'rubygems'
require 'rake'
require 'haml'

FileList.new('./src/*.html.haml').each do |filename|
  if filename =~ /([^\/]+)\.haml$/
    File.open($1, 'w') do |f|
      f.write Haml::Engine.new(File.read(filename)).render
    end
  end
end

./src/layout.html.haml

!!!
%html
  %head
    %title Yay
  %body
    = yield

./src/home.html.haml

= render :layout => 'header' do
  %p This is awesome

现在这显然行不通,因为在 Rails 的上下文之外未定义渲染方法,但我希望它能够理解我正在尝试做的事情。

有什么建议吗?

【问题讨论】:

    标签: ruby haml


    【解决方案1】:

    您混淆了两个不同的 Rails 功能:partials (using render)layouts (using yield)

    您可以将其中任何一个(或两个)的类似 rails 的版本添加到仅 Haml 的程序中。

    部分

    在 Rails 视图中,您可以使用 render :partial_name 使文件 _partial_name.html.haml 在包含视图中的该点呈现(实际上 Rails 允许您使用任何支持的模板语言,它会找到正确的文件扩展名使用,但我会在这里坚持使用 Haml)。 Rails 之外的render 不可用,但可以很容易地添加。

    一个简单的render 方法只会找到适当的haml 文件,渲染它,然后返回html 字符串以包含在父级中:

    def render(partial)
      # assuming we want to keep the rails practice of prefixing file names
      # of partials with "_"
      Haml::Engine.new(File.read("_#{partial}.html.haml")).render
    end
    

    Haml::Engine.render 的第一个参数是一个作用域对象,我们可以使用它来添加在 haml 模板中可用的方法。默认为Object.new。然而,在这样一个简单的例子中,我们可以在顶层定义 render 方法,它可以在 Haml 模板的范围内使用。我们只是在调用Haml::Engine.new(...).render之前将我们的render方法放在脚本中,然后在我们的模板中这样调用它:

    !!!
    %html
      %head
        %title Hello
      %body
        =render :the_partial
    

    现在文件_the_partial.html.haml 将呈现在输出的适当位置。

    局部变量

    我们可以更进一步。 Rails 允许您将 local variables 的哈希值传递给部分。 Haml 还将接受要作为局部变量传递的变量散列,作为 Haml render 方法的第二个参数。因此,如果我们将渲染方法扩展为如下所示:

    def render(partial, locals = {})
      Haml::Engine.new(File.read("_#{partial}.html.haml")).render(Object.new, locals)
    end
    

    我们可以使用如下所示的部分:

    %p You passed in #{foo}
    

    并从我们的模板中调用它:

    %body
      =render :partial, :foo => "bar"
    

    将渲染

    <body>
      <p>You passed in bar</p>
    </body>
    

    布局

    在 Rails 中,您可以为视图指定布局,以便所有页面可以共享相同的 标题、菜单区域等。这是通过指定一个布局文件来完成的,您可以在其中调用yield 来呈现有问题的实际视图。将布局添加到haml 中稍微复杂一些,但仍然可以完成。

    Hamls render 方法也接受一个块,所以一个简单的解决方案是渲染布局文件,并传递一个渲染视图文件的块:

    Haml::Engine.new(File.read("layout.html.haml")).render do
      Haml::Engine.new(File.read("view.html.haml")).render
    end
    

    这将使layout.html.haml 的内容与view.html.haml 的内容一起呈现,其中布局文件包含=yield

    content_for

    Rails 比这更灵活一些。它允许您在布局文件中多次调用yield,在每种情况下命名一个特定区域,并在您的视图中使用content_for 方法指定要在每个区域添加的内容。所以在你的布局文件中:

    !!!
    %html
      %head
        = yield :title
      %body
        =yield
    

    在你看来:

    -content_for :title do
      %title Hello
    %p
      Here's a paragraph.
    

    Rails 的实际工作方式是首先渲染视图部分,存储所有不同的部分,然后渲染布局,每当在布局中调用 yield 时传递一个提供适当块的块。我们可以使用一个小助手类来复制它,以提供content_for 方法并跟踪每个区域的渲染块:

    class Regions  
      def initialize
        @regions_hash={}
      end
    
      def content_for(region, &blk)  
        @regions_hash[region] = capture_haml(&blk)
      end
    
      def [](region)
        @regions_hash[region]
      end
    end
    

    这里我们使用capture_haml method 来获取渲染后的haml,而不会将其直接输出。请注意,这不会捕获视图的未命名部分。

    我们现在可以使用我们的帮助类来呈现最终输出。

    regions = Regions.new
    unnamed = Haml::Engine.new(File.read("view_named.html.haml")).render(regions)
    
    output = Haml::Engine.new(File.read("layout_named.html.haml")).render do |region|
      region ? regions[region] : unnamed
    end
    

    现在变量 output 包含最终渲染的输出。

    请注意,此处的代码并未提供 Rails 所包含的所有灵活性,但希望它足以向您展示从哪里开始自定义 Haml 以满足您的需求。

    【讨论】:

    • 谢谢! Engine#render 如何使用该块非常有趣,通过您的示例,我想我终于理解了传递给#render 的第一个参数的性质。绝妙的答案。
    • 这是我见过的最彻底、写得好、格式好的帖子之一。 @matt 谢谢!
    • 我没有在 Ruby-on-Rails 下使用 haml。如何通过独立命令 haml 调用这些自定义代码?我的命令行:haml test_with_the_partial_in_it.haml @matt
    • 模板是否可以有rails helpers?我在 Rails 上使用它,不使用 rails 助手时效果很好。但在我的情况下,我共享标题并且它有一个我可以使用= image_tag 'logo.png' 访问的徽标。但不幸的是我收到了错误undefined method 'image_tag' for #&lt;Object:0x0000010122e190&gt;
    【解决方案2】:
    content = Haml::Engine.new(content_haml).render(
      Object.new, 
      :local_var_1 => ..., 
      :local_var_2 => ...
    )
    
    
    Haml::Engine.new(layout_haml).render(Object.new, :content => content)
    

    layout.haml

    !!!
    %html
      %head
        %title
      %body
        = content
    

    我相信你也可以在haml中使用Object.new的实例变量(用有意义的对象替换)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-10-26
      • 1970-01-01
      • 2012-02-09
      • 1970-01-01
      • 2014-12-03
      • 2017-04-09
      • 1970-01-01
      相关资源
      最近更新 更多