Directiva personalizada en Vue 2 para insertar componentes dinámicos en el DOM

Archivo de directiva: dinamizarDom.js

import { extraerVariablesCadena } from "@/utilidades"; import almacen from '@/almacen'; import { Input, SelectorFecha, Mensaje } from 'element-ui'; import Vue from 'vue';

export default { insertado(el, enlace, nodo) { const { valor, soloLectura } = enlace.valor; let variablesExtraidas = extraerVariablesCadena(valor); let datosProtocolo = almacen.obtDatosProtocolo || []; let cadenaProcesada = valor;

(variablesExtraidas || []).forEach(elemento => {
  cadenaProcesada = cadenaProcesada.replace(elemento.clave, `--#${--${elemento.identificador}--#--`);
});

let partesCadena = cadenaProcesada.split('--#--');
let casoActual = datosProtocolo.length ? almacen.obtCasoActual : null;
let tiposEditables = ["preparado", "texto", "fecha", "monto"];

(partesCadena || []).forEach(segmento => {
  if (datosProtocolo.some(v => v.identificador == segmento)) {
    let objetoActual = datosProtocolo.find(v => v.identificador == segmento);
    let instanciaVue = null;

    if (soloLectura) {
      if (segmento.includes("preparado")) {
        let valorMostrar = "";
        if (segmento.includes("preparadoNombre")) valorMostrar = casoActual?.nombre_caso || "";
        else if (segmento.includes("preparadoId")) valorMostrar = casoActual?.identificacion || "";
        else if (segmento.includes("preparadoNumero")) valorMostrar = casoActual?.numero_caso || "";
        else if (segmento.includes("preparadoPlataforma")) valorMostrar = casoActual?.plataforma || "";
        else if (segmento.includes("preparadoUnidad")) valorMostrar = casoActual?.unidad_aceptacion || "";
        else if (segmento.includes("preparadoMonto")) valorMostrar = casoActual?.monto || "";
        else if (segmento.includes("preparadoRazon")) valorMostrar = casoActual?.razon || "";

        instanciaVue = new Vue({
          render(h) {
            return h('span', {
              attrs: { id: segmento },
              class: "valor_personalizado"
            }, [valorMostrar || "________________"]);
          },
        }).$mount();
      } else {
        instanciaVue = new Vue({
          render(h) {
            return h('span', {
              attrs: { id: segmento },
              class: objetoActual?.valor ? "caja_personalizada" : null
            }, [objetoActual?.valor || '________________']);
          },
        }).$mount();
      }
    } else {
      if (segmento.includes("texto_") || segmento.includes("monto_")) {
        instanciaVue = new Vue({
          render(h) {
            return h(Input, {
              attrs: { id: segmento },
              class: soloLectura ? "solo_lectura" : "",
              props: {
                type: "text",
                readonly: soloLectura,
                placeholder: "Introduzca contenido",
              },
              model: {
                value: this.valorInput,
                callback: this.manejarEntrada,
                expression: 'valorInput',
              },
              on: { change: this.manejarCambio },
            });
          },
          data() {
            return {
              valorInput: objetoActual?.valor || '',
              placeholder: soloLectura ? "" : segmento.includes("texto_") ? 'Introduzca contenido' : 'Introduzca monto',
            };
          },
          methods: {
            manejarEntrada(nuevoValor) {
              this.valorInput = nuevoValor;
            },
            manejarCambio(evento) {
              if (segmento.includes("monto_")) {
                let regexMonto = /^(\d+(\.\d{1,7})?)$/;
                if (!regexMonto.test(evento)) {
                  Mensaje.error("Formato de monto incorrecto");
                  this.valorInput = "";
                  return;
                }
              }
              let elementoInput = this.$el.querySelector('input');
              let idTemp = elementoInput.getAttribute('id');
              let datosProtocoloCopia = JSON.parse(JSON.stringify(almacen.obtDatosProtocolo || []));
              let datosActualizados = datosProtocoloCopia.map(item => {
                if (item.identificador == idTemp) item.valor = evento;
                return item;
              });
              almacen.despachar("cambiarEstado", { clave: "datosProtocolo", valor: datosActualizados });
            },
          },
        }).$mount();
      } else if (segmento.includes("fecha_")) {
        instanciaVue = new Vue({
          render(h) {
            return h(SelectorFecha, {
              attrs: { id: segmento },
              class: soloLectura ? "solo_lectura" : "",
              props: {
                placeholder: this.placeholder,
                type: "date",
                valueFormat: "yyyy-MM-dd",
                readonly: soloLectura,
              },
              model: {
                value: this.valorFecha,
                callback: this.manejarFecha,
                expression: 'valorFecha',
              },
              on: { change: this.manejarCambio },
            });
          },
          data() {
            return {
              valorFecha: objetoActual?.valor || '',
              placeholder: soloLectura ? "" : 'Seleccione fecha',
            };
          },
          methods: {
            manejarFecha(nuevoValor) {
              this.valorFecha = nuevoValor;
            },
            manejarCambio(evento) {
              let elementoInput = this.$el.querySelector('input');
              let idTemp = elementoInput.getAttribute('id');
              let datosProtocoloCopia = JSON.parse(JSON.stringify(almacen.obtDatosProtocolo || []));
              let datosActualizados = datosProtocoloCopia.map(item => {
                if (item.identificador == idTemp) item.valor = evento;
                return item;
              });
              almacen.despachar("cambiarEstado", { clave: "datosProtocolo", valor: datosActualizados });
            },
          },
        }).$mount();
      } else if (segmento.includes("preparado")) {
        let valorMostrar = "";
        if (segmento.includes("preparadoNombre")) valorMostrar = casoActual?.nombre_caso || "";
        else if (segmento.includes("preparadoId")) valorMostrar = casoActual?.identificacion || "";
        else if (segmento.includes("preparadoNumero")) valorMostrar = casoActual?.numero_caso || "";
        else if (segmento.includes("preparadoPlataforma")) valorMostrar = casoActual?.plataforma || "";
        else if (segmento.includes("preparadoUnidad")) valorMostrar = casoActual?.unidad_aceptacion || "";
        else if (segmento.includes("preparadoMonto")) valorMostrar = casoActual?.monto || "";
        else if (segmento.includes("preparadoRazon")) valorMostrar = casoActual?.razon || "";

        instanciaVue = new Vue({
          render(h) {
            return h('span', {
              attrs: { id: segmento },
              class: "valor_personalizado"
            }, [valorMostrar || "________________"]);
          },
        }).$mount();
      }
    }

    if (instanciaVue) el.appendChild(instanciaVue.$el);
  } else {
    if (tiposEditables.some(t => segmento.includes(t) && segmento.includes("_"))) {
      el.appendChild(document.createTextNode("________________"));
    } else {
      el.appendChild(document.createTextNode(segmento));
    }
  }
});

}, desenlazar(el) { if (el.firstChild && el.firstChild.$destruir) el.firstChild.$destruir(); } };


</details>### Archivo de registro: registro.js

<details><summary>registro.js</summary>```

import dinamizarDom from "./dinamizarDom";
import Vue from 'vue';

const instalar = function(Vue) {
  Vue.directive('dinamizarDom', dinamizarDom);
};

if (window.Vue) {
  window['dinamizarDom'] = dinamizarDom;
  Vue.use(instalar);
}

dinamizarDom.instalar = instalar;
export default dinamizarDom;

import dinamizarDom from "@/utilidades/dinamizarDom";
Vue.use(dinamizarDom);### Ejemplo de uso

<div v-dinamizar-dom="{ valor: textoPlantilla, soloLectura: modoLectura }"></div>Donde textoPlantilla es una cadena con variables marcadas y modoLectura es un booleano.

Etiquetas: Vue2 directivas-personalizadas vuex element-ui manipulacion-dom-dinamica

Publicado el 6-17 17:16