Solucionado (ver solução)
Solucionado
(ver solução)
19
respostas

Duvida com scope e diretivas

Boa tarde. Minha dúvida é a seguinte: Supondo que eu tenha uma partial que utilize um controller apenas para fazer uma requisição http, buscar algo e armazenar no scope ou algo do tipo. Mas eu também tenho uma diretiva de cabeçalho que é utilizada em todas as minhas partials, que precisa exibir esta informação que veio de um controller de uma partial específica. Em outras palavras, tenho uma partial de login e quero mostrar o usuário logado na diretiva de cabeçalho que é utilizada em todas as paginas. Como consigo isso?

angular.module('minhasDiretivas', []) .directive('header', function () { return { restrict: 'A', replace: true, scope: {user: '='}, templateUrl: "js/directives/header.html", controller: ['$scope', '$filter', function ($scope, $filter) { }] } });

19 respostas

PS: Para maiores detalhes, minha aplicação de estudo está no github em http://github.com/codigoalvo/lab-rest

Tem como você colocar seu código formatado?

O jeito mais recomentafo de você compartilhar dados entre diferentes controllers é usar um service. O service e instanciado uma vez apenas (se você der enter na url do navegador ele vão recarregar....))Você aprendeu a fazer services no curso. Aguarde o usuário como atributo e acesse onde quiser.

Se vc não usar sistema de rotas do Angular não funcionará.

Se sua diretiva só quer pegar dado de um controller é igual fizemos no curso. Vc usa @ ou =

Cassio, tenta seguir minha dica. Se ficou vago ou ainda não resolveu poste seu código formatado. Excepcionalmente não estou na frente do meu computador. Para te ajudar ainda melhor se vc detalhar eu consigo te ajudar via smartphone.

Eu estou ciente da sua dúvida. ..fique tranquilo que gente resolve. Mas o usuário logado precisa ser armazenado em um service. Um service e instanciado apenas uma vez, lugar perfeito para guardar o usuário logado.

No controller vc injeta o service e através controller disponibiliza dado para a view.

Foi exatamente o que eu fiz, com o "=", mas não funcionou. Eu já estou usando o sistema de rotas mas sem o html5mode, inclusive consegui fazer o interceptor que eu estava com dúvida antes. Só não consegui deixar o código do interceptor em um arquivo separado, tive que deixar no maim.js

angular.module('minhasDiretivas', [])
    .directive('header', function () {
        return {
            restrict: 'A',
            replace: true,
            scope: {user: '='},
            templateUrl: "js/directives/header.html",
            controller: ['$scope', '$filter', function ($scope, $filter) {
                // behaviour goes here
            }]
        }
    });

e o html:

<nav class="navbar navbar-default navbar-fixed-top">
    <div class="container" ng-controller="LoginController">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed"
                data-toggle="collapse" data-target="#navbar" aria-expanded="false"
                aria-controls="navbar">
                <span class="sr-only">Toggle navigation</span> <span
                    class="icon-bar"></span> <span class="icon-bar"></span> <span
                    class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#home"><img src="images/codigoalvo.svg" alt="codigoalvo" height="23px"/></a>
        </div>
        <div id="navbar" class="navbar-collapse collapse">
            <ul class="nav navbar-nav">
                <li><a href="#home"><span class="glyphicon glyphicon-home" /></a></li>
                <li><a href="ws/categorias/1">Categoria (1) - REST</a></li>
                <li><a href="ws/categorias">Categorias - REST</a></li>
                <li><a href="#categorias">Categorias - Angular</a></li>
            </ul>
            <ul class="nav navbar-nav navbar-right">
                <li ui-if="!user"><a href="#login">login</a></li>
                <li ui-if="user"><a href="#logout" ng-click="efetuarLogout()">logout ({{user.login}})</a></li>
            </ul>
        </div>
    </div>
</nav>

e na utilização do div do cabeçalho:

<div header user="{{usuarioLogado}}"></div>

Porém no navegador depois de armazenar no $scope.usuarioLogado o json do usuario que o login retornou, aparece na tela: login logout ({{user.login}})

Não sei o que eu estou fazendo errado!

Onde está o código que pega o usuário! ??? Ese e seu código completo?

Aparece erro na tela? Não está claro para mim. Aparece angular expression não avaliada? Já leu o seu Log do chrone?

Olhando só o que você me passou estou viajando para entender o que vice quer. Até entendi que é exibir o usuário mas de onde vem esse cara?

Sem pressa Flávio, quando puder, por gentileza, dê uma olhada no código que está no guithub. Pode desconsiderar o que for referente a Java que cuida do backend, que essa parte eu sei que está funcionando tudo corretamente. O conteúdo do angular está na pasta WebContent. vc pode baixar como zip o conteudo do lab-rest ou visualizar diretamente no github. Se vc pudesse me explicar como transformar meu loginController em um Service que possa ser utilizado no login controller e consequentemente na diretiva e na partial de login... Eu continuarei tentando soluções de fóruns e paginas da web pra ver se acho alguma solução enquanto aguardo sua ajuda. Se eu conseguir resolver posto a solução aqui. Por enquanto, obrigado pela atenção!

Já tentou pedir ajuda também no fórum angularjs Brasil no Facebook? Pode acelerar até que eu possa chegar ao meu computador.

Ele vem da partial de login que assim como a directive do header tb utiliza o LoginController. Vou postar os dois arquivos completos formatados aqui pra ver se ajuda. O login em si eu sei que está funcionando pois eu tenho outra partial que acessa outro serviço do rest que se o login não tiver sido realizado e o token não estiver no $window.sessionStorage.token o serviço não funciona, e está funcionando.

login.html

<div header user="{{usuarioLogado}}"></div>
<div ng-controller="LoginController">
    <form class="form-signin">
        <h2 class="form-signin-heading text-center">Dados de login</h2>
        <label for="login" class="sr-only">Login</label> <input type="text" id="login" class="form-control"
            ng-model="usuario.login" placeholder="Login do usuário" required autofocus> <label for="senha"
            class="sr-only">Senha</label> <input type="password" id="senha" class="form-control" ng-model="usuario.senha"
            placeholder="Senha" required>
        <button class="btn btn-lg btn-primary btn-block" ng-click="efetuarLogin()" id="submit" type="submit">Enviar</button>
    </form>
</div>

LoginController

angular.module('alvoApp').controller('LoginController',    function($scope, $http, $window, $location) {
    $scope.usuario = {
        login : 'admin',
        senha : 'admin',
    };
    $scope.usuarioLogado = '';

    $scope.efetuarLogin = function() {
        $http({
              method: 'POST',
              data: $scope.usuario,
              url:'ws/login',
              headers: {'Content-Type':'application/json'}
        }).then(
            function(resp) {
                $scope.usuarioLogado = resp.data;
                console.log('usuarioLogado', resp.data);
                $window.sessionStorage.usuarioLogado = resp.data;
                $location.path("/home");
            }, function(err) {
                $scope.usuarioLogado = '';
                console.error('Error', err);
        })
    };

    $scope.efetuarLogout = function() {
        console.log('Removendo usuarioLogado da sessão!')
        $scope.usuarioLogado = '';
        delete $window.sessionStorage.usuarioLogado;
        console.log('Removendo token da sessão!')
        delete $window.sessionStorage.token;
    };
})

main.js

angular.module('alvoApp', ['ngRoute', 'ngResource', 'minhasDiretivas', 'categoriaService'])
    .config(function($routeProvider, $httpProvider, $locationProvider) {

        //$locationProvider.html5Mode(true);
        //$httpProvider.interceptors.push('AuthInterceptor');

        var interceptor = ['$q', '$window', '$location', '$injector', function($q, $window, $location, $injector) {
            return {
                request: function (config) {
                    config.headers = config.headers || {};
                    if ($window.sessionStorage.token) {
                        config.headers.Authorization = $window.sessionStorage.token;
                    }
                    return config;
                },

                requestError: function(rejection) {
                    return $q.reject(rejection);
                },

                response: function (response) {
                    var token = response.headers('Authorization');
                    if (token != null) {
                        console.log('Old token in Session: ', $window.sessionStorage.token);
                        console.log('New token from Header', token);
                        $window.sessionStorage.token = token;
                        console.log('*** Token in Session ***  ', $window.sessionStorage.token);
                    } 
                    return response || $q.when(response);
                },

                // Revoke client authentication if 401 is received
                responseError: function(rejection) {
                    console.log('Rejection response: '+rejection);
                    if (rejection != null && rejection.status === 401) {
                        if ($window.sessionStorage.token) {
                            console.log('Removendo token da sessão!')
                            delete $window.sessionStorage.token;
                        }
                        console.log('Redirecionando para login!')
                        $location.path("/login");
                    }
                    return $q.reject(rejection);
                }
            };
        }];
        $httpProvider.interceptors.push(interceptor);


        $routeProvider.when('/categorias', {
            templateUrl: 'partials/categorias.html',
            controller: 'CategoriaController'
        });

        $routeProvider.when('/categorias/nova', {
            templateUrl: 'partials/categoria.html',
            controller: 'CategoriaController'
        });

        $routeProvider.when('/categorias/editar/:categoriaId', {
            templateUrl: 'partials/categoria.html',
            controller: 'CategoriaController'
        });

        $routeProvider.when('/login', {
            templateUrl: 'partials/login.html',
            controller: 'LoginController'
        });

        $routeProvider.when('/home', {
            templateUrl: 'partials/home.html'
        });

        $routeProvider.otherwise({redirectTo: '/home'});

    });

No curso vc aprendeu a criar um service. Certo? Que nada mais é que um json retornado. Nele vc coloca os dados que quiser. Fizemos isso duas vezes, só não guardamos estado . Em Angular um service guarda estado. Crie o service com a função factory e injete o service em um controller.

Mas para vc fazer o que quer nem precisava disso. Gerava o html principal pelo Server mesclando o usuário no índex html usando jsp.

Vou tentar depois criar esse Service e guardar o estado. Guardo no $scope do service mesmo? e recupero com {{}} no html? Guardando no $window.sessionStorage o json do Usuario, Como eu poderia recuperar isso no HTML ou de outra forma dentro do directive do header? Não seria mais simples do que criar o service? Eu estou tentando não usar JSP na view pra deixar totalmente independende do java, assim posso separar o REST em um servidor e a view em um host de HTML qualquer depois se eu quiser.

Você não usa scope service. O dado e uma propriedade do service. Services não tem $scope. Se você vai usar localstorage ou sessionstorage vai depender da sua solução.

Eu não uso token, uso sessão. Vc quer fazer um back stateless com angular. Não há receita de bolo. Por isso eu uso sessão.

Ainda assim, gostaria muito de uma ajuda de como controlar a exibição do nome do usuário logado no header e a visibilidade de algumas opções do header dependendo do usuário estar logado ou não, tudo isso controlado apenas pelo angular. Se alguém tentar sacanear e mudar o html na mão, não tem problema porque o server vai controlar o acesso pelo token.

.

Nunca implementei essa solução. Por ser algo bem específico sugiro procurar ajuda no angularjs Brasil no Facebook.

Estou que nem você, tendo que pesquisar.

Vou deixar em aberta sua dúvida uma semana para ver se alguém consegue ajudar.

solução!

Bom dia Flávio. Acabei por conseguindo uma solução satisfatória: Utilizei factory/service pra armazenar e recuperar do $window.sessionStorage o token do usuário e dentro da diretiva eu chamo o service que busca o token no $window.sessionStorage, decodifica e armazena no $scope da diretiva o json do usuario. Acabou ficando bem interessante. Pra exibir ou não detarminada informação eu uso a tag do angular ng-show="usuarioLogado". Não achei uma solução boa, porque no codigo fonte do HTML fica todo o HTML, o do logado e o do não logado. Estou pensando em passar todo o HTML da diretiva do menu pra dentro do .js e controlar se concateno ou não determinadas partes dependendo da existencia da variavel do usuário. Acho uma solução um tanto quanto deselegante, mas não vejo muito outra forma. Ficaria muito grato por alguma sugestão. Mas de qq forma, o problema principal foi resolvido. Quem tiver interesse, o código fonte completo está em : http://github.com/codigoalvo/lab-rest

Ficou ótimo! Essa ideia de você concatenar, ou seja, criar um HTML específico através de JS é outra solução, com certeza. Agora que você já conseguiu exercitar essa parte do token que pode ser feito de zilhões de formas (você criou sua implementação e tem mérito por isso) pode com mais calma verificar outras soluções.

Para todo aluno com seu perfil incansável em encontrar soluções só tenho a desejar muito sucesso. Inclusive seus achados podem virar post, palestra ou qualquer coisa nesse sentido.

Sucesso e bom estudo!