如何在动态渲染的组件上渲染无线电和复选框组?

时间:2019-10-24 12:49:48

标签: vue.js

我拥有在全球范围内注册的组件。我使用is指令和v-for循环来动态渲染组件列表。

这导致我遇到一个问题,即我无法正确显示一组复选框和收音机。

我阅读了文档,发现与v-if一起使用v-for是一种不好的做法,所以我决定避免使用它。

请看下面我的代码,请忽略丑陋的props值,因为它是通过API响应获取的:

BaseCheckbox.vue

<template>
  <div class="form-group">
    <div class="custom-control custom-checkbox">
      <input
        type="checkbox"
        class="custom-control-input"
        :name="name"
        :id="id"
        :checked="value"
        @click="$emit('input', $event.target.checked)"
      />
      <label class="custom-control-label" :for="id">{{ label }}</label>
    </div>
  </div>
</template>

<script>
  export default {
    props: {
      id: {
        type: String,
        required: true
      },
      name: {
        type: String,
        required: true
      },
      value: {
        type: [String, Boolean],
        required: true
      },
      label: {
        type: String,
        required: true
      }
    },
    data() {
      return {
        checked: false
      };
    },
    methods: {
      check() {
        this.checked = !this.checked;
      }
    }
  };
</script>

BaseRadio.vue

<template>
  <div class="custom-control custom-radio" @click="select">
    <input type="radio" :id="id" :name="name" class="custom-control-input" :value="value" />
    <label class="custom-control-label" :for="id">{{ label }}</label>
  </div>
</template>

<script>
  export default {
    props: {
      id: {
        type: String,
        required: true
      },
      name: {
        type: String,
        required: true
      },
      label: {
        type: String,
        required: true
      },
      value: {
        type: String,
        required: true
      }
    },
    methods: {
      select() {
        this.$emit("change", this.value);
      }
    }
  };
</script>

<style>
</style>

ComponentRenderer.vue

<template>
  <div class="component-renderer">
    <!-- Parent component -->
    <Component
      :is="matches('Base', component)"
      :id="component.id.toString()"
      :name="slugged(component.title, component.type)"
      :label="component.title"
      :text="component.title"
      :content="component.title"
      :placeholder="component.placeholder"
      :value="component.options"
      :items="component.options"
      :position="!!component.options.value ? component.options.value : component.options"
      :size="!!component.subtype.value ? component.subtype.value : component.subtype"
      :required="component.required"
      name-obj="textOption"
      val-obj="valueOption"
      @click.native.prevent="select(component)"
    >
      <!-- Card header if any -->
      <template #header v-if="component.title">
        <h3 class="font-weight-bold" v-text="component.title"></h3>
        <p class="mb-0" v-text="component.options"></p>
      </template>

      <!-- Child component -->
      <Component
        v-for="(child, index) in component.components"
        :key="index"
        :is="matches('Base', child)"
        :id="child.id.toString()"
        :name="slugged(child.title, child.type)"
        :label="child.title"
        :text="child.title"
        :content="child.title"
        :placeholder="child.placeholder"
        :value="child.options"
        :items="child.options"
        :position="!!child.options.value ? child.options.value : child.options"
        :size="!!child.subtype.value ? child.subtype.value : child.subtype"
        :required="child.required"
        name-obj="textOption"
        val-obj="valueOption"
        @click.native.stop="select(child)"
      />
    </Component>

    <!-- Component Configuration Sidebar -->
    <Component :is="matches('ConfigureBase', selected)" v-if="selected" :config="selected" />
  </div>
</template>

<script>
  export default {
    props: {
      component: {
        type: Object,
        required: true
      }
    },
    data() {
      return {
        resizeSensor: null,
        clientHeight: null,
        selected: null
      };
    },
    mounted() {
      this.autoResizeParent();
      this.listenConfigPanelEvent();
    },
    methods: {
      /**
       * Set selected data when user click at a component.
       *
       * @param {Object} component
       * @return void
       */
      select(component) {
        this.selected = component;
      },

      /**
       * Convert component type string to match global-registered
       * base component name.
       *
       * @param {String} base ['NonSeparatedWords']
       * @param {Object} component
       * @return {String} BaseComponent
       */
      matches(base, component) {
        return base + _.capitalize(component.type);
      },

      /**
       * Handle size of the parent component as the child is being
       * added more and more.
       *
       * @return void
       */
      autoResizeParent() {
        const vm = this;
        this.gridItem = this.$parent;

        this.resizeSensor = new ResizeSensor(this.$el, () => {
          const widgetHeight = this.$el.clientHeight;

          if (widgetHeight !== vm.widgetHeight) {
            const h = Math.ceil(
              (widgetHeight + vm.gridItem.margin[1]) /
                (vm.gridItem.rowHeight + vm.gridItem.margin[1])
            );

            const newH = h;

            vm.widgetHeight = widgetHeight;

            vm.gridItem.eventBus.$emit(
              "resizeEvent",
              "resizeend",
              vm.gridItem.i,
              vm.gridItem.x,
              vm.gridItem.y,
              newH,
              vm.gridItem.w
            );
          }
        });

        this.clientHeight = this.$el.clientHeight;
      },

      /**
       * Slugify strings.
       *
       * @param {String} str
       * @return {String} ['something-separated-with-whitespaces']
       */
      slugify(str) {
        str = str.replace(/^\s+|\s+$/g, "").toLowerCase(); // trim & force lowercase

        str = str
          .replace(/[^a-z0-9 -]/g, "") // remove invalid chars
          .replace(/\s+/g, "-") // collapse whitespace and replace by -
          .replace(/-+/g, "-"); // collapse dashes

        return str;
      },

      /**
       * Combine title and type as a slugged name.
       *
       * @param {String} title
       * @param {String} type
       * @return {String} ['title-type']
       */
      slugged(title, type) {
        return this.slugify(title) + "-" + this.slugify(type);
      },

      /**
       * Listen to config panel events.
       *
       * @return void
       */
      listenConfigPanelEvent() {
        EventBus.$on("hide-sidebar", () => {
          this.selected = null;
        });
      }
    }
  };
</script>

任何解决方案?

0 个答案:

没有答案