AngularJS에서 뷰 간을 변경할 때 범위 모델 유지


152

AngularJS를 배우고 있습니다. My1Ctrl을 사용하는 / view1 , My2Ctrl을 사용하는 / view2 가 있다고 가정 해 봅시다 . 각보기마다 간단하지만 다른 형식이있는 탭을 사용하여 탐색 할 수 있습니다.

사용자가 떠나서 view1 로 돌아올 때 view1 형식으로 입력 한 값 이 재설정되지 않도록하려면 어떻게해야합니까?

내 말은, view1에 대한 두 번째 방문은 어떻게 모델을 떠났을 때와 정확히 동일한 상태를 유지할 수 있습니까?

답변:


174

이 작업을 수행하는 가장 좋은 방법은 무엇입니까? 또한 사용자가 페이지를 떠난 후 뒤로 버튼을 눌러 이전 페이지로 돌아갈 때 상태를 유지하고 싶었습니다. 내 모든 데이터를 루트 스코프에 넣지 않고 .

최종 결과는 각 컨트롤러에 서비스제공하는 것 입니다 입니다. 컨트롤러에는 신경 쓰지 않는 함수와 변수가 있습니다.

컨트롤러의 서비스는 의존성 주입에 의해 주입됩니다. 서비스는 싱글 톤이므로 데이터는 컨트롤러의 데이터처럼 파괴되지 않습니다.

서비스에는 모델이 있습니다. 모델에만 데이터가 있으며 기능은 없습니다. 그렇게하면 JSON에서 앞뒤로 변환하여 유지할 수 있습니다. 지속성을 위해 html5 로컬 저장소를 사용했습니다.

마지막으로 내가 사용 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>

그리고 앱 모듈 에서 실행 기능 내 에서 saveStaterestoreState브로드 캐스트 를 처리합니다.

$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에서 개발할 때 매우 일반적인 문제라고 생각하는 것을 수행하는 것은 약간의 엔지니어링입니다.

더 쉽게보고 싶지만 사용자가 페이지를 떠날 때를 포함하여 컨트롤러의 상태 유지를 처리하는 깨끗한 방법입니다.


10
경로가 변경 될 때 페이지를 유지하는 방법이 아니라 페이지를 새로 고칠 때 데이터를 유지하는 방법에 대해 설명하므로 질문에 대답하지 않습니다. 또한 경로를 사용하지 않는 앱에서는 작동하지 않습니다. 또한 sessionStorage.restoreState로 설정된 위치 는 표시되지 않습니다 "true". 페이지를 새로 고칠 때 데이터를 유지하는 올바른 솔루션을 보려면 여기를 참조 하십시오 . 경로가 변경 될 때 데이터를 유지하려면 carloscarcamo의 답변을 확인하십시오. 데이터를 서비스에 저장하기 만하면됩니다.
Hugo Wood

2
그것은 당신이 제안한 것과 똑같은 방식으로 여러 뷰에서 지속되며 서비스에 저장됩니다. 흥미롭게도, 그것은 창에서 제안하는 것과 같은 방식으로 페이지 변경에도 지속됩니다. 그래도 다시 확인 해 주셔서 감사합니다. 이 대답은 1 년이 넘었으며 여전히 각도로 일을하는 가장 간단한 방법 인 것 같습니다.
Anton

오래되었지만 여기로 돌아와 주셔서 감사합니다. :). 솔루션이 뷰와 페이지를 모두 다루고 있지만 어떤 코드가 어떤 목적을 달성하는지 설명하지 못하고 사용하기에 충분하지는 않습니다 . 나는 단지 독자들에게 더 이상의 질문 을 피하도록 경고 했다 . 답변을 개선하기 위해 수정해야 할 시간이 있다면 좋을 것입니다.
Hugo Wood

대신이의 컨트롤러에서 만약에 : $scope.user = userService;당신은이 같은 있습니다 $scope.name = userService.model.name; $scope.email = userService.model.email;. 작동하거나 뷰에서 변수를 변경해도 서비스에서 변경되지 않습니까?
스포츠

2
그 각도가 내장 된 메커니즘과 같은 일반적인 일을 위해 부족 생각
obe6

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

페이지 탐색 (페이지 새로 고침이 아님) 사이 에서도이 작업을 수행해야합니다. 이 올바른지?
FutuToad

이 답변을 완성하기 위해 부분을 추가해야한다고 생각 updateService합니다. 컨트롤러가 파괴 될 때 호출되어야합니다. – $scope.$on('$destroy', function() { console.log('Ctrl destroyed'); $scope.updateServiceName($scope.name); });
Shivendra


8

Angular는 실제로 당신이 찾고있는 것을 제공하지 않습니다. 내가 한 것을 성취하기 위해 할 일은 다음 애드온을 사용하는 것입니다.

UI 라우터 UI 라우터 추가 사항

이 둘은 상태 기반 라우팅 및 고정 상태를 제공하며 상태 간을 탭할 수 있으며 모든 정보는 "유지 상태"범위로 저장됩니다.

UI 라우터 엑스트라는 또한 끈적 끈적한 상태의 작동 방식을 잘 보여줍니다 .


문서를 읽었지만 사용자가 브라우저 뒤로 버튼을 클릭 할 때 양식 입력을 유지하는 방법을 찾을 수 없습니다. 구체적인 사항을 알려 주시겠습니까? 감사!
Dalibor

6

나는 같은 문제를 겪었다. 이것은 내가 한 일이다 : 나는 동일한 페이지에 여러 개의 뷰를 가진 SPA를 가지고있다 (아약스없이), 이것은 모듈의 코드이다 :

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 API를 사용할 필요가 없습니다 (이 경우 내 경우에는 다른 경우에는 html5를 사용해야 할 수도 있습니다) 로컬 스토리지 및 기타 자료와 같은 API).


예, 매력처럼 일했습니다. 우리의 데이터 팩토리는 이미 코딩되었으며 ControllerAs 구문을 사용하고 있었으므로 $ scope 컨트롤러에 다시 작성하여 Angular가 제어 할 수있었습니다. 구현은 매우 간단했습니다. 이것은 내가 쓴 첫 번째 각도 앱입니다. tks
egidiocs

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

1

여러 범위 및 해당 범위 내의 여러 변수에 사용할 수있는 솔루션

이 서비스는 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;
}]);

이 코드를 앱 모듈의 실행 기능에 추가하십시오.

$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를 저장된 값으로 초기화합니다. 그렇지 않으면 제공된 값으로 기본 설정되어 저장됩니다. 페이지를 닫거나 새로 고치거나 경로를 변경하면 등록 된 모든 범위 변수의 현재 값이 저장되고 다음에 경로 / 페이지를 방문 할 때 복원됩니다.


이것을 코드에 추가하면 "TypeError : undefined의 'locals'속성을 읽을 수 없습니다"라는 오류가 발생합니다. 어떻게 해결합니까? @brett
Michael Mahony

Angular를 사용하여 경로를 처리하고 있습니까? $ route.current가 정의되지 않은 것으로 보이므로 "아니오"라고 생각합니다.
Brett Pennings

예, 각도는 내 라우팅을 처리하고 있습니다. 다음은 라우팅에 포함 된 샘플입니다. JBenchApp.config ([ '$ routeProvider', '$ locationProvider', function ($ routeProvider, $ locationProvider) {$ routeProvider. when ( '/ dashboard', {templateUrl : ' partials / dashboard.html ', 컨트롤러 :'JBenchCtrl '})
Michael Mahony

내 유일한 다른 추측은 앱이 구성되기 전에 컨트롤러가 실행되고 있다는 것입니다. 어느 쪽이든, 아마도 $ route 대신 $ location을 사용하고 경로에서 액세스하는 대신 컨트롤러에서 $ scope를 전달하여 문제를 해결할 수 있습니다. 사용해보십시오 gist.github.com/brettpennings/ae5d34de0961eef010c6를 대신 서비스 및 컨트롤러에 restoreScope.GetOrRegisterScopeVariables의 세 번째 매개 변수로 $ 범위에 전달합니다. 이것이 당신을 위해 효과가 있다면 나는 단지 새로운 대답을 할 수 있습니다. 행운을 빕니다!
Brett Pennings

1

$locationChangeStart이벤트를 사용 $rootScope하여 서비스 에 또는 서비스에 이전 값을 저장할 수 있습니다 . 돌아 오면 이전에 저장된 모든 값을 초기화하십시오. 다음은을 사용한 빠른 데모 $rootScope입니다.

여기에 이미지 설명을 입력하십시오

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>

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.