Fala galera!

Semana passada, recebi um e-mail perguntando se eu tinha algum exemplo, com AngularJS, para controlar as permissões de acesso do usuário aos botões da aplicação. Pensei: porquê não juntar a fome com a vontade de comer e blogar a respeito? Então vamos nessa!

Diretiva

Antes de colocar a mão na massa, é importante saber que o melhor lugar para se manipular o DOM é dentro de uma directive. Isso se deve ao comportamento do Angular HTML Compiler. Pode parecer meio estranho falar sobre compilar, mas é graças ao HTML Compiler que podemos anexar comportamento a qualquer elemento HTML ou adicionar atributos.

Uma dica importante refere-se a normalização dos nomes das diretivas. O AngularJS geralmente utiliza camelCase para normalizá-las (exemplo: ngRepeat e ngModel). Porém, temos que lembrar que o HTML não é case sensitive. Por esse motivo, usamos letras minúsculas e mais um traço separando as palavras (ng-repeat e ng-model) para utilizar as diretivas no DOM .

O nome da nossa diretiva de acesso será “permissaoAcesso. Ela terá a restrição de ser utilizada unicamente como atributo em um elemento do DOM. E é aqui que pode surgir a dúvida: “mas Gabriel, tem como restringir como a diretiva vai ser utilizada?”

A resposta é sim. As diretivas possuem a opção de restringir como serão usadas. Essa opção é a restrict  e ela obedece os seguintes parâmetros:

  • A: só pode ser utilizada como atributo.
  • E: só pode ser utilizada como elemento.
  • C: só pode ser utilizada como classe (class) css.
  • M: só pode ser utilizada como comentário.

Mas e se eu quiser usar como elemento e atributo, como faço? Aí é só mesclar com as letras que você desejar, por exemplo ‘AECM’, que libera o uso da diretiva em todas as opções mencionadas acima.

Por default, a opção restrict será ‘AE’. Ou seja, poderá ser utilizada como elemento e atributo.

Construindo a diretiva permissaoAcesso

Para iniciar, vamos criar uma página HTML que exibirá os elementos (botão e link). A regra é que se o usuário não possuir a permissão de acesso o elemento deverá ser desabilitado, caso contrário ele será exibido normalmente.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<!DOCTYPE html>
<html ng-app="acesso">
<head>
  <meta charset="utf-8" />
  <link rel="shortcut icon" type="image/x-icon" href="favicon.ico">
  <title>Blog do Gabriel Feitosa</title>
  <link rel="stylesheet" href="font-awesome/css/font-awesome.min.css">
  <style>
    .block {
        margin-left: 5px;
        color: red;
    }
    
    a[disabled] {
        pointer-events: none;
    }
    
    td {
        padding: 5px
    }
  </style>
</head>

<body>
  <h1>AngularJS: Diretiva de acesso</h1>
  <table>
    <th>
      <tr>
          <td></td>
          <td>Botão</td>
          <td>Link</td>
      </tr>
    </th>
    <tr>
      <td>Com Restrição</td>
      <td>
          <button permissao-acesso="block" onclick="alert('Ops, deu errado!')">Botão</button>
      </td>
      <td>
          <a href="" permissao-acesso="block" onclick="alert('Ops, deu errado!')">Link</a>
      </td>
    </tr>
    <tr>
      <td>Sem Restrição</td>
      <td>
        <button permissao-acesso="no-block" onclick="alert('Aeee \\o/, botão liberado!')">Botão</button>
      </td>
      <td>
        <a href="" permissao-acesso="no-block" onclick="alert('Aeee \\o/, link liberado!')">Link</a>
      </td>
    </tr>
  </table>

  <footer>
      <hr/>
      <a href="http://gabrielfeitosa.com"> Blog do Gabriel Feitosa</a>
  </footer>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.min.js"></script>
  <script src="js/app.js"></script>
</body>

</html>

Como pode ser visto no código acima, os elementos “button” e “a” possuem a nossa diretiva “permissao-acesso”. A diretiva está recebendo um valor (block e no-block) que será utilizado na nossa lógica para bloquear ou não o elemento.

O evento de click dos elementos está configurado com um alert. Assim,  uma mensagem será exibida caso consigamos clicar no elemento.

Para incrementar a diretiva, utilizei a lib Font Awesome, para adicionar um ícone quando o elemento estiver bloqueado (só frescura mesmo! ;))

Agora vamos ver a implementação da nossa diretiva permissaoAcesso:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
(function(){
  angular.module('acesso',[])
  .directive('permissaoAcesso', function(){
    return {
      restrict: 'A',
      link: function ($scope, element, attrs) {
        if (attrs.permissaoAcesso === 'block') {
            element.attr('disabled', 'disabled');
            element.append('<span class="block fa fa-lock"></span>');
        }
      }
    };
  });
})();

Perceba que o primeiro parâmetro da diretiva é o restrict com valor “A”. Como dito anteriormente, essa diretiva só poderá ser utilizada como um atributo.

Em seguida, temos uma novidade que é a opção link. É aqui que normalmente manipulamos o DOM e colocamos a nossa lógica de negócio. A função do link é executada após o template ser clonado e recebe cinco parâmetros:

  1. scope: o objeto $scope do AngularJS.
  2. element: é o elemento que está sendo processado. Esse elemento está dentro de um wrapper do jqLite.
  3. attrs: é um hash dos atributos e seus respectivos valores que estão presentes no elemento.
  4. controller: é uma instância do controlador.
  5. transcludeFn: é um link para a função de transclude.

A lógica de negócio da nossa diretiva só utiliza os parâmetros attrs e element.

O attrs é utilizado para ter acesso ao valor do atributo permissaoAcesso e verificar se o conteúdo dele é block. Caso a condição seja verdadeira, iremos manipular o elemento através do parâmetro element. A ideia é bem simples: adicionamos ao elemento um atributo disabled para bloqueá-lo. Na sequência, incorporamos um elemento span com o ícone do Font Awesome, o que será feito através do element.append.

A Figura 1 exibe o resultado do uso da diretiva. O exemplo do código está rodando aqui e os fontes estão no github.

Resultado do uso da diretiva

Há muito mais coisas para comentar sobre diretivas, como o isolamento do escopo, $compile, transclude. Vale a pena dar uma olhada na documentação oficial, que por sinal é muito rica.

Usando a imaginação dá para incrementar a diretiva para outros elementos HTML e adicionar ou remover um comportamento de acordo com o tipo do elemento. Lembre que você tem em mãos um wrapper do elemento em jqLite, que é um subconjunto do jQuery.

Bom, por hora é isso pessoal. Espero que tenham curtido a postagem de hoje.

Abraços e até a próxima!