Comment puis-je gérer le rafraîchissement de la page avec une application D'une Page unique AngularJS?
deux problèmes m'ont troublé comme j'ai appris l'angulaire:
-
comment restaurer l'état lorsque l'utilisateur rafraîchit la page ou appuie sur le bouton Précédent?
-
comment partager des données entre des portées appartenant à des contrôleurs différents?
ci-dessous, je montre une solution simple qui fait usage de côté client stockage de la session. Il permet à la fois le partage de données communes et la restauration automatique de l'état après un utilisateur rafraîchit la page ou appuie sur le bouton Précédent.
Note: la solution ci-dessous s'est révélée essentielle pour répondre à la question suivante:
Comment faire pour que le bouton arrière fonctionne avec une machine D'état AngularJS ui-router?
3 réponses
la solution dépend de la classe SessionService
indiquée ci-dessous. La syntaxe est en coffescript.
SessionService Classe
class SessionService
scopes:[]
setStorage:(key, value) ->
scope[key] = value for scope in @scopes
value = if value is undefined then null else JSON.stringify value
sessionStorage.setItem key, value
getStorage:(key)->
sessionValue = sessionStorage.getItem key
if sessionValue == "undefined"
return null
JSON.parse sessionValue
register:(scope)->
for key, value of sessionStorage
scope[key] = if value? and value != "undefined" then JSON.parse(value) else null
@scopes.push scope
scope.$on '$destroy', =>
@scopes = @scopes.filter (s) -> s.$id != scope.$id
clear: ->
@setStorage(key, null) for key of sessionStorage
isAuthenticated: ->
@accessor 'isAuthenticated', value
user:(value=null) ->
@accessor 'user', value
# other storage items go here
accessor:(name, value)->
return @getStorage name unless value?
@setStorage name, value
angular
.module 'app.Services'
.service 'sessionService', SessionService
la catégorie SessionService
définit la propriété isAuthenticated
(simple bool) et la propriété user
(un objet complexe) . Les valeurs de ces propriétés sont automatiquement stringifiées / parsed comme ils sont stockés / récupérés en utilisant le côté client local sessionStorage
objet fourni par javascript.
vous ajoutez plus de propriétés comme requis . Comme $rootScope
vous ajoutez des propriétés avec parcimonie. Contrairement à $rootScope
les valeurs des propriétés sont toujours disponibles après un rafraîchissement de page ou un clic de bouton de retour.
le service permet à n'importe quel nombre de portées d'être enregistré avec lui. Lorsqu'un champ est enregistré toutes les valeurs stockées dans sessionStorage
sont automatiquement affectés à cette portée. De cette façon, tous les portées enregistrées ont toujours accès à toutes les propriétés de la session.
Lorsqu'une valeur de propriété est mise à jour, toutes les portées enregistrées ont leurs valeurs correspondantes mises à jour.
quand angular détruit un scope, il est automatiquement retiré de la liste des scopes enregistrés pour éviter de gaspiller des ressources.
si un utilisateur rafraîchit la page ou appuie sur le bouton précédent, alors l'application angulaire est forcée de redémarrer. Normalement, ce signifierait que vous auriez à reconstruire votre état actuel. Le SessionService
le fait pour vous automatiquement car chaque scope aura ses valeurs restaurées à partir du stockage local quand elles sont enregistrées lors de l'initialisation de l'application.
donc maintenant il est facile de résoudre le problème du partage des données entre les portées ainsi que la restauration des valeurs lorsque l'utilisateur rafraîchit ou appuie sur le bouton Précédent.
voici un exemple de code angulaire qui montre comment utiliser la classe SessionService
.
enregistrer une portée avec SessionService dans quelque contrôleur
angular
.module 'app'
.controller 'mainCtrl', ($scope, $state, session, security) ->
#register the scope with the session service
session.register $scope
#hook up the 'login' method (see security service)
$scope.login = security.login
# check the value of a session property
# it may well be true if the page has been refreshed
if session.isAuthenticated
$state.go('home')
else
$state.go('login')
Définir les valeurs de Session dans un service
class SecurityService
@$inject:['$http','sessionService', 'api']
constructor:(@http, @session, @api) ->
login:(username, password) =>
@http.get "#{@api.base}/security/login/credentials/#{username}/#{password}"
.success (user)=>
@session.isAuthenticated = true
@session.user = user
.error (ex)=>
# process error
angular
.module 'app'
.service 'securityService', SecurityService
utiliser les valeurs de Session dans L'UI (Jade template)
div(ng-show="isAuthenticated")
div Hello {{user.Name}}
j'ai été confronté à la même question et a choisi d'utiliser des cookies angulaires, depuis le le seul état qui n'est pas tiré par le modèle via ng-init est l'utilisateur connecté état.
je stocke le nom d'utilisateur dans un cookie lors de la connexion après avoir reçu l'utilisateur
modèle de notre serveur et j'efface le cookie d'ID utilisateur sur la déconnexion. Ensuite, pour récupérer
l'état utilisateur connecté sur un événement de rafraîchissement de page ou de retour de bouton, j'accroche le
$location
service $locationChangeStart
événement. De mon l'expérimentation, ce
l'événement est déclenché au point de l'emplacement est sur le point de changer, mais avant le
partielle/modèle a été chargé. Cela permet à l'état nécessaire d'être chargé juste
dans le temps.
Je ne suis pas convaincu que je n'ai pas une condition de race ici comme
$scope.loadLoggedInUser(...)
utilise asynch $ http pour charger l'état nécessaire mais jusqu'à présent
il a travaillé de manière fiable pour moi.
$scope.$on('$locationChangeStart', function() {
$log.debug("locationChangeStart");
if (!$scope.appCtx.models.loggedInUser) {
var userID = $cookies.get("userID");
if (!userID) {
$scope.doLogout();
return;
}
$scope.loadLoggedInUser(userID, true);
}
});
il y a une solution simple lorsque vous utilisez le noeud .js pour configurer votre serveur. Vous devez organiser votre routage côté client d'une manière qui fait de vos liens de route des expressions régulières uniques. En application.js vous aurez:
(function () {
var app = angular.module('dataCollector', ['ngRoute']);
app.config(['$routeProvider', '$locationProvider',
function ($routeProvider, $locationProvider) {
$routeProvider
.when('/', {
templateUrl: 'home.html',
controller: 'mainController'
})
.when('/about', {
templateUrl: 'about.html',
controller: 'aboutController'
})
.when('/login', {
templateUrl: 'login.html',
controller: 'loginController'
});
$locationProvider.html5Mode(true);
}]);
app.controller('mainController', ['$scope', function ($scope) {
}]);
})();
dans cet exemple, toutes les routes , sauf '/'
, peuvent être écrites dans un schéma d'expression régulière [A-Za-z]
. Ainsi, le serveur.fichier js serait comme ceci:
var express = require('express');
var http = require('http');
var fs = require('fs');
var path = require('path');
var app = express();
app.use(express.static('public'));
app.get(/[A-Za-z]/, function (req, res) {
res.sendFile(path.join(__dirname + '/index.html'));
});
http.createServer(app).listen(80);
maintenant chaque GET
demande qui correspond au regex [A-Za-z]
fera une réponse avec index.html
(qui sont nos routes appelées lors du rafraîchissement d'une page par exemple /about
). Toute autre demande GET
répondra avec un fichier du répertoire /public
(ici chaque fichier avec l'extension *.html
). Cela permet de rafraîchir le spa AngularJS de manière appropriée.