在AngularJS中的视图之间切换时保持范围模型

时间:2012-10-17 18:34:33

标签: javascript angularjs

我正在学习AngularJS。假设我使用 My1Ctrl / view1 ,使用 My2Ctrl / view2 ;可以导航到使用制表符,其中每个视图都有自己的简单但不同的形式。

当用户离开然后返回 view1 时,如何确保以 view1 的形式输入的值不会重置?

我的意思是,第二次访问 view1 如何保持与我离开时模型完全相同的状态?

7 个答案:

答案 0 :(得分:171)

我花了一些时间来研究这样做的最佳方法。我还希望保持状态,当用户离开页面然后按下后退按钮,返回到旧页面;而不只是将我的所有数据都放入 rootscope

最终结果是为每个控制器提供服务。在控制器中,如果它们被清除,你只需要不关心的函数和变量。

通过依赖注入注入控制器的服务。由于服务是单例,因此它们的数据不会像控制器中的数据那样被破坏。

在服务中,我有一个模特。该模型只有数据 - 没有功能 - 。这样它就可以从JSON来回转换以保持它。我使用html5 localstorage进行持久化。

最后,我使用window.onbeforeunload$rootScope.$broadcast('saveState');让所有服务都知道他们应该保存自己的状态,$rootScope.$broadcast('restoreState')让他们知道恢复状态(用于用户时)离开页面并按下后退按钮分别返回页面。

我的 userController 名为 userService 的服务示例:

app.factory('userService', ['$rootScope', function ($rootScope) {

    var service = {

        model: {
            name: '',
            email: ''
        },

        SaveState: function () {
            sessionStorage.userService = angular.toJson(service.model);
        },

        RestoreState: function () {
            service.model = angular.fromJson(sessionStorage.userService);
        }
    }

    $rootScope.$on("savestate", service.SaveState);
    $rootScope.$on("restorestate", service.RestoreState);

    return service;
}]);

userController 示例

function userCtrl($scope, userService) {
    $scope.user = userService;
}

视图然后使用这样的绑定

<h1>{{user.model.name}}</h1>

app模块中,在运行功能中,我处理 saveState restoreState 广播

$rootScope.$on("$routeChangeStart", function (event, next, current) {
    if (sessionStorage.restorestate == "true") {
        $rootScope.$broadcast('restorestate'); //let everything know we need to restore state
        sessionStorage.restorestate = false;
    }
});

//let everthing know that we need to save state now.
window.onbeforeunload = function (event) {
    $rootScope.$broadcast('savestate');
};

正如我所提到的,这需要一段时间才能实现。这是一种非常干净的方式,但是在Angular中进行开发时,我怀疑这是一个非常常见的问题,这是一项相当的工程设计。

我希望看到更简单,但作为处理控制器状态的干净方法,包括用户离开并返回页面时。

答案 1 :(得分:22)

答案有点迟,但只是更新了一些最佳实践

jsfiddle

var myApp = angular.module('myApp',[]);
myApp.factory('UserService', function() {
    var userService = {};

    userService.name = "HI Atul";

    userService.ChangeName = function (value) {

       userService.name = value;
    };

    return userService;
});

function MyCtrl($scope, UserService) {
    $scope.name = UserService.name;
    $scope.updatedname="";
    $scope.changeName=function(data){
        $scope.updateServiceName(data);
    }
    $scope.updateServiceName = function(name){
        UserService.ChangeName(name);
        $scope.name = UserService.name;
    }
}

答案 2 :(得分:10)

$ rootScope是一个很大的全局变量,适用于一次性事物或小型应用程序。 如果要封装模型和/或行为(并可能在其他地方重复使用),请使用服务。除了提到OP的google小组帖子之外,另请参阅https://groups.google.com/d/topic/angular/eegk_lB6kVs/discussion

答案 3 :(得分:8)

Angular并不能真正提供你想要的东西。我要做的就是使用以下附加内容

来完成你所追求的目标

UI Router&amp; UI Router Extras

这两个将为您提供基于状态的路由和粘滞状态,您可以在状态之间切换,所有信息都将保存为“保持活跃”范围。

检查两者的文档,因为它非常简单,ui路由器附加功能也有一个很好的demonstration粘性状态的工作原理。

答案 4 :(得分:6)

我有同样的问题,这就是我做的: 我在同一页面中有一个包含多个视图的SPA(没有ajax),所以这是模块的代码:

var app = angular.module('otisApp', ['chieffancypants.loadingBar', 'ngRoute']);

app.config(['$routeProvider', function($routeProvider){
    $routeProvider.when('/:page', {
        templateUrl: function(page){return page.page + '.html';},
        controller:'otisCtrl'
    })
    .otherwise({redirectTo:'/otis'});
}]);

我只有一个控制器用于所有视图,但是,问题与问题相同,控制器总是刷新数据,为了避免这种行为,我做了上面人们建议的,我为此创建了一个服务,然后按如下方式将其传递给控制器​​:

app.factory('otisService', function($http){
    var service = {            
        answers:[],
        ...

    }        
    return service;
});

app.controller('otisCtrl', ['$scope', '$window', 'otisService', '$routeParams',  
function($scope, $window, otisService, $routeParams){        
    $scope.message = "Hello from page: " + $routeParams.page;
    $scope.update = function(answer){
        otisService.answers.push(answers);
    };
    ...
}]);

现在我可以从任何视图调用更新函数,传递值并更新我的模型,我没有必要使用html5 apis来存储持久性数据(这在我的情况下,可能在其他情况下会有必要使用html5 apis,如localstorage和其他东西)。

答案 5 :(得分:2)

服务的替代方法是使用值存储。

在我的应用程序的基础上,我添加了这个

var agentApp = angular.module('rbAgent', ['ui.router', 'rbApp.tryGoal', 'rbApp.tryGoal.service', 'ui.bootstrap']);

agentApp.value('agentMemory',
    {
        contextId: '',
        sessionId: ''
    }
);
...

然后在我的控制器中我只引用了值存储。如果用户关闭浏览器,我不认为这是件事。

angular.module('rbAgent')
.controller('AgentGoalListController', ['agentMemory', '$scope', '$rootScope', 'config', '$state', function(agentMemory, $scope, $rootScope, config, $state){

$scope.config = config;
$scope.contextId = agentMemory.contextId;
...

答案 6 :(得分:1)

您可以使用$locationChangeStart事件将先前的值存储在$rootScope或服务中。当您回来时,只需初始化所有先前存储的值即可。这是使用$rootScope的快速演示。

enter image description here

var app = angular.module("myApp", ["ngRoute"]);
app.controller("tab1Ctrl", function($scope, $rootScope) {
    if ($rootScope.savedScopes) {
        for (key in $rootScope.savedScopes) {
            $scope[key] = $rootScope.savedScopes[key];
        }
    }
    $scope.$on('$locationChangeStart', function(event, next, current) {
        $rootScope.savedScopes = {
            name: $scope.name,
            age: $scope.age
        };
    });
});
app.controller("tab2Ctrl", function($scope) {
    $scope.language = "English";
});
app.config(function($routeProvider) {
    $routeProvider
        .when("/", {
            template: "<h2>Tab1 content</h2>Name: <input ng-model='name'/><br/><br/>Age: <input type='number' ng-model='age' /><h4 style='color: red'>Fill the details and click on Tab2</h4>",
            controller: "tab1Ctrl"
        })
        .when("/tab2", {
            template: "<h2>Tab2 content</h2> My language: {{language}}<h4 style='color: red'>Now go back to Tab1</h4>",
            controller: "tab2Ctrl"
        });
});
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular-route.js"></script>
<body ng-app="myApp">
    <a href="#/!">Tab1</a>
    <a href="#!tab2">Tab2</a>
    <div ng-view></div>
</body>
</html>