2013-07-09 25 views
2

我正在开发一个带有RoR后端的AngularJS应用程序,并在使用多个布局时遇到问题。应用程序在将页面呈现给未经身份验证的用户时使用一种布局,并且在用户通过身份验证后更改为另一种布局。#!使用Angular/Rails与多个布局导致路由问题

布局是初始页面加载的服务器,由Rails管理。

class SampleController2 < ApplicationController 
    layout :current_layout 

    def current_layout 
    "anotherLayout" unless request.xhr? 
    end 
end 

这是用于控制器管理授权/非授权用户单独定义:对于不同的部

class SampleController < ApplicationController 
    layout :current_layout 

    def current_layout 
    "layout" unless request.xhr? 
    end 
end 

样品控制器:示出我们如何基于在路径上加载不同的布局一些示例代码,并基本提供适当的布局。当Angular进入画面时,我们使用XHR检查来防止路由循环。

因此,这在大多数浏览器中正常工作,但使用IE9时会中断。角落回到使用#!在IE9中的URL,所以Rails不知道要加载哪个控制器,因为哈希没有发送到后端。在这种情况下,Rails加载根和它的关联布局。如果认证部分设置为默认部分,那么即使对于未经认证的用户也会加载此布局,反之亦然。

所以基本上,我需要找到一种方法来使这个多布局应用程序正常工作,即使在不支持HTML5 pushState的浏览器中。我已经在这个地方检查了一个适当的解决方案,并且不能提出任何问题。

+0

做这些选项的任何一个为你工作? 1.将布局放在AngularJS控件下(在Angular模板中)。 2.一些用户登录后,某些应用会更改其根用户的URL以呈现其他特定于用户的登录页面。这可能会解决您的问题,因为登录用户在登录用户登录时会得到与登录用户不同的布局根网址。 – colllin

+0

@colllin ** 1。**根据我的研究,Angular目前还没有对多种布局提供适当的支持。我找到的最接近的东西是[Angular-UI Router](https://github.com/angular-ui/ui-router),但是无论如何这仍然会重新加载整个页面。 ** 2在完美的世界中,我可以完全控制它,但客户端不希望以任何方式更改根URL。 – godfrzero

+0

我想出了一种方法,通过稍微调整后端来实现它。一旦我有足够的时间打印出来,我会将其作为答案发布。 – godfrzero

回答

2

好的,所以在测试了客户端和服务器端的许多调整之后,我最终使用了以下解决方案。基本上,我只是停止暴露Angular到多个布局,并在Rails中处理。当然,这确实会对用户造成限制。

该应用程序分为两个主要部分:仪表板验证。因此,在后端,我们限制用户访问任何身份验证页面(如果他们已登录),并且显然仪表板页面只能在用户通过身份验证时才能访问。

问题的根源在于,Rails无法知道要提供哪种布局,是否应该提供布局。我们使用cookies来区分经过身份验证的用户和未经身份验证的用户。如果cookie已设置,并且它通过了真实性测试,则应该加载仪表板布局。否则,尝试访问仪表盘的任何用户将被重定向到识别部分:

class DashboardController < ApplicationController 

    layout :current_layout 

    before_filter :authenticate_user 

    def authenticate_user 
    if request.xhr? && !cookies[:access_token] 
     redirect_to "/login" 
    end 
    end 

    def current_layout 
    if cookies[:access_token] 
     "dashboard" unless request.xhr? 
    else 
     "application" unless request.xhr? 
    end 
    end 
end 

同样为验证部件:这里要注意

class AuthenticationController < ApplicationController 

    layout :current_layout 

    before_filter :redirect_if_authenticated 

    def redirect_if_authenticated 
    if request.xhr? && cookies[:access_token] 
     redirect_to "/dashboard" 
    end 
    end 

    def current_layout 
    if cookies[:access_token] 
     "dashboard" unless request.xhr? 
    else 
     "application" unless request.xhr? 
    end 
    end 
end 

所以要点:

  • 如果请求是XHR,请不要再次提供布局。再次使用布局会导致IE9中的无限加载循环,也可能在其他IE版本中。

  • 由于我们没有在服务器端获取URL碎片,我们无法知道应该加载哪个布局。所以我们使用cookies作为真相的来源,并且仅基于此来控制访问。

  • 这会在客户端引入一些不对称情况:在某些情况下,如果用户在另一节中输入URL,则url和布局将保留。由于我们无法根据服务器的URL进行任何操作,并且由于Cookie在此情况下保持不变,因此必须由Angular处理。

对于最后一点,在我的情况,验证部件只有几个可能的网址,所以我只是重定向,如果我有一个正则表达式匹配如下:

if(Modernizr.history) { 
    if(!/(login|sign|pass)/.test(location.pathname)) { 
     $location.path('/'); 
    } 
} else { 
    if(location.hash.length > 3 && !/(login|sign|pass)/.test(location.hash)) { 
     $window.location.href = '/'; 
    } 
} 

检查的history API在这里,因为在较旧的IE浏览器(我猜其他较旧的浏览器?),$location.path('/')没有一直重新加载整个页面。当使用这一点,$routeProvider配置也应该有条件地加载的布局,对于我们重定向到根/情况:

$routeProvider 
// Code for routes 
.otherwise({ 
    // Of course, add something to check access_token authenticity here as well :P 
    redirectTo: $.cookie('access_token') ? "/dashboard" : "/login" 
}); 

因此,这些夫妇的调整,应用程序在所有浏览器中正常工作,并与多种布局功能。当然,我对RoR一无所知,所以我希望有人会有一些改进建议,或更好的答案。 ;)