Поддержание модели области при изменении между представлениями в AngularJS
Я изучаю AngularJS. Скажем, у меня есть /view 1 с помощью My1Ctrl и /view 2 с помощью My2Ctrl; которые могут быть перенесены на использование вкладок, где каждый вид имеет свою простую, но различную форму.
Как я могу убедиться, что значения, введенные в форме view1, не являются reset, когда пользователь уходит, а затем возвращается в view1?
Что я имею в виду, как второй визит в view1 сохранит то же самое состояние модели, что и я?
Ответы
Ответ 1
Я потратил немного времени на то, чтобы понять, что является лучшим способом сделать это. Я также хотел сохранить состояние, когда пользователь покидает страницу, а затем нажимает кнопку "Назад", чтобы вернуться к старой странице; и не просто поместить все мои данные в корнеплод.
Конечным результатом является наличие службы для каждого контроллера. В контроллере у вас есть только функции и переменные, которые вам не нужны, если они очищены.
Служба для контроллера вводится инъекцией зависимостей. Поскольку сервисы являются одиночными, их данные не уничтожаются, как данные в контроллере.
В службе у меня есть модель. модель ТОЛЬКО имеет данные - никаких функций -. Таким образом, он может быть преобразован из JSON взад и вперед, чтобы сохранить его. Я использовал html5 localstorage для сохранения.
Наконец, я использовал window.onbeforeunload
и $rootScope.$broadcast('saveState');
, чтобы все службы знали, что они должны сохранить свое состояние, и $rootScope.$broadcast('restoreState')
, чтобы сообщить им о восстановлении своего состояния (используется, когда пользователь покидает страницу и нажимает кнопку назад, чтобы вернуться на страницу соответственно).
Пример службы userService для моего 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>
И в модуле приложения в функции запуска я обрабатываю широковещание 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.
Мне бы очень хотелось видеть проще, но как чистые способы обработки состояния между контроллерами, в том числе, когда пользователь уходит и возвращается на страницу.
Ответ 2
Немного поздно для ответа, но только что обновленная скрипка с лучшей практикой
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;
}
}
Ответ 3
$rootScope - это большая глобальная переменная, которая подходит для одноразовых вещей или небольших приложений.
Используйте службу, если вы хотите инкапсулировать свою модель и/или поведение (и, возможно, повторно использовать ее в другом месте). В дополнение к сообщению группы google, упомянутому в OP, см. Также https://groups.google.com/d/topic/angular/eegk_lB6kVs/discussion.
Ответ 4
Angular на самом деле не обеспечивает то, что вы ищете из коробки. Что бы я сделал, чтобы выполнить то, что вам нужно, используйте следующие дополнения
UI Router и Дополнительные возможности интерфейса пользователя
Эти два предоставят вам состояние маршрутизации и липкие состояния на основе состояния, вы можете вставить вкладки между состояниями, и вся информация будет сохранена, так как область "остается в живых", так сказать.
Проверяйте документацию на обоих, так как это довольно прямолинейно. Утилиты ui router также имеют хорошую демонстрацию о том, как работают липкие состояния.
Ответ 5
У меня была такая же проблема. Это то, что я сделал:
У меня есть 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 и другие вещи).
Ответ 6
Альтернативой сервисам является использование хранилища значений.
В базе моего приложения я добавил это
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;
...
Ответ 7
Решение, которое будет работать для нескольких областей и нескольких переменных в этих областях
Эта услуга была основана на ответе Антона, но более расширяема и будет работать в нескольких областях и позволяет выбирать несколько переменных области видимости в одной области. Он использует маршрут маршрута для индексации каждой области, а затем имена переменных области, чтобы индексировать один уровень глубже.
Создать сервис с помощью этого кода:
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;
}]);
Добавьте этот код в свою функцию запуска в вашем модуле приложения:
$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: '[email protected]' },
{ name: 'another user name', email: '[email protected]' }
]);
}
В приведенном выше примере будет инициализироваться $scope.user сохраненному значению, в противном случае значение по умолчанию будет предоставлено и сохранено это значение. Если страница закрыта, обновлена или маршрут изменен, текущие значения всех зарегистрированных переменных области будут сохранены и будут восстановлены при следующем посещении маршрута/страницы.