2014-01-28 48 views
8

两个问题已经困扰我,因为我已经学会角:如何处理清爽页面与AngularJS单页应用

  1. 如何恢复状态,当用户刷新页面或者点击后退按钮?

  2. 如何在属于不同控制器的范围之间共享数据?

下面我展示了一个使用客户端会话存储的简单解决方案。它允许共同两个数据共享和状态自动恢复用户刷新页面或者点击后退按钮后。

注意:下面的方案就证明了必须回答以下问题:

How do I get the Back Button to work with an AngularJS ui-router state machine?

回答

5

的解决方案取决于如下所示的SessionService类。语法是coffeescript。

SessionService类

class SessionService 
    scopes:[] 

    setStorage:(key, value) -> 
     scope[key] = value for scope in @scopes 
     value = if value is undefined then null else JSON.stringify value 
     sessionStorage.setItem key, value 

    getStorage:(key)-> 
     sessionValue = sessionStorage.getItem key 
     if sessionValue == "undefined" 
      return null 
     JSON.parse sessionValue 

    register:(scope)-> 
     for key, value of sessionStorage 
      scope[key] = if value? and value != "undefined" then JSON.parse(value) else null 
     @scopes.push scope 
     scope.$on '$destroy', => 
      @scopes = @scopes.filter (s) -> s.$id != scope.$id 

    clear: -> 
     @setStorage(key, null) for key of sessionStorage 

    isAuthenticated: -> 
     @accessor 'isAuthenticated', value 

    user:(value=null) -> 
     @accessor 'user', value 

    # other storage items go here 

    accessor:(name, value)-> 
     return @getStorage name unless value? 
     @setStorage name, value 

angular 
.module 'app.Services' 
.service 'sessionService', SessionService 

SessionService类定义了isAuthenticated属性(简单布尔)和user属性(一个复杂的对象)。这些属性的值是自动字符串化/解析为它们被存储/使用客户端侧由JavaScript提供本地sessionStorage对象检索。

你加更多的属性需要。和$rootScope一样,你要谨慎地添加属性。不像$rootScope属性值仍刷新页面或后退按钮点击后可用。

该服务允许用它来注册的任何数量的作用域。当一个范围被登记在sessionStorage所有存储的值被自动分配给该范围。通过这种方式,所有注册的范围始终可以访问所有会话属性。

当属性值被更新,所有注册的范围有其相应的值更新。

当角销毁一个范围内,可以自动地从注册范围的列表中删除以节省浪费资源。

如果用户刷新页面或者点击后退按钮,然后角应用被强制重新启动。通常这意味着你将不得不重建你的当前状态。该SessionService自动为您完成此,因为每个范围将是从本地存储恢复它的价值时,他们的应用程序初始化期间注册。

所以,现在很容易解决范围之间的两个数据共享的问题,以及当用户刷新或点击后退按钮恢复值。

下面是一些示例角度代码,显示如何使用SessionService类。

在某些控制器注册与SessionService一个范围

angular 
.module 'app' 
.controller 'mainCtrl', ($scope, $state, session, security) -> 
    #register the scope with the session service 
    session.register $scope 

    #hook up the 'login' method (see security service) 
    $scope.login = security.login 

    # check the value of a session property 
    # it may well be true if the page has been refreshed 
    if session.isAuthenticated 
     $state.go('home') 
    else 
     $state.go('login') 

在服务

class SecurityService 
    @$inject:['$http','sessionService', 'api'] 
    constructor:(@http, @session, @api) -> 

    login:(username, password) => 
     @http.get "#{@api.base}/security/login/credentials/#{username}/#{password}" 
     .success (user)=> 
      @session.isAuthenticated = true 
      @session.user = user 
     .error (ex)=> 
      # process error 

angular 
.module 'app' 
.service 'securityService', SecurityService 

在UI(玉模板)使用Session值

设置会话值
div(ng-show="isAuthenticated") 
    div Hello {{user.Name}} 
+1

有没有这个非咖啡标签版本?这也是唯一可行的解​​决方案吗?谢谢! – EKet

+1

您可以将coffeescript转换为javascript,然后再回到这里:http://js2coffee.org/。一定会有其他更好的解决方案,起初我认为必须有一种简单,有角度的方式来处理诸如后退/刷新按钮之类的常见事情。但是我找不到它,所以你在这里看到的是我最好的,最好的,使它的工作。我希望有人发布更好的解决方案。 – biofractal

+18

+1为'为什么人们发布咖啡角标解决方案?' – edzillion

0

我面临同样的问题,并选择使用角度曲奇,因为 只有状态不是由模板通过ng-init拉动的是登录用户 状态。

我从我们的服务器收到用户 模型后,在登录时将用户ID存储在cookie中,并在注销时清除用户ID cookie。然后在页面刷新或返回按钮事件中恢复登录的用户状态 ,我钩了 $location服务的$locationChangeStart事件。从我的实验中,这个 事件在位置即将更改但在部分/模板已加载之前触发。这允许及时加载所需状态 。

我不相信,我没有竞争状态这里 $scope.loadLoggedInUser(...)采用非同步$ HTTP加载所需的状态,但到目前为止 它可靠地为我工作。

$scope.$on('$locationChangeStart', function() { 
      $log.debug("locationChangeStart"); 
      if (!$scope.appCtx.models.loggedInUser) { 
       var userID = $cookies.get("userID"); 
       if (!userID) { 
        $scope.doLogout(); 
        return; 
       } 
       $scope.loadLoggedInUser(userID, true); 
      } 
     });