我拥有在全球范围内注册的组件。我使用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>
任何解决方案?