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

$templateRequest com ng-show ou ng-hide

Fala ai pessoal, estou apanhando mais que o José Aldo com o Angular tentando fazer uma diretiva simples.

Meu objetivo é ter uma lista animada, quando o usuário clicar em um item, ele abrir e mostrar um conteúdo.

Eu poderia colocar essa div de conteúdo com ng-hide=true, mas estaria duplicando uma grande quantidade de código, visto que apenas um item da lista pode ter seu conteúdo aberto por vez. Ou seja, cliquei em outro item da lista, o item anterior deve ser fechado(removido do dom). No meu código estou incluindo a div no dom dinamicamente.

Vamos lá.

O código abaixo funciona, consigo incluir e remover a div que peguei no templateRequest. O problema é que o ng-show só funciona na primeira execução, ou seja, quando clico pela primeira vez. Nas outras vezes que eu clico, posso alterar a variável "mostrar" que ele não reflete no template.

Espero que entendam.

JS

.directive('minhaDiretiva', function($templateRequest, $compile) {

        var ddo = {};

        ddo.restrict = "A";

        var itemListaPedido;

        $templateRequest("js/directives/minha-diretiva.html").then(function(html){
          itemListaPedido = angular.element(html);
        });

        var elementoAnterior;

        ddo.link = function(scope, elem, attrs){
            console.log(elem);
            elem.bind('click', function() {

                if(elementoAnterior){
                    if (elementoAnterior === elem) {
            // Quando o usuário clica novamente na LI ele entra aqui, porém o template não liga para a variável mostrar, coloco para true e ele continua a mostrar a div.
                        scope.mostrar = false;
                        elem.next().remove();
                        elementoAnterior = undefined;
                    }else{
                        elementoAnterior.next().remove();
                        elementoAnterior = elem;
                        elem.after(itemListaPedido);
                        $compile(itemListaPedido)(scope);
                    }
                }else{
                  // Essa primeira execução funciona!!!
                    elementoAnterior = elem;
                    scope.mostrar = true;
                    elem.after(itemListaPedido);
                    $compile(itemListaPedido)(scope);
                }

            });
        }

        return ddo;
    });

Template utilizado na diretiva

<div ng-show="mostrar">
    Alguma coisa interessante aqui
</div>

Lista que usa a diretiva

<ul>
<li>
    <div excluir-pedido>
        Meu conteúdo a ser clicado.
    </div>
<li>
<li>
    <div excluir-pedido>
        Meu conteúdo a ser clicado.
    </div>
<li>
<li>
    <div excluir-pedido>
        Meu conteúdo a ser clicado.
    </div>
<li>
</ul>
8 respostas

Entendi seu problema, mas não o seu código. Sua diretiva é "minhaDiretiva" e seu html nem faz uso dela, pelo menos o que mostrou. Você diz que só um elemento pode ser exibido por vez e quer remover os não clicados do Dom. Isso está correto? Não é só para o conteúdo dos demais ficarem escondidos?

Bom, independente do que voce queira, talvez seu problema seja porque você não definiu ddo.scope. Você não criou um escopo privado usando o modificador = (igual) para a propriedade mostra.

Flavio,

Acabei esquecendo e coloquei o nome errado da diretiva.

Então ficaria assim:

<ul>
<li>
    <div minha-diretiva>
        Meu conteúdo a ser clicado.
    </div>
<li>
<li>
    <div minha-diretiva>
        Meu conteúdo a ser clicado.
    </div>
<li>
<li>
    <div minha-diretiva>
        Meu conteúdo a ser clicado.
    </div>
<li>
</ul>

Inicialmente, quando a página carregar, a div que contem a minha-diretiva só vai exibir mesmo "Meu conteúdo a ser clicado". Quando clicar em "Meu conteúdo a ser clicado" a diretiva vai capturar o evento click e vai criar uma nova DIV abaixo da div atual. Se eu voltar a clicar em "Meu conteúdo a ser clicado", a diretiva vai capturar novamente o click, e vai remover a div adicionada anteriormente.

Todo esse controle está sendo feito e está funcionando.O meu problema é que eu gostaria de adicionar efeitos via css, e dessa forma o ng-animate não adiciona classes por eu estar manipulando o dom diretamente!!!

Eu gostaria de ter alguma forma de fazer a mesma coisa, utilizando ng-hide ou ng-show, que automaticamente adicionaria as classes do ng-animate. Mas eu por estar utilizando o $compile, acho que as diretivas ng-hide/show não funcionam.

Vou fazer um exemplo funcional em algum site e postar aqui e ajudar no entendimento de todos.

Flávio,

Consegui colocar um exemplo na internet. Apenas removi a chamada externa do html e coloquei dentro da variável "html". Clique em qualquer item da lista que ele vai exibir um conteúdo.

Meu objetivo é fazer exatamente o que fiz, porém utilizando diretivas do angular, para me beneficiar das classes de animação.

http://codepen.io/anon/pen/XXKMML

Entendi. É não, vejo solução fácil. Talvez, para você não ficar brigando com o Angular, porque não aplica classes CSS lá na sua diretiva que tenha o mesmo efeito? Você adiciona e remove classe, mesma coisa que algumas diretivas do Angular fazem.

Flavio,

Obrigado pela resposta. Tenho o conceito que sempre quando faço algo que está ficando muito complicado é porque estou fazendo algo errado e por isso postei aqui rsrsrs

Mas vamos lá, o que você falou eu já havia tentado. Porém esbarrei em dois problemas que não consegui resolver:

1) Quando o comando do angular eu vou fazer dar o Hide no elemento? 2) Se eu colocar o ".remove" logo depois do hide, o JS vai remover o elemento antes de animação terminar. Como vou prender isso?

Postei o código também em: http://codepen.io/anon/pen/XXKMML

ddo.link = function(scope, elem, attrs){
            elem.bind('click', function() {

                if(elementoAnterior){
                    if (elementoAnterior === elem) {

                        elem.next().addClass("conteudo-antes");
                        elem.next().addClass("conteudo-depois");
            // Aqui eu não posso simplesmente remover o elemento, preciso dar um Hide nele e só após terminar a animação, remover o elemento.
                        elem.next().remove();
                        elementoAnterior = undefined;



                    }else{
                        elementoAnterior.next().remove();
                        elementoAnterior = elem;
                        elem.after(itemListaPedido);
                        $compile(itemListaPedido)(scope);
                    }
                }else{
                    elementoAnterior = elem;
                    elem.after(itemListaPedido);
                    $compile(itemListaPedido)(scope);
                }

            });
        }

É realmente, não sei como resolver. Como você mesmo disse, quando algo fica complicado é porque tem algo de errado.

Talvez seja interessante voltar ao problema do zero e tentar outra solução. Agora de cabeça, não me vem nenhuma.

Vamos deixar aberto aqui um tempo para ver se alguém traz alguma ideia.

Olá Eduardo! Vendo o seu exemplo no codepen, talvez a abordagem abaixo te ajude a enxergar seu problema de outra forma:

var app = angular.module('myApp',[]).
directive('diretivaAlura', function(){
return {
    restrict : 'E',
    scope:{
      ordemTexto: '=',
      show: '='
    },
    template:"<div><button ng-click='show = ordemTexto'>{{nome}}</button> <span ng-show='show == ordemTexto'>{{texto}}</span></div>",
    controller : "@",
    name:"nomeController"    
    }   
}).
controller("UmCtrl",function($scope){
 $scope.nome = 'Controller 1';
 $scope.texto = 'Meu conteúdo 1.';
}).
controller("DoisCtrl",function($scope){
$scope.nome = 'Controller 2';
$scope.texto = 'Meu conteúdo 2.';
}).
controller("TresCtrl",function($scope){
$scope.nome = 'Controller 3';
$scope.texto = 'Meu conteúdo 3.';
})

HTML

<diretiva-alura ordem-texto="1" show="exibe" nome-controller="UmCtrl"></diretiva-alura>
<diretiva-alura ordem-texto="2" show="exibe" nome-controller="DoisCtrl"></diretiva-alura>
<diretiva-alura ordem-texto="3" show="exibe" nome-controller="TresCtrl"></diretiva-alura>
solução!

Olá Eduardo! Vendo o seu exemplo no codepen, talvez a abordagem abaixo te ajude a enxergar seu problema de outra forma:

var app = angular.module('myApp',[]).
directive('diretivaAlura', function(){
return {
    restrict : 'E',
    scope:{
      ordemTexto: '=',
      show: '='
    },
    template:"<div><button ng-click='show = ordemTexto'>{{nome}}</button> <span ng-show='show == ordemTexto'>{{texto}}</span></div>",
    controller : "@",
    name:"nomeController"    
    }   
}).
controller("UmCtrl",function($scope){
 $scope.nome = 'Controller 1';
 $scope.texto = 'Meu conteúdo 1.';
}).
controller("DoisCtrl",function($scope){
$scope.nome = 'Controller 2';
$scope.texto = 'Meu conteúdo 2.';
}).
controller("TresCtrl",function($scope){
$scope.nome = 'Controller 3';
$scope.texto = 'Meu conteúdo 3.';
})

HTML

<diretiva-alura ordem-texto="1" show="exibe" nome-controller="UmCtrl"></diretiva-alura>
<diretiva-alura ordem-texto="2" show="exibe" nome-controller="DoisCtrl"></diretiva-alura>
<diretiva-alura ordem-texto="3" show="exibe" nome-controller="TresCtrl"></diretiva-alura>