# Diretivas

Diretivas são as formas que temos para manipular o template, com ela podemos alterar o nosso DOM.

O Vue nos da a possibilidade de usar varias diretivas como tbm criar as nossas proprias customizadas.

Não vou mostrar como montar a nossa, mas na DOC do Vue podemos ver como fazer facilmente.

Toda diretiva deve ser declara com v- na frente, tirando duas que podemos omitir/abreviar (Sessão abreviação).

Algumas diretivas podem receber parâmetros, argumentos dinâmicos e modificadores, são elas o v-bind e o v-on, falarei quando chegar nestes dois.

# Diretivas

  • v-text
  • v-html
  • v-show
  • v-once
  • v-if
  • v-else
  • v-else-if
  • v-for
  • v-bind
  • v-bind:class
  • v-bind:style
  • v-model
  • v-on
  • v-cloak
  • v-slot
  • v-pre

# v-text

É outra forma que temos para imprimir variáveis sem ser com o mustache.

Nas diretivas não precisamos usar o this.nomeVariavel para acessa-lá pois já tentam pegar os dados por padrão.

<span v-text="msg"></span>

<span>{{msg}}</span>
1
2
3

# v-html

É usada para imprimir conteúdo html, ou seja, atualiza o innerHTML do elemento.

Então se tivermos uma variavel com o conteúdo:

<script>
export default {
  name: 'App',
  data () {
    return {
      conteudo: '<h2>Seja Bem Vindo</h2>'
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10

E passarmos ele pela diretiva v-html ele sera inserido como HTML e não compilado como template vue.

Então declarando isso:

<span v-text="conteúdo"></span>
1

Seria isso ao carregar a página:

<span><h2>Seja Bem Vindo</h2></span>
1

Evite usar v-html em todo seu código pois pode facilmente levar a ataques XSS. Use somente em conteúdo confiável e nunca em conteúdo fornecido pelo usuário.

Um lembrete, estilos scoped não serão aplicados a conteúdos v-html pois o HTML não é processado com o template do vue.

# v-show

O v-show sempre será renderizado e permanecerá no DOM, ela apenas alterna a propriedade CSS de exibição do elemento.

O v-show não suporta o elemento < template >, nem funciona com o v-else.

Então se tivermos uma variavel com o conteúdo:

<script>
export default {
  name: 'App',
  data () {
    return {
      mostra: true
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
<div v-show="mostra">Seja Bem Vindo</div>
1

# v-once

Esta por sua vez faz o papel contrario ao v-show, ela desliga a reatividade.

Então o dado vai ser carregado uma vez, se for alterado ou nas renderizações subsequentes, o elemento/componente e todos os seus filhos serão tratados como conteúdo estático e ignorados. Isso pode ser usado para otimizar o desempenho da atualização.

<script>
export default {
  name: 'App',
  data () {
    return {
      nome: 'Nome inicial'
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
<div v-once>{{ name }}</div>
<input type="text" v-model="name" />
1
2

Ao inserir um novo nome no input ele nao será refletido na div pois ela já foi renderizada e por ter o v-once não vai sofrer mutação. Resumindo, apenas uma vez ocorrera a mutação.

# v-if, v-else e v-else-if

Podemos fazer teste condicional com esta diretiva

A condição funciona da mesma forma que trabalhamos em javascript

<div v-if="valor === 'A'">
  A
</div>
<div v-else-if="valor === 'B'">
  B
</div>
<div v-else-if="valor === 'C'">
  C
</div>
<div v-else>
  Não A/B/C
</div>
1
2
3
4
5
6
7
8
9
10
11
12

# v-for

A diretiva v-for nos permite percorrer itens em uma matriz ou objeto. Requer uma sintaxe especial na forma de item in lista, em que lista é a matriz de dados de origem e item é um alias para o elemento da matriz que está sendo iterado.

A forma mais simples é esta:

<div v-for="item in lista">
  {{ item.texto }}
</div>
1
2
3

Podemos obter o seu indice:

<div v-for="(item, indice) in lista">
  {{ item.texto }}
</div>

Outra coisa importante é que você pode informar a identidade de cada nó, ajudando o vue a rastrear facilmente eles se for por exemplo precisar reordenar os itens, não só para isso, o vue por meio da **key** efetua a ordenação do item:
```html
<div v-for="(item, indice) in lista" :key="indice">
  {{ item.texto }}
</div>
1
2
3
4
5
6
7
8
9

No :key você pode informar qualquer coisa que venha identificar exclusivamente cada item, eu costumo usar o indice por ser unico.

Alternativamente, você também pode especificar um alias para o índice (ou a chave, se usada em um Objeto):

<div v-for="(item, indice) in lista"></div>
<div v-for="(valor, chave) in objeto"></div>
<div v-for="(valor, nome, indice) in objeto"></div>
1
2
3

# v-bind

É usado para ligar dinamicamente um ou mais atributos ou um suporte de componente a uma expressão.

Quando usado para vincular o atributo de classe ou estilo, ele suporta tipos de valor adicionais, como Matriz ou Objetos.

Você pode omitir o v-bind deixando apenas os dois pontos (Mas na sessão abreviação).

Resumidamente o v-bind é usado para passar valores dinâmicos para atributos html como src, classe, style, name, id, ... Todos os atributos praticamente.

<img v-bind:src="imageSrc">

<!-- valor dinâmico -->
<button v-bind:[key]="value"></button>

<img :src="'/path/to/images/' + fileName">

<div :class="{ red: isRed }"></div>
<div :class="[classA, classB]"></div>
<div :class="[classA, { classB: isB, classC: isC }]">

<div :style="{ fontSize: size + 'px' }"></div>
<div :style="[styleObjectA, styleObjectB]"></div>

<div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>

<!-- propriedade de um componente -->
<my-component :prop="someThing"></my-component>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

A forma abreviada para o v-bind seria retirando o v-bind e deixando apenas o atributo:

<img :src="imageSrc">
1

# v-model

Esta diretiva nos permite vincular uma variável com o valor do elemento.

Isso é chamado de data binding (two-way binding), que faz parte da reatividade.

Quando alterar o valor do imput ele sera alterado na variavel no mesmo instante.

Limitado a:

  • input
  • select
  • textarea
  • componentes

Possui modificadores:

  • .lazy - escuta por eventos change ao invés de input, ou seja, quando retirar o foco do componente
  • .number - faz a conversão da String informada para números
  • .trim - faz trim dos dados informados
<script>
export default {
  name: 'App',
  data () {
    return {
      nome: ''
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
<div>{{ nome }}</div>
<input type="text" v-model="nome" />
1
2

Com modificador

<div>{{ nome }}</div>
<input type="text" v-model.trim="nome" />
1
2

# v-on

Esta é a forma que o Vue nos dá para executar ações, invocar eventos.

Você pode omitir o v-on: deixando apenas @nomeEvento

Existem alguns eventos:

  • change
  • blur
  • focus

Existem modificadores de eventos, e a ordem importa ao definir modificadores encadeados:

  • .stop
  • .prevent
  • .capture
  • .self
  • .once
  • .passive
<!-- a propagação do evento click será interrompida -->
<a v-on:click.stop="doThis"></a>

<!-- o evento submit deixará de recarregar a página -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- modificadores podem ser encadeados -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- é possível utilizar apenas o modificador -->
<form v-on:submit.prevent></form>

<!-- usar modo de captura ao adicionar o evento -->
<!-- ou seja, um evento em um elemento interno é tratado aqui após ser tratado por aquele elemento -->
<div v-on:click.capture="doThis">...</div>

<!-- só aciona o manipulador se event.target é o próprio elemento -->
<!-- isto é, não aciona a partir de um elemento filho -->
<div v-on:click.self="doThat">...</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

Existem modificadores de teclado, o uso de keyCode (numero da tecla) esta obsoleto e pode não funcionar em alguns navegadores:

  • .enter
  • .tab
  • .delete (captura tanto “Delete” quanto “Backspace”)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right
<input v-on:keyup.13="submit">

<input v-on:keyup.enter="submit">
1
2
3

Existem modificadores de sistema

  • .ctrl
  • .alt
  • .shift
  • .meta
<!-- Alt + C -->
<input @keyup.alt.67="clear">

<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Faça alguma coisa</div>
1
2
3
4
5

Modificador extra, o .exact permite controlar a exata combinação de modificadores de sistema que deve ser pressionada para que o gatilho dispare.

<!-- dispara mesmo se Alt ou Shift também estiverem pressionados -->
<button @click.ctrl="onClick">A</button>

<!-- dispara quando somente Ctrl estiver pressionado -->
<button @click.ctrl.exact="onCtrlClick">A</button>

<!-- dispara somente se não houverem teclas de sistema pressionadas -->
<button @click.exact="onClick">A</button>
1
2
3
4
5
6
7
8

Existem modificadores de mouse, restringem o manipulador a eventos disparados por um botão específico do mouse, respectivamente o botão da esquerda, o da direita e o central (quando existente).

  • .left
  • .right
  • .middle
<div @click.middle="name = 'x'">{{ name }}</div>
1

# v-cloak

Essa diretiva permanecerá no elemento até que a instância associada de Vue termine de compilar. Ao ser combinada com regras CSS como [ v-cloak ] { display: none }, essa diretiva pode ser usada para esconder interligações mustache não-compiladas até que a instância de Vue esteja pronta.

A div não ficará visível até que a compilação tenha terminado.

No css:

[v-cloak] {
  display: none;
}
1
2
3

No html:

<div v-cloak>
  {{ mensagem }}
</div>
1
2
3

# v-bind:class ou :class

data: {
  isActive: true,
  hasError: false
}

<div v-bind:class="{ active: isActive }"></div>


<div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }"></div>


// Pode receber uma computed que retorne um objeto de chave:valor
<div v-bind:class="classObject"></div>


// Pode receber um array para aplicar uma lista de classes
data: {
  activeClass: 'active',
  errorClass: 'text-danger'
}
<div v-bind:class="[activeClass, errorClass]"></div>


// Pode receber o valor condicionalmente
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>


// Pode usar a sintaxe de objeto dentro da de array
<div v-bind:class="[{ active: isActive }, errorClass]"></div>
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

Quando você utiliza o atributo class em um componente personalizado, estas classes serão adicionadas ao elemento raiz. Classes que já existam neste elemento não serão removidas.

Vue.component('my-component', {
  template: '<p class="foo bar">Oi</p>'
})


// Chamando o componente e declarando outras classes
<my-component class="baz boo"></my-component>


// Resultado
<p class="foo bar baz boo">Oi</p>
1
2
3
4
5
6
7
8
9
10
11

O mesmo vale para v-bind:class

<my-component v-bind:class="{ active: isActive }"></my-component>

// Resultado
<p class="foo bar active">Oi</p>
1
2
3
4

# v-bind:style ou :style

É bastante parecido com :class mas em um objeto, pode usar camelCase ou kebab-case para nome das propriedades.

data: {
  activeColor: 'red',
  fontSize: 30
}

<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
1
2
3
4
5
6

O mais indicado é vincular com um objeto diretamente para ficar mais limpo o codigo:

data: {
  styleObject: {
    color: 'red',
    fontSize: '13px'
  }
}

<div v-bind:style="styleObject"></div>
1
2
3
4
5
6
7
8

Pode ser usado com computeds que retornam objetos de chave:valor.

Aceita array, mas aceita objetos apenas.

<div v-bind:style="[baseStyles, overridingStyles]"></div>
1

Quando você usa uma propriedade CSS que requer prefixos de fabricantes em v-bind:style, por exemplo transform, Vue irá automaticamente detectar e adicionar os prefixos apropriados para os estilos aplicados.

Podemos disponibilizar um array com múltiplos valores (prefixados) para estilizar um atributo, por exemplo:

<div v-bind:style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
1

Isto irá renderizar apenas o último valor do Array que o navegador suportar. Neste exemplo, irá renderizar display: flex nos navegadores que suportam a versão sem prefixo do módulo Flexbox.

# v-slot

Slot é a forma que temos de deixar o conteudo de um elemento dinamico.

Se tivessemos uma div que não sabemos tudo que vai dentro dela, mas sabemos que por padrão ela vai ter uma tag p, poderiamos definir um slot dentro desta div chamada outros e quando formos querer passar este conteudo extra é só passar um slot com o nome outros e ele sera definido dentro da div.

Outro exemplo seria um componente de card que vai receber slots juntamente com o conteudo dele.

Componente card:

<div>
  <h1>Titulo Fixo do Card</h1>
  <slot name="descrição"></slot>
  <slot name="link"></slot>
</div>
1
2
3
4
5

Chamando o componente card e passando os slots

<card>
  <p slot="descrição">Descrição qualquer, ......</p>
</card>
1
2
3

Seria renderizado:

<div>
  <h1>Titulo Fixo do Card</h1>
  <p>Descrição qualquer, ......</p>
</div>
1
2
3
4

Se eu passar o link:

<div>
  <h1>Titulo Fixo do Card</h1>
  <p>Descrição qualquer, ... </p>
  <a href="#">algum lugar</a>
</div>
1
2
3
4
5

Basicamente são espaços reservados para outros conteudos, mesmo um elemento permitindo passar slots, não é obrigatorio passar só pq ele permite, você que escolhe, é apenas um espaço reservado para aquele item, se ele não for passado OK.

Outra coisa, o slot passado pode ser um texto, tag, outro componente, ...

# v-pre

Nos permite ignorar a compilação para o elemento em questão e todos os seus filhos.

Podemos usar para imprimir o mustache.

Se usarmos muitas vezes esta tecnica pode nos permitir aceleração na compilação.

Se quisermos imprimir por exemplo este mustache , podemos fazer assim:

<h1 v-pre>{{ nomeDoUsuario }}</h1>
1

Logo a saida será um H1 com o conteudo {{nomeDoUsuario}} dentro.

# Diretiva personalizada

Como podemos ver as diretivas são atributos especiais com o prefixo v-

Elas tem o papel de aplicar reativamente efeitos colaterais ao DOM quando o seu valor for alterado.

Vimos que o Vue fornece uma ampla variedade de diretivas para usarmos.

# NOME

Para criar uma diretiva o mais basico que precisamos informar é um nome, somente informando um nome ela não aceita argumentos e nem possui modificadores.

Se não for possivel passarmos um valor ela basicamente nao será muito flexível mas pode ter alguma funcionalidade do DOM.

O modelo mais basico de uma diretiva seria o v-else.

Um exemplo de diretiva basica poderia ser este:

<app-navigation v-sticky> </app-navigation>
1

# VALOR

Podemos passar um valor para um diretiva, por exemplo o v-else:

<div v-if="mostrar">Pode ser mostrado</div>
1

Poderiamos passar uma string:

<div v-fruta="'banana'"></div>
1

Que seria mais ou menos igual ao v-html.

# ARGUMENTOS

Diretivas personalizadas podem receber um argumento indicado por dois-pontos após o nome da diretiva.

Um exemplo:

<app-navigation v-sticky:bottom> </app-navigation>
1

Nome da diretiva é sticky e o argumento button.

As diretivas podem ter apenas um argumento.

# MODIFICADORES

São correções especiais indicadas por um ponto que indicam que uma diretiva deve ser vinculada de alguma maneira especial.

Serve para controlar como a diretiva se comporta.

<span v-formatar.sublinhado>Texto</span>
1

Neste exemplo o modificador aplica um sublinhado ao texto. Podemos usar varios modificadores encadeados ao mesmo tempo.

<span v-formatar.negrito.destacado.sublinhado>Texto</span>
1

Neste outro exemplo o texto estará em negrito, destacado e com sublinhado.

# HOOKS

O VUE dispobiliza algumas hooks para usarmos nas diretivas.

  • bind - isso ocorre uma vez quando a diretiva é anexada ao elemento.
  • inserted - isso ocorre quando o elemento é inserido no DOM pai
  • update - isso ocorre quando o elemento é atualizado, mas as crianças ainda não foram atualizadas
  • componentUpdated - isso ocorre quando o componente e os filhos foram atualizados
  • unbind - isto ocorre quando a diretiva é removida

E cada um deles possuem os argumentos el , binding e vnode argumentos disponíveis para eles:

  • el - o elemento em que a ligação está
  • binding - um objeto que contém os argumentos que são passados ​​para os ganchos. Existem muitos argumentos disponíveis, incluindo name, value, oldValue, expression, arg e modifiers.
  • vnode - permite referir-se diretamente ao nó no DOM virtual, se necessário. Tanto a binding quanto o vnode devem ser tratados como somente leitura.
  • update e componentUpdated - ambos expõem um argumento adicional chamado oldvnode. O argumento oldvnode é usado para diferenciar entre o valor mais antigo passado e o valor mais recente. bind e update são os mais úteis dos cinco.

# CRIANDO

Podemos criar diretivas globalmente na nossa aplicacao por meio do Vue.directive e passando um nome para ela:

// No main.js
Vue.directive ('sticky');
1
2

Para usar esta diretiva podemos fazer assim:

<app-navigation v-sticky></app-navigation>
1

# EXEMPLOS

Deixa o componente fixo na tela

<app-navigation v-sticky></app-navigation>
1
Vue.directive('sticky',
    function(el, binding, vnode) {
        el.style.position = 'fixed';
    }
))
1
2
3
4
5

Define a cor do texto para laranja

<h1 v-orange>Ola mundo</h1>
1
Vue.directive("orange", function(el, binding, vnode) {
    el.style.color = "orange";
})
1
2
3

Passando uma cor para a diretiva

<h1 v-color="'red'">{{ msg }}</h1>
<h1 v-color="'blue'">{{ msg }}</h1>
<h1 v-color="'#ffff00'">{{ msg }}</h1>
1
2
3
Vue.directive("color", function(el, binding, vnode) {
    el.style.color = binding.value;
})
1
2
3

Passando argumento para definir se vai ficar fixo no top ou bottom da tela. Se não for passado nada vai ser no top

<!-- (top -->
<app-navigation v-sticky></app-navigation>

<!-- (bottom -->
<app-navigation v-sticky:bottom></app-navigation>
1
2
3
4
5
Vue.directive("sticky", function(el, binding, vnode) {
  const loc = binding.arg === "bottom" ? "bottom" : "top";
  el.style.position = "fixed";
  el.style[loc] = 0;
  if (loc === "bottom") {
    el.style.background = "burlywood";
  } else {
    el.style.background = "#7e7e7e";
  }
})
1
2
3
4
5
6
7
8
9
10
<div v-example a="hi"></div>
1
Vue.directive('example', {
  params: ['a'],
  bind: function () {
    console.log(this.params.a) // -> "hi"
  }
})
1
2
3
4
5
6

Modificadores

<span v-format.underline>guide</span>
<span v-format.bold>configure / customize</span>
<span v-format.highlight>check out</span>
<h3 v-format.highlight.bold.underline>Installed CLI Plugins</h3>
1
2
3
4
Vue.directive("format", function(el, binding, vnode) {
  const modifiers = binding.modifiers;
  if (modifiers.underline) {
    el.style.textDecoration = "underline";
  }
  if (modifiers.bold) {
    el.style.fontWeight = "bold";
  }
  if (modifiers.highlight) {
    el.style.background = "#ffff00";
  }
})
1
2
3
4
5
6
7
8
9
10
11
12

Hooks em ação

<template>
  <div class="about">
    <h1>This is an about page</h1>

    <div v-hook-demo>{{ count }}</div>
    <button @click="++count;">Update</button>
  </div>
</template>
<script>
export default {
  name: "About",
  data() {
    return {
      count: 0
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Vue.directive("hook-demo", {
  bind(el, binding, vnode) {
    console.log("bind");
  },
  inserted(el, binding, vndoe) {
    console.log("inserted");
  },
  updated(el, binding, vnode) {
    console.log("updated");
  },
  componentUpdated(el, binding, vnode, oldVnode) {
    console.log("componentUpdated");
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Vue.directive('phone', {
  bind (el, binding, vnode) {
    console.log('bind')
    console.log('bd elemento: ', el)
    console.log('bd binding: ', binding)
    console.log('bd valor: ', vnode)
    const modifiers = binding.modifiers

    if (modifiers.mask) {
      el.mask('####-####', { placeholder: '0000-0000' })
      el.change(function () {
        let value = this.val()
        el.set(value)
      })
    }
  },
  update (el, binding, vnode) {
    console.log('update')
    console.log('up elemento: ', el)
    console.log('up binding: ', binding)
    console.log('up valor: ', vnode)
    const modifiers = binding.modifiers

    if (modifiers.mask) {
      el.mask('####-####', { placeholder: '0000-0000' })
      el.change(function () {
        let value = this.val()
        el.set(value)
      })
    }
  }
})
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
Vue.directive('date', {
    'twoWay': true,
    'bind': function () {
        var self = this

        self.mask = "99/99/9999"
        $(self.el).mask(self.mask, { placeholder:"MM/DD/YYYY" })
        $(self.el).change(function() {
            var value = $(this).val()
            self.set(value)
        })
    },
    'unbind': function () {
        var self = this

        $(self.el).unmask(self.mask)
    }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# Armazenamento (Bonus)

Uma coisa que podemos fazer tambem é criar um diretorio para concentrar todas as diretivas por arquivos individuais facilitando assim a manutencao.

Entao para este exemplo, dentro do diretorio directives criar um arquivo chamado background-directive.js com o seguinte conteudo:

const defaultBackgroundColor = "#86bbff";

const BackgroundDirective. = {
  bind(el, binding, vnode) {
    const color = binding.value || defaultBackgroundColor;

    if (el) {=
      el.style.backgroundColor = color;
    }
  }
};

export default BackgroundDirective;
1
2
3
4
5
6
7
8
9
10
11
12
13

Neste momento criamos uma diretiva, para melhor exemplificar vou criar só mais uma de teste.

Ainda dentro do diretorio directives criar agora um arquivo chamado red-color-directive.js com o seguinte conteudo:

const defaultColor = "#86bbff";

const RedColorDirective. = {
  bind(el, binding, vnode) {
    const color = binding.value || defaultColor;

    if (el) {=
      el.style.color = color;
    }
  }
};

export default RedColorDirective;
1
2
3
4
5
6
7
8
9
10
11
12
13

Beleza, agora vamos no mais.js para declarar estas diretivas.

import Vue from "vue";
import App from "./App.vue";
import BackgroundDirective from "@/directives/background-directive.js";
import RedColorDirective from "@/directives/red-color-directive.js";

const Directives = {
  "background-dir": BackgroundDirective,
  "color-dir": RedColorDirective
};

Object.keys(Directives).forEach(name => {
    Vue.directive(name, Directives[name]);
});

Vue.config.productionTip = false;

new Vue({
  render: h => h(App)
}).$mount("#app");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

Para usar as diretivas agora, em algum algum compotente/view:

<span v-background-dir>guide</span>
<span v-background-dir="'0f0'">guide</span>

<span v-color-dir>guide</span>
<span v-color-dir="'0f0'">guide</span>
1
2
3
4
5