Neste exemplo, implementaremos uma diretiva personalizada no Vue.js que converte uma string contendo variáveis em elementos HTML enterativos, permitindo a entrada de dados pelo usuário. A solução integra Vuex para gerenciamneto de estado e utiliza componentes do Element UI para os campos de entrada.
Exemplo de Texto com Variáveis:
"Contrato celebrado entre $var<empresa_contratante> (CNPJ: $var<cnpj_empresa>) e $var<nome_cliente> (CPF: $var<cpf_cliente>). Valor total: $var<valor_total> em $var<data_vencimento>."
Configuração do Vuex:
const mutations = { [TIPOS.ATUALIZAR_ESTADO]: (estado, { chave, valor }) => { estado[chave] = valor; }, }; export default mutations;
</details><details><summary>Actions</summary>```
const actions = {
atualizarValorEstado({ commit }, { chave, valor, retorno }) {
commit(TIPOS.ATUALIZAR_ESTADO, { chave, valor });
if (retorno) retorno();
},
};
export default actions;
<div class="container-texto" v-renderizar-h="{conteudo: item, modoLeitura: false}"></div>
Estilos CSS (ajustes visuais para os campos):
/deep/.container-texto {
.el-input {
width: 250px !important;
.el-input__inner {
border-left: none !important;
border-right: none !important;
border-top: none !important;
border-bottom: 1px solid #DCDFE6;
&:hover {
border-bottom: 1px solid #14DAAF;
}
}
&.campo-readonly {
.el-input__inner {
border-bottom: 1px solid #DCDFE6 !important;
}
}
}
}
Implementação da Diretiva Personalizada:
const instalacao = function (Vue) { Vue.directive('renderizarHtml', renderizarHtml); };
if (window.Vue) { window['renderizarHtml'] = renderizarHtml; Vue.use(instalacao); } renderizarHtml.instalacao = instalacao; export default renderizarHtml;
</details><details><summary>Lógica da Diretiva (renderizarHtml.js)</summary>```
import {
verificarPadrao,
extrairVariaveis
} from "@/utils/transformacao";
import store from '@/store';
import { Input, DatePicker, Message } from 'element-ui';
import Vue from 'vue';
export default {
inserido(el, binding, vnode) {
const { conteudo, modoLeitura } = binding.value;
const listaVariaveis = extrairVariaveis(conteudo);
const dadosArmazenados = store.getters?.obterDadosContrato || [];
let textoProcessado = conteudo;
listaVariaveis.forEach(variavel => {
textoProcessado = textoProcessado.replace(variavel.padrao, `--SEPARADOR--${variavel.identificador}--SEPARADOR--`);
});
const fragmentos = textoProcessado.split('--SEPARADOR--');
fragmentos.forEach(fragmento => {
const itemEncontrado = dadosArmazenados.find(dado => dado.identificador === fragmento);
if (itemEncontrado) {
let instanciaComponente = null;
if (fragmento.includes("texto_") || fragmento.includes("dinheiro_")) {
instanciaComponente = new Vue({
render(h) {
return h(Input, {
attrs: { id: fragmento },
class: modoLeitura ? 'campo-readonly' : '',
props: {
placeholder: modoLeitura ? '' : (fragmento.includes("texto_") ? 'Insira texto' : 'Insira valor monetário'),
type: 'text',
readonly: modoLeitura,
},
model: {
value: this.valorCampo,
callback: this.atualizarValor,
expression: 'valorCampo',
},
on: { change: this.processarAlteracao },
});
},
data() {
return {
valorCampo: itemEncontrado.valor || '',
};
},
methods: {
atualizarValor(novoValor) {
this.valorCampo = novoValor;
},
processarAlteracao(evento) {
if (fragmento.includes("dinheiro_")) {
const regexMonetario = /^(\d+(\.\d{1,7})?)$/;
if (!regexMonetario.test(evento)) {
Message.error("Formato de valor inválido");
this.valorCampo = '';
return;
}
}
const idCampo = this.$el.querySelector('input')?.getAttribute('id');
const dadosAtualizados = (store.getters?.obterDadosContrato || []).map(item => {
if (item.identificador === idCampo) {
item.valor = evento;
}
return item;
});
store.dispatch("atualizarValorEstado", {
chave: "dadosContrato",
valor: dadosAtualizados,
});
},
},
}).$mount();
} else if (fragmento.includes("data_")) {
instanciaComponente = new Vue({
render(h) {
return h(DatePicker, {
attrs: { id: fragmento },
class: modoLeitura ? 'campo-readonly' : '',
props: {
placeholder: modoLeitura ? '' : 'Selecione uma data',
type: 'date',
valueFormat: 'yyyy-MM-dd',
readonly: modoLeitura,
},
model: {
value: this.valorCampo,
callback: this.atualizarValor,
expression: 'valorCampo',
},
on: { change: this.processarAlteracao },
});
},
data() {
return {
valorCampo: itemEncontrado.valor || '',
};
},
methods: {
atualizarValor(novoValor) {
this.valorCampo = novoValor;
},
processarAlteracao(evento) {
const idCampo = this.$el.querySelector('input')?.getAttribute('id');
const dadosAtualizados = (store.getters?.obterDadosContrato || []).map(item => {
if (item.identificador === idCampo) {
item.valor = evento;
}
return item;
});
store.dispatch("atualizarValorEstado", {
chave: "dadosContrato",
valor: dadosAtualizados,
});
},
},
}).$mount();
}
if (instanciaComponente) {
el.appendChild(instanciaComponente.$el);
}
} else {
el.appendChild(document.createTextNode(fragmento));
}
});
},
desvinculado(el) {
const primeiroFilho = el.firstChild;
if (primeiroFilho && primeiroFilho.$destroy) {
primeiroFilho.$destroy();
}
},
};
import renderizarHtml from "@/utils/renderizarHtml";
Vue.use(renderizarHtml);
Utilitários para Processamento de Texto:
/**
* Verifica se uma string contém o padrão de variável.
* @param {string} texto - Texto a ser verificado.
* @returns {boolean} Verdadeiro se o padrão for encontrado.
*/
const verificarPadrao = (texto) => {
const expressaoRegular = /\$var<[a-zA-Z\d_]+>/;
return expressaoRegular.test(texto);
};
/**
* Extrai variáveis de uma string com base em um padrão específico.
* @param {string} texto - Texto contendo variáveis.
* @returns {Array} Lista de objetos com identificador e padrão da variável.
*/
const extrairVariaveis = (texto) => {
const variaveis = [];
const expressaoRegular = /\$var<([^>]+)>/g;
let correspondencia;
while ((correspondencia = expressaoRegular.exec(texto)) !== null) {
variaveis.push({
identificador: correspondencia[1],
padrao: correspondencia[0],
valor: '',
posicao: correspondencia.index,
});
}
return variaveis;
};
export { verificarPadrao, extrairVariaveis };