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

我正在我们正常的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。 无需在移动视图顶部进行额外编码。 成功。

但我有很多其他观点,其中相同的模板可用于在移动布局内提供但稍有变化。

例如,在MessagesController中,相同的视图几乎可以用于移动设备

 In index.html.erb Normal user info, common for mobile and html  'messages') %> 

呈现messages.html.erb,不需要messages.mobile.erb。 移动视图可以用css完成

    'vote_form') %>  

现在我要渲染vote_form。[mobile | html] .erb取决于网站…

现在,如果我可以在vote_form.html.erb或vote_form.mobile.erb之间进行选择,那么index.html.erb 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部分。

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

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

现在,您可以在模板中执行类似的操作以指定格式优先级(请参阅如何在Rails中呈现不同格式的部分内容? ):

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

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

更新:

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

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

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

所以这基本上是这样的。

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

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

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

  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 

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

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

是。

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

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

事实certificate,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之前运行filter(正如您所期望的那样)。 所以无论你设置什么都会被#process_action拉出请求所破坏。

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

变种

允许您对同一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 

Rails 4.1中有什么新function

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