在rails 3.1中更改视图格式(提供移动html格式,在普通html上回退)

时间:2012-01-14 15:45:31

标签: ruby-on-rails ruby-on-rails-3.1

我正在我们正常的html网站旁边创建一个移动网站。使用rails 3.1。移动站点在子域m.site.com中访问。

我已经定义了移动格式(Mime :: Type.register_alias“text / html”,:mobile)。

在ApplicationController中,我有“before_filter:mobile_site_before_filter”来识别移动网站并根据它设置格式。

def mobile_site_before_filter
  if request.subdomains.first == 'm'
    original_format = request.format
    request.format = :mobile
    @mobile_site = true
    request.formats.push(original_format)
  end
end

在布局中,我有'main.html.erb'和'main.mobile.erb'。因此,对于移动网站,使用移动布局。

现在它的工作正常。

在UserController中我有索引操作,它自动选择index.html.erb或index.mobile.erb。无需在移动视图顶部进行额外编码。成功。

但我有很多其他观点,可以使用相同的模板在移动布局中提供服务,但只需稍作修改。

E.g。在MessagesController中,相同的视图几乎可以用于移动

In index.html.erb
Normal user info, common for mobile and html
<%= render(:partial => 'messages') %>

渲染messages.html.erb,不需要messages.mobile.erb。移动视图可以使用css

完成
<%# By default self.formats = [:html] because this is .html.erb partial, even in mobile site %>
<%= self.formats = [:mobile, :html] if @mobile_site # How to get rid of this? %>
<%= render(:partial => 'vote_form') %>
<!-- rest of page ... -->

现在我想渲染一下vote_form。[mobile | html] .erb取决于网站......

现在,如果我可以在vote_form.html.erb或vote_form.mobile.erb之间进行选择,那么index.html.erb partial可以用于移动设备。我可以choose to use mobile partial使用 在index.html.erb的开头,“self.formats = [:mobile,:html] if @mobile_site”。但是在所有模板的开头写这个都感觉很愚蠢。

所以问题是:

  • self.formats在视图中的位置(最初设置的是什么)以及如何在移动网站中设置它始终是[:mobile,:html]?为什么它与我在控制器before_filter中设置的不一样?我可以在控制器中以某种方式设置它吗?

(小额奖金问题)

  • 这种方法有问题吗?使用大多数相同的html视图,在某些特定情况下使用mobile.erb视图。为什么默认情况下这不适用于rails?

  • 是否还有其他方式可以选择渲染:如果找到移动视图并回退到普通的html视图?甚至跨越部分,以便html.erb-view尝试使用_partial.mobile.erb partials。

5 个答案:

答案 0 :(得分:4)

我想我找到了最好的方法。我正在尝试与你相同的事情,但后来我记得在rails 3.1中引入了template inheritance,这正是我们需要这样的东西才能工作的。我真的不能对这个实现有多大的信任,因为它全部都是由Ryan Bates在railscasts链接中提出来的。

所以这基本上是这样的。

app/views中创建一个子目录。我标记了我的mobile

将要覆盖的所有视图模板嵌套在与views目录中相同的结构格式中。 views/posts/index.html.erb -> views/mobile/posts/index.html.erb

before_filter中创建一个Application_Controller并执行此操作。

 before_filter :prep_mobile
 def is_mobile?
   request.user_agent =~ /Mobile|webOS|iPhone/
 end 
 def prep_mobile
   prepend_view_path "app/views/mobile" if is_mobile?
 end

完成后,如果移动设备位于移动设备上,您的文件将默认为移动视图,如果移动设备不存在,则会回退到常规模板。

答案 1 :(得分:4)

  

如何在移动网站内设置始终为[:mobile,:html]?我可以在控制器中以某种方式设置它吗?

这是一个简单的解决方案,但它有点粗略。

class ApplicationController
    ...
    def formats=(values)
        values << :html if values == [:mobile]
        super(values)
    end
    ...
end

事实证明,Rails(3.2.11)已经为:js格式的请求添加了:html回退。这是ActionView::LookupContext#formats=

# Override formats= to expand ["*/*"] values and automatically
# add :html as fallback to :js.
def formats=(values)
  if values
    values.concat(default_formats) if values.delete "*/*"
    values << :html if values == [:js]
  end
  super(values)
end

因此,您可以自己覆盖格式,并且可以想象它不会比现有的Rails实现更糟糕和粗暴。

  

self.formats在视图中的位置(最初设置的是什么)

ActionController::Rendering#process_action从请求中分配格式数组(请参阅action_controller/metal/rendering.rb

# Before processing, set the request formats in current controller formats.
def process_action(*) #:nodoc:
  self.formats = request.formats.map { |x| x.ref }
  super
end
  

为什么它与我在控制器before_filter中设置的不一样?

它与您在before_filter中设置的内容不同,因为在 #process_action之前运行之前(正如您所期望的那样)。所以无论你设置什么都会被#process_action拉出请求所破坏。

答案 2 :(得分:3)

Rails 4.1包含一个非常简洁的功能:

  

<强>变体

     

允许您为相同的mime类型(例如,HTML)设置不同的模板和操作响应。对于任何为移动客户端提供服务的Rails应用程序而言,这是一个神奇的子弹。您现在可以拥有桌面,平板电脑和手机视图的各个模板,同时共享所有相同的控制器逻辑。

现在你可以这样做:

class PostController < ApplicationController
  def show
    @post = Post.find(params[:id])

    respond_to do |format|
      format.json
      format.html               # /app/views/posts/show.html.erb
      format.html.phone         # /app/views/posts/show.html+phone.erb
      format.html.tablet do
        @show_edit_link = false
      end
    end
  end
end

您只需根据需要设置变体,例如在before_filter

class ApplicationController < ActionController::Base
  before_action :detect_device_variant

  private

    def detect_device_variant
      case request.user_agent
      when /iPad/i
        request.variant = :tablet
      when /iPhone/i
        request.variant = :phone
      end
    end
end

What's new in Rails 4.1

答案 3 :(得分:2)

您可以在mime类型初始值设定项中注册整个应用程序的新格式:

 Mime::Type.register_alias "text/html", :mobile

现在,您可以在模板中执行以下操作来指定格式优先级(请参阅How do I render a partial of a different format in Rails?):

<% self.formats = [:mobile, :html] %>

在这种情况下,如果有移动模板,它将用于渲染回退到普通的html模板。现在,您应该只确定用户是否通过移动浏览器进行浏览并有条件地执行此代码。或者您可以在ApplicationController中分配格式值过滤器,这样就可以选择正确的模板自动

<强>更新

似乎此时没有“合法”的方法来使用Rails解决这个问题。以下是rails存储库中的unclosed issue。在那里你可以找到可以解决你的问题的补丁,但它使用私有的Rails API,所以它可能不稳定。

此外,您可以尝试实施可能解决问题的自己的视图解析器:http://jkfill.com/2011/03/11/implementing-a-rails-3-view-resolver/

答案 4 :(得分:0)

为什么不调查Responsive Web Design,使用CSS和媒体查询来呈现页面。这样你的控制器就不需要知道视图是否可移动。