Поддержание модели области при изменении между представлениями в 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 сохраненному значению, в противном случае значение по умолчанию будет предоставлено и сохранено это значение. Если страница закрыта, обновлена ​​или маршрут изменен, текущие значения всех зарегистрированных переменных области будут сохранены и будут восстановлены при следующем посещении маршрута/страницы.