在 Angular 项目中安装并使用 Piying-View 渲染第一个表单。
🚀 想直接看效果? 直接拉模板仓库:https://github.com/piying-org/piying-view-angular-template
git clone https://github.com/piying-org/piying-view-angular-template
cd piying-view-angular-template
npm install && npm start
💡 先分清两种模式:本文演示的是自动模式(<piying-view> + Schema 全自动渲染)。另一种手动模式(convertToField + [formControl] / [fieldTemplate] 手动绑定)见 两种使用模式。
quick-start-demo.component.ts
import { Component, signal } from '@angular/core';
import { JsonPipe } from '@angular/common';
import { actions, PiyingView } from '@piying/view-angular';
import * as v from 'valibot';
import {
FormInputWithValidate,
PresetDefine,
} from '@piying-lib/angular-daisyui/preset';
@Component({
selector: 'quick-start-demo',
standalone: true,
template: `
<piying-view
[schema]="schema"
[(model)]="model"
[options]="options"
></piying-view>
<pre class="mt-4 rounded-box bg-base-200 p-3 text-sm">
Model 值: {{ model() | json }}</pre
>
`,
imports: [PiyingView, JsonPipe],
host: {
class: 'not-content',
},
})
export class QuickStartDemoComponent {
model = signal({ name: '', age: 18, email: '', active: true });
options = {
fieldGlobalConfig: {
...PresetDefine,
types: {
...PresetDefine.types,
...FormInputWithValidate,
},
},
};
schema = v.pipe(
v.object({
name: v.pipe(
v.string(),
actions.attributes.set({ placeholder: '名称(至少 2 个字符)' }),
),
age: v.pipe(
v.number(),
actions.attributes.set({ placeholder: '年龄(至少 18 岁)' }),
),
email: v.pipe(
v.optional(v.string()),
actions.attributes.set({ placeholder: '邮箱(可选)' }),
),
active: v.pipe(v.boolean()),
}),
actions.wrappers.set(['fieldset']),
actions.class.top(
'bg-base-200 border-base-300 rounded-box w-xs border p-4',
),
);
}
pnpm add valibot @piying/view-angular @piying/view-angular-core
字段控件本质上就是一个 Angular 自定义表单控件:注册 NG_VALUE_ACCESSOR 即可与 Piying-View 双向通信。
Piying-View 提供了 BaseControl 基类,帮你省掉手写 ControlValueAccessor 的样板代码。
// src/app/input.component.ts
import { Component, forwardRef } from '@angular/core';
import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
import { BaseControl } from '@piying/view-angular';
@Component({
selector: 'app-input',
standalone: true,
imports: [FormsModule],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => InputComponent),
multi: true,
},
],
template: `
<input
class="input"
[(ngModel)]="value$"
[disabled]="disabled$()"
(blur)="touchedChange()"
/>
`,
})
export class InputComponent extends BaseControl {}
BaseControl 提供:
| 成员 |
说明 |
value$ |
当前值(Signal) |
disabled$ |
禁用状态(Signal) |
valueChange(v) |
主动写值并通知表单 |
touchedChange() |
标记为已触碰 |
不想用 BaseControl?直接自己实现 ControlValueAccessor 并注册 NG_VALUE_ACCESSOR 也完全可以。
fieldConfig 是一张「类型名 → 组件」的注册表,Piying-View 按 Schema 推导出的类型名来这里查找渲染组件。
// src/app/piying-define.ts
import { actions, PiyingViewGroup, type PiViewConfig } from '@piying/view-angular';
import { InputComponent } from './input.component';
export const fieldConfig = {
types: {
// Schema 里的 string / number / boolean 分别用哪个组件渲染
string: {
type: InputComponent,
actions: [actions.wrappers.set(['label'])],
},
number: {
type: InputComponent,
actions: [actions.wrappers.set(['label'])],
},
boolean: {
type: InputComponent,
actions: [actions.wrappers.set(['label'])],
},
// 对象 / 数组等容器类型,用内置的组容器
object: { type: PiyingViewGroup },
array: { type: PiyingViewGroup },
},
wrappers: {
label: { type: LabelWrapperComponent },
},
} as PiViewConfig;
其中 label 包装器负责把字段的 title 渲染成标签:
// src/app/label-wrapper.component.ts
import { Component, inject } from '@angular/core';
import { PI_VIEW_FIELD_TOKEN } from '@piying/view-angular';
@Component({
selector: 'app-label-wrapper',
standalone: true,
template: `
<div class="flex items-center gap-2">
@if (field.props()['title']) {
<span class="label">{{ field.props()['title'] }}</span>
}
<ng-content />
</div>
`,
})
export class LabelWrapperComponent {
field = inject(PI_VIEW_FIELD_TOKEN)!;
}
Wrappers 的完整写法(V1 / V2 模板、InsertFieldDirective)见 Wrappers 包装器。
import * as v from 'valibot';
export const schema = v.object({
name: v.pipe(v.string(), v.minLength(2, '名称至少 2 个字符'), v.title('姓名')),
age: v.pipe(v.number(), v.minValue(18, '必须年满 18 岁'), v.title('年龄')),
email: v.pipe(v.optional(v.string()), v.title('邮箱')),
});
import { Component, signal } from '@angular/core';
import { JsonPipe } from '@angular/common';
import { PiyingView } from '@piying/view-angular';
import { fieldConfig } from './piying-define';
import { schema } from './schema';
@Component({
selector: 'app-example',
standalone: true,
imports: [PiyingView, JsonPipe],
template: `
<piying-view
[schema]="schema"
[(model)]="model"
[options]="options"
></piying-view>
<pre>{{ model() | json }}</pre>
`,
})
export class ExampleComponent {
schema = schema;
model = signal<Record<string, any>>({});
options = {
fieldGlobalConfig: fieldConfig,
};
}
<piying-view> 接收三个属性:
| 属性 |
说明 |
schema |
Valibot Schema,定义字段和验证规则 |
[(model)] |
双向绑定,同步表单数据 |
options |
Options 配置(fieldGlobalConfig 等) |
打开浏览器,你会看到 name / age / email 三个字段的表单。输入数据时,model signal 会同步更新;校验不通过时不会向 model 写出错误值。