在 AngularJS 中维护视图之间切换时的范围模型

我在学 AngularJS。假设我有使用 My1Ctrl/视图1和使用 My2Ctrl/视图2; 这可以导航到使用选项卡,其中每个视图都有自己简单但不同的形式。

当用户离开然后返回到 视图1时,如何确保以 视图1形式输入的值不被重置?

我的意思是,如何能够第二次访问 视图1保持完全相同的状态的模型,因为我离开了它?

113468 次浏览

$rootScope 是一个很大的全局变量,适用于一次性事件或小型应用程序。 如果您想要封装您的模型和/或行为(并且可能在其他地方重用它) ,请使用服务。除了谷歌组提到的 OP 后,也看到 https://groups.google.com/d/topic/angular/eegk_lB6kVs/discussion

我花了一点时间来找出做这件事的最佳方法。我还希望在用户离开页面然后按下返回按钮时保持状态,以返回到原来的页面; 而不仅仅是将我的所有数据放入 根窥镜

最终的结果是每个 控制器都有一个 服务。在控制器中,只有一些函数和变量,如果它们被清除的话,那么它们是不需要关心的。

控制器的服务由依赖注入注入。由于服务是单例的,它们的数据不会像控制器中的数据那样被销毁。

在军队里,我有个模特。模型只有数据-没有函数-。这样就可以从 JSON 来回转换以持久化它。我使用 html5本地存储来实现持久性。

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

为我的 UserController提供名为 用户服务的示例服务:

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>

应用程序模块中,在 run 函数中,我处理 SaveState恢复状态广播

$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');
};

正如我提到的,我们花了一段时间才走到这一步。这是一个非常干净的方式来做它,但它是一个公平的位工程做的东西,我会怀疑是一个非常普遍的问题时,在角度发展。

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

现在回答有点晚了,不过刚刚更新了一些最佳实践

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;
}
}

我也有同样的问题,这就是我所做的: 我有一个 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);
};
...
}]);

现在我可以从我的任何视图调用 update 函数,传递值和更新模型,我不需要使用 html5 apis 来保存数据(在我的例子中,也许在其他情况下需要使用 html5 apis,比如本地存储和其他东西)。

角度并不真正提供你正在寻找的盒子。为了达到你的目的,我会使用以下附加组件

UI 路由器附加功能

这两个程序将为您提供基于状态的路由和粘贴状态,您可以在状态之间设置选项卡,所有信息将被保存为范围“保持活动”,可以这么说。

检查两者的文档,因为它是相当直接的,ui 路由器附加也有一个很好的 示范的粘性状态如何工作。

可用于这些范围内的多个范围和多个变量的解决方案

这个服务基于 Anton 的回答,但是更具可扩展性,可以跨多个作用域工作,并允许在同一作用域中选择多个作用域变量。它使用路由路径对每个作用域进行索引,然后使用作用域变量名对更深一级进行索引。

使用以下代码创建服务:

angular.module('restoreScope', []).factory('restoreScope', ['$rootScope', '$route', function ($rootScope, $route) {


var getOrRegisterScopeVariable = function (scope, name, defaultValue, storedScope) {
if (storedScope[name] == null) {
storedScope[name] = defaultValue;
}
scope[name] = storedScope[name];
}


var service = {


GetOrRegisterScopeVariables: function (names, defaultValues) {
var scope = $route.current.locals.$scope;
var storedBaseScope = angular.fromJson(sessionStorage.restoreScope);
if (storedBaseScope == null) {
storedBaseScope = {};
}
// stored scope is indexed by route name
var storedScope = storedBaseScope[$route.current.$$route.originalPath];
if (storedScope == null) {
storedScope = {};
}
if (typeof names === "string") {
getOrRegisterScopeVariable(scope, names, defaultValues, storedScope);
} else if (Array.isArray(names)) {
angular.forEach(names, function (name, i) {
getOrRegisterScopeVariable(scope, name, defaultValues[i], storedScope);
});
} else {
console.error("First argument to GetOrRegisterScopeVariables is not a string or array");
}
// save stored scope back off
storedBaseScope[$route.current.$$route.originalPath] = storedScope;
sessionStorage.restoreScope = angular.toJson(storedBaseScope);
},


SaveState: function () {
// get current scope
var scope = $route.current.locals.$scope;
var storedBaseScope = angular.fromJson(sessionStorage.restoreScope);


// save off scope based on registered indexes
angular.forEach(storedBaseScope[$route.current.$$route.originalPath], function (item, i) {
storedBaseScope[$route.current.$$route.originalPath][i] = scope[i];
});


sessionStorage.restoreScope = angular.toJson(storedBaseScope);
}
}


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


return service;
}]);

将这段代码添加到应用程序模块的 run 函数中:

$rootScope.$on('$locationChangeStart', function (event, next, current) {
$rootScope.$broadcast('savestate');
});


window.onbeforeunload = function (event) {
$rootScope.$broadcast('savestate');
};

将 restoreScope 服务注入到控制器中(下面的示例) :

function My1Ctrl($scope, restoreScope) {
restoreScope.GetOrRegisterScopeVariables([
// scope variable name(s)
'user',
'anotherUser'
],[
// default value(s)
{ name: 'user name', email: 'user@website.com' },
{ name: 'another user name', email: 'anotherUser@website.com' }
]);
}

上面的示例将 $scope.user 初始化为存储值,否则将默认设置为提供的值并将其保存。如果页面被关闭、刷新或路由被更改,所有已注册范围变量的当前值将被保存,并在下次访问路由/页面时恢复。

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

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

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;
...

可以使用 $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>