阅读之前
你应该:
- 了解DSL相关内容。母版-布局-DSL 渲染基础(v4)
- 了解SPI机制相关内容。组件SPI机制(v4.3.0)
自定义组件简介
前面我们简单介绍过一个简单的自定义组件该如何被定义,并应用于页面中。这篇文章将对自定义组件进行详细介绍。
自定义一个带有具名插槽的容器组件(一般用于Object数据类型的视图中)
使用BasePackWidget组件
进行注册,最终体现在DSL模板中为<pack widget="SlotDemo">
。
SlotDemoWidget.ts
import { BasePackWidget, SPI } from '@kunlun/dependencies';
import SlotDemo from './SlotDemo.vue';
@SPI.ClassFactory(BasePackWidget.Token({ widget: 'SlotDemo' }))
export class SlotDemoWidget extends BasePackWidget {
public initialize(props) {
super.initialize(props);
this.setComponent(SlotDemo);
return this;
}
}
定义一个Vue组件,包含三个插槽,分别是default不具名插槽
、title具名插槽
、footer具名插槽
。
SlotDemo.vue
<template>
<div class="slot-demo-wrapper" v-show="!invisible">
<div class="title">
<slot name="title" />
</div>
<div class="content">
<slot />
</div>
<div class="footer">
<slot name="footer" />
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'SlotDemo',
props: {
invisible: {
type: Boolean,
default: undefined
}
}
});
</script>
在一个表单(FORM)
的DSL模板中,我们可以这样使用这三个插槽:
<view type="FORM">
<pack widget="SlotDemo">
<template slot="default">
<field data="id" />
</template>
<template slot="title">
<field data="name" />
</template>
<template slot="footer">
<field data="isEnabled" />
</template>
</pack>
</view>
这样定义的一个组件插槽和DSL模板就进行了渲染上的结合。
针对不具名插槽的特性,我们可以缺省slot="default"
标签,缺少template标签
包裹的所有元素都将被收集到default不具名插槽
中进行渲染,则上述DSL模板可以改为:
<view type="FORM">
<pack widget="SlotDemo">
<field data="id" />
<template slot="title">
<field data="name" />
</template>
<template slot="footer">
<field data="isEnabled" />
</template>
</pack>
</view>
自定义一个数组渲染组件(一般用于List数据类型的视图中)
由于表格无法体现DSL模板渲染的相关能力,因此我们以画廊视图(GALLERY)
进行演示。
先定义一个数组每一项的数据结构:
typing.ts
export interface ListItem {
key: string;
data: Record<string, unknown>;
index: number;
}
ListRenderDemoWidget.ts
import { BaseElementListViewWidget, BaseElementWidget, SPI } from '@kunlun/dependencies';
import ListRenderDemo from './ListRenderDemo.vue';
@SPI.ClassFactory(BaseElementWidget.Token({ widget: 'ListRenderDemo' }))
export class ListRenderDemoWidget extends BaseElementListViewWidget {
public initialize(props) {
super.initialize(props);
this.setComponent(ListRenderDemo);
return this;
}
}
ListItemDemoWidget.ts
import { ActiveRecord, ActiveRecordsOperator, BaseElementWidget, SPI, Widget } from '@kunlun/dependencies';
import ListItemDemo from './ListItemDemo.vue';
import { ListItem } from './typing';
@SPI.ClassFactory(BaseElementWidget.Token({ widget: 'ListItemDemo' }))
export class ListItemDemoWidget extends BaseElementWidget {
@Widget.Reactive()
protected get formData(): ActiveRecord {
return this.activeRecords?.[0] || {};
}
@Widget.Reactive()
protected rowIndex: number | undefined;
public initialize(props) {
const slotContext = props.slotContext as ListItem;
if (slotContext) {
props.activeRecords = ActiveRecordsOperator.repairRecords(slotContext.data);
this.rowIndex = slotContext.index;
}
super.initialize(props);
this.setComponent(ListItemDemo);
return this;
}
}
ListRenderDemo.vue
<template>
<div class="list-render-demo-wrapper">
<div class="list-render-item-wrapper" v-for="item in list" :key="item.key">
<slot v-bind="item" />
</div>
</div>
</template>
<script lang="ts">
import { ActiveRecord, uniqueKeyGenerator } from '@kunlun/dependencies';
import { computed, defineComponent, PropType } from 'vue';
import { ListItem } from './typing';
export default defineComponent({
name: 'ListRenderDemo',
props: {
showDataSource: {
type: Array as PropType<ActiveRecord[]>
}
},
setup(props) {
const list = computed<ListItem[]>(() => {
return (
props.showDataSource?.map((data, index) => {
const item: ListItem = {
key: data.__draftId || uniqueKeyGenerator(),
data,
index
};
return item;
}) || []
);
});
return {
list
};
}
});
</script>
ListItemDemo.vue
<template>
<div class="list-item-demo">
<div class="title">
<slot name="title" />
</div>
<div class="content">
<slot />
</div>
<div class="footer">
<slot name="footer" />
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'ListItemDemo'
});
</script>
在一个画廊(GALLERY)
的DSL模板中,我们可以这样使用这三个插槽:
<view type="GALLERY">
<template slot="gallery" widget="ListRenderDemo">
<element widget="ListItemDemo">
<template slot="default">
<field data="id" />
</template>
<template slot="title">
<field data="name" />
</template>
<template slot="footer">
<field data="isEnabled" />
</template>
</element>
</template>
</view>
PS:从默认提供的画廊视图(GALLERY)
布局来看,我们可以通过slot="gallery"
插槽完全替换下面的子标签,并指定widget="ListRenderDemo"
来使用我们的自定义组件。
Oinone社区 作者:oinone原创文章,如若转载,请注明出处:https://doc.oinone.top/frontend/24.html
访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验