本文介绍如何在 Piying-View 中定义和使用组件,包括创建多个组件、非表单控件、属性操作、事件输出、上下文等。
| 类型 |
说明 |
| 普通组件 |
没有实现 ControlValueAccessor 的组件 |
| 控件(Control) |
实现 ControlValueAccessor 的表单控件 |
| 普通分组 |
可包含子组件的容器组件 |
| 逻辑分组 |
intersect(and)/ union(or)逻辑组合 |
默认提供基础的 object 组件,用来展示内部子组件,也可自定义显示方式或增加包装器。
component-use.definition.ts
import { actions } from '@piying/view-angular';
import * as v from 'valibot';
import { NFCSchema, setComponent } from '@piying/view-angular-core';
export const schema = v.pipe(
v.object({
k1: v.pipe(
v.string(),
v.title('k1-label'),
actions.wrappers.set(['label']),
),
k2: v.pipe(v.number(), v.title('k2-label'), v.minValue(10)),
__helper: v.pipe(NFCSchema, setComponent('formHelper')),
}),
setComponent('fieldset'),
);
- 基础 object 组件:
v.object({ k1: v.string(), k2: v.number() }) 默认展示内部子组件
- 自定义显示:通过
setComponent('fieldset') 修改为 fieldset 分组展示
- 增加包装器:通过
actions.wrappers.set(['label']) 添加标签,v.minValue(10) 添加验证
默认情况下,Valibot 的强类型定义使所有输入默认为表单控件。若需定义为非表单控件,可使用 NFCSchema(v.optional(v.void())),使用后必须用 setComponent 手动指定组件:
nfc.definition.ts
import * as v from 'valibot';
import { NFCSchema, setComponent, actions } from '@piying/view-angular-core';
export const schema = v.pipe(
v.object({
__k1: v.pipe(NFCSchema, setComponent('string'), actions.wrappers.set([])),
__k2: v.pipe(
v.optional(v.void()),
setComponent('string'),
actions.wrappers.set([]),
),
}),
setComponent('fieldset'),
);
NFCSchema:NonFieldControlSchema,用于定义非表单控件。
通过 actions.inputs 设置输入属性,并通过 outputChange 监听事件输出:
action.definition.ts
import { actions } from '@piying/view-angular';
import * as v from 'valibot';
import {
NFCSchema,
outputChange,
setComponent,
} from '@piying/view-angular-core';
export const schema = v.pipe(
v.object({
input: v.pipe(v.string(),),
__btn: v.pipe(
NFCSchema,
setComponent('demo'),
actions.inputs.set({ input1: 1 }),
outputChange((fn) => {
fn([{ list: undefined, output: 'output1' }]).subscribe(({ field }) => {
console.log('output received', field);
});
}),
),
}),
setComponent('fieldset'),
);
- 静态属性:
actions.inputs.set({ input1: 1 }) 直接设置输入值
- 动态变更:
actions.inputs.patchAsync({ input1: async (field) => 1 }) 支持 Promise/Observable/Signal
- 事件输出:
outputChange 同时监听多个组件、多个输出,再根据逻辑关系处理
通过传入 context 使得组件可以通过上下文动态获取参数:
context.definition.ts
import * as v from 'valibot';
import { setComponent, valueChange } from '@piying/view-angular-core';
import { BehaviorSubject } from 'rxjs';
export const context = {
lastValue$: new BehaviorSubject<string | undefined>(undefined),
};
export const options = { context };
export const schema = v.pipe(
v.object({
input1: v.pipe(
v.string(),
valueChange((fn) => {
fn({ list: [undefined] }).subscribe(({ list, field }) => {
console.log('prev:', field.context.lastValue$.value);
field.context.lastValue$.next(list[0]);
});
}),
),
}),
);
所有设置操作都基于 rawConfig 封装,rawConfig 允许修改底层设置,方便开发者自行定义:
raw.definition.ts
import * as v from 'valibot';
import { rawConfig, setComponent } from '@piying/view-angular-core';
export const schema = v.pipe(
v.object({
k1: v.pipe(
v.string(),
rawConfig((field) => {
field.attributes = {
...field.attributes,
placeholder: 'rawConfig 底层配置',
};
return field;
}),
),
}),
setComponent('fieldset'),
);
默认渲染字段组件时会多出一层宿主标签(如 <app-input>)。任意类型的字段组件(普通组件 / 控件 / 表单组件)都可以通过以下三个标记成为 selectorless 组件:组件只投影内容,不产生宿主标签:
- 类上添加静态标记
static __version = 2(库以此识别 V2 组件)
- 类中声明
templateRef = viewChild.required('templateRef')
- 模板中用
<ng-template #templateRef> 包裹整个 UI
import { Component, viewChild } from '@angular/core';
@Component({
selector: 'app-my-component',
standalone: true,
templateUrl: './my-component.component.html',
})
export class MyComponent {
static __version = 2;
templateRef = viewChild.required('templateRef');
}
<!-- my-component.component.html -->
<ng-template #templateRef>
<span>任意内容,外层不会有宿主标签</span>
</ng-template>
⚠️ 注意:selectorless(V2)组件不会自动绑定 actions.attributes / actions.events,需要时从模板上下文取出,配合内置的 AttributesDirective([attributes])与 EventsDirective([events])手动应用:
<ng-template #templateRef let-attr="attributes" let-ev="events">
<div [attributes]="attr()" [events]="ev()">
<!-- 组件内容 -->
</div>
</ng-template>
selectorless.definition.ts
import { actions } from '@piying/view-angular';
import * as v from 'valibot';
import { NFCSchema, setComponent } from '@piying/view-angular-core';
export const schema = v.pipe(
v.object({
__sel: v.pipe(
NFCSchema,
setComponent('selectorless-demo'),
actions.wrappers.set(['label']),
v.title('selectorless'),
),
}),
setComponent('fieldset'),
);