Como faço para armazenar um contexto de usuário atual no AngularJS?

92

Eu tenho um AuthService, que registra um usuário, ele retorna um objeto json do usuário. O que eu quero fazer é definir esse objeto e ter todas as alterações refletidas no aplicativo (estado conectado / desconectado) sem ter que atualizar a página.

Como eu faria isso com o AngularJS?

Arte
fonte

Respostas:

180

A maneira mais fácil de fazer isso é usando um serviço. Por exemplo:

app.factory( 'AuthService', function() {
  var currentUser;

  return {
    login: function() { ... },
    logout: function() { ... },
    isLoggedIn: function() { ... },
    currentUser: function() { return currentUser; }
    ...
  };
});

Você pode então fazer referência a isso em qualquer um de seus controladores. O código a seguir observa as alterações em um valor do serviço (chamando a função especificada) e, em seguida, sincroniza os valores alterados com o escopo.

app.controller( 'MainCtrl', function( $scope, AuthService ) {
  $scope.$watch( AuthService.isLoggedIn, function ( isLoggedIn ) {
    $scope.isLoggedIn = isLoggedIn;
    $scope.currentUser = AuthService.currentUser();
  });
});

E então, é claro, você pode usar essas informações da maneira que achar necessário; por exemplo, em diretivas, em modelos, etc. Você pode repetir isso (personalizado para o que você precisa fazer) em seus controladores de menu, etc. Tudo será atualizado automaticamente quando você alterar o estado no serviço.

Qualquer coisa mais específica depende da sua implementação.

Espero que isto ajude!

Josh David Miller
fonte
28
@ChrisNicola Na verdade, no AngularJS todos os serviços são singletons. Portanto, o serviço é criado na primeira vez que é solicitado (ou seja, por um controlador ou outro serviço) e todas as solicitações subsequentes retornam exatamente a mesma instância.
Josh David Miller
2
Pode ser, mas como uma função, podemos remover os detalhes internos de como armazenamos essas informações da API pública e na API privada. Isso torna a refatoração posterior muito mais fácil. Mas a função ainda retornaria um booleano.
Josh David Miller,
7
Esta pode ser uma pergunta estúpida ... mas o que acontece se o usuário atualizar a página - as informações de login foram perdidas?
Tomba
10
@Tomba Essa é uma boa pergunta. :-) Na verdade, a informação se perde na atualização. Normalmente, você deseja armazenar algumas informações da sessão em um cookie. Essas informações da sessão também podem ser verificadas ao configurar o AuthService. Isso ajuda não apenas para atualizações de página, mas para alguém abrindo um link em uma nova guia.
Josh David Miller
2
@PixMach Você está 100% certo sobre a curva de aprendizado. Sua pergunta vai depender muito do caso específico, mas aqui estão alguns padrões gerais. Mantenha a separação de preocupações: a IU relacionada ao início de um login é separada da própria auth, que é separada do estado da auth, que é separada de qualquer menu que possa depender desse estado. Nav / menu são geralmente mais bem tratados por um único controlador, e estados aninhados (a la ui-router) e resoluções de rota são uma boa maneira de manter os controles de autenticação DRY. O que você escreveu parece certo.
Josh David Miller
5

Eu corrigiria a boa resposta de Josh acrescentando que, como um AuthService é tipicamente do interesse de qualquer pessoa (digamos, qualquer um, exceto a visualização de login deve desaparecer se ninguém estiver conectado), talvez uma alternativa mais simples seja notificar as partes interessadas usando $rootScope.$broadcast('loginStatusChanged', isLoggedIn);(1 ) (2), enquanto as partes interessadas (como controladores) ouviriam usando $scope.$on('loginStatusChanged', function (event, isLoggedIn) { $scope.isLoggedIn = isLoggedIn; }.

(1) $rootScopesendo injetado como um argumento do serviço

(2) Observe que, no caso provável de uma operação de login assíncrona, você desejará notificar o Angular que a transmissão mudará as coisas, incluindo-o em uma $rootScope.$apply()função.

Agora, por falar em manter o contexto do usuário em todos / muitos controladores, você pode não gostar de ouvir as alterações de login em todos eles e pode preferir ouvir apenas em um controlador de login superior, adicionando então outros controladores com reconhecimento de login como filhos / controladores embutidos deste. Dessa forma, o controlador filho será capaz de ver as propriedades herdadas do parent $ scope, como o contexto do usuário.

Javarome
fonte
4
Votos negativos para explicação incorreta da função de fábrica. Esse mal-entendido já foi abordado neste comentário meses antes de você postar sua resposta.
Rhys van der Waerden