Behoud het model van het bereik bij het wisselen tussen weergaven in AngularJS

Ik ben AngularJS aan het leren. Laten we zeggen dat ik /view1heb met My1Ctrlen /view2met My2Ctrl; waarnaar kan worden genavigeerd met behulp van tabbladen waar elke weergave zijn eigen eenvoudige, maar andere vorm heeft.

Hoe kan ik ervoor zorgen dat de waarden die zijn ingevoerd in de vorm van view1niet opnieuw worden ingesteld wanneer een gebruiker vertrekt en vervolgens terugkeert naar view1?

Wat ik bedoel is, hoe kan het tweede bezoek aan view1exact dezelfde staat van het model behouden als ik het achterliet?


Antwoord 1, autoriteit 100%

Ik heb even de tijd genomen om uit te zoeken wat de beste manier is om dit te doen. Ik wilde ook de status behouden, wanneer de gebruiker de pagina verlaat en vervolgens op de terugknop drukt, om terug te gaan naar de oude pagina; en niet alleen al mijn gegevens in de rootscopestoppen.

Het uiteindelijke resultaat is om een ​​servicete hebben voor elke controller. In de controller heb je alleen functies en variabelen waar je niet om geeft, als ze worden gewist.

De service voor de controller wordt geïnjecteerd door afhankelijkheidsinjectie. Aangezien diensten singletons zijn, worden hun gegevens niet vernietigd zoals de gegevens in de controller.

In de dienst heb ik een model. het model heeft ALLEEN data – geen functies -. Op die manier kan het heen en weer worden geconverteerd vanuit JSON om het te behouden. Ik gebruikte de html5 localstorage voor persistentie.

Ten slotte heb ik window.onbeforeunloaden $rootScope.$broadcast('saveState');gebruikt om alle services te laten weten dat ze hun status moeten opslaan, en $rootScope.$broadcast('restoreState')om hen te laten weten dat ze hun status moeten herstellen (wordt gebruikt voor wanneer de gebruiker de pagina verlaat en op de terug-knop drukt om respectievelijk naar de pagina terug te keren).

Voorbeeldservice genaamd userServicevoor mijn 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;
}]);

userControllervoorbeeld

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

De weergave gebruikt dan binding zoals deze

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

En in de app-module, binnen de run-functie, verwerk ik de uitzendingvan de saveStateen 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');
};

Zoals ik al zei, duurde het even voordat het zover was. Het is een zeer schone manier om het te doen, maar het is nogal wat engineering om iets te doen waarvan ik vermoed dat het een veel voorkomend probleem is bij het ontwikkelen in Angular.

Ik zou graag eenvoudigere, maar schone manieren zien om de status van alle controllers bij te houden, ook wanneer de gebruiker de pagina verlaat en terugkeert naar de pagina.


Antwoord 2, autoriteit 13%

Een beetje laat voor een antwoord, maar ik heb zojuist de viool geüpdatet met wat praktische tips

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

Antwoord 3, autoriteit 6%

$rootScope is een grote globale variabele, wat prima is voor eenmalige dingen of kleine apps.
Gebruik een dienst als je je model en/of gedrag wilt inkapselen (en eventueel elders wilt hergebruiken). Zie naast de google group post de genoemde OP ook https://groups.google.com/d /topic/angular/eegk_lB6kVs/discussion.


Antwoord 4, autoriteit 5%

Angular biedt niet direct wat u zoekt. Wat ik zou doen om te bereiken wat u zoekt, is de volgende add-ons gebruiken

UI-router& UI-router-extra’s

Deze twee zullen je voorzien van op staat gebaseerde routering en plakkerige toestanden, je kunt tussen toestanden taben en alle informatie zal worden opgeslagen als het bereik “blijft in leven” om zo te zeggen.

Bekijk de documentatie over beide, want het is vrij eenvoudig, ui router extra’s heeft ook een goede demonstratievan hoe plakkerige toestanden werken.


Antwoord 5, autoriteit 3%

Ik had hetzelfde probleem. Dit is wat ik deed:
Ik heb een SPA met meerdere views op dezelfde pagina (zonder ajax), dus dit is de code van de module:

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

Ik heb maar één controller voor alle weergaven, maar het probleem is hetzelfde als de vraag, de controller ververst altijd gegevens, om dit gedrag te voorkomen, deed ik wat mensen hierboven suggereren en ik heb voor dat doel een service gemaakt, geef het dan als volgt door aan de controller:

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);
    };
    ...
}]);

Nu kan ik de update-functie vanuit elk van mijn weergaven oproepen, waarden doorgeven en mijn model bijwerken, ik heb geen html5-apis hoeven te gebruiken voor persistentiegegevens (dit is in mijn geval, misschien zou dat in andere gevallen nodig zijn om html5 api’s zoals localstorage en andere dingen te gebruiken).


Antwoord 6

Een alternatief voor services is het gebruik van de waardeopslag.

In de basis van mijn app heb ik dit toegevoegd

var agentApp = angular.module('rbAgent', ['ui.router', 'rbApp.tryGoal', 'rbApp.tryGoal.service', 'ui.bootstrap']);
agentApp.value('agentMemory',
    {
        contextId: '',
        sessionId: ''
    }
);
...

En dan verwijs ik in mijn controller naar de waardeopslag. Ik denk niet dat het van belang is als de gebruiker de browser sluit.

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

Antwoord 7

Oplossing die werkt voor meerdere bereiken en meerdere variabelen binnen die bereiken

Deze service was gebaseerd op het antwoord van Anton, maar is meer uitbreidbaar en werkt over meerdere scopes en maakt de selectie van meerdere scopevariabelen in dezelfde scope mogelijk. Het gebruikt het routepad om elk bereik te indexeren, en vervolgens de namen van de bereikvariabelen om een ​​niveau dieper te indexeren.

Service maken met deze code:

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

Voeg deze code toe aan uw run-functie in uw app-module:

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

Injecteer de restoreScope-service in uw controller (voorbeeld hieronder):

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

Het bovenstaande voorbeeld initialiseert $ scope.user naar de opgeslagen waarde, anders zal de standaardwaarde zijn en opslaan. Als de pagina is gesloten, vernieuwd of de route wordt gewijzigd, worden de huidige waarden van alle geregistreerde scope-variabelen opgeslagen en worden ze opnieuw gerestaureerd en de volgende keer dat de route / pagina is bezocht.

Other episodes