# # 自定义表单属性对话框

本章节将指导您如何替换默认表单属性对话框，应用内置表单属性编辑组件布局新的属性对话框，并展示如何使用 `PDFFormProperty` 构建新的表单属性编辑组件。

注意：

> 如果您的应用是基于 PDFViewCtrl 实现，而不是基于 UIExtension 实现，则本章中的内容不适用。

## # 替换默认表单属性对话框

在某些情况下，默认表单属性对话框可能无法满足您的需求，例如，您需要添加一些自定义的表单属性或者改变现有的属性配置方式。

要替换默认的表单属性对话框，您可以：

### # 1. 使用自定义的对话框组件替换

这种替换方式适用于对话框组件基于 UIExtension 框架实现的情况，则需要重载 doShown 方法。 要注意：对话框的 name 属性名称必须设定为 `fv--form-designer-widget-properties-dialog` 才会被识别。

#### # 步骤

- 创建自定义对话框：根据您的需求设计并实现一个自定义对话框组件，这里仅以一个简单的对话框为例。

      class CustomDialogComponent extends UIExtension.SeniorComponentFactory.createSuperClass({
          template: `
              <layer @var.dialog="$component">
                  <layer-header
                      @draggable="{type: 'parent'}"
                      title="Widget Properties Dialog"
                  ></layer-header>
                  <div>
                      Field Name: @{dialog.properties.fieldName}
                  </div>
              </layer>
          `
      }) {
          static getName() {
              return 'widget-properties-dialog'
          }
          properties = {
              fieldName: ''
          }
          doShown() {
              this.pdfUI.getAllActivatedElements().then(([widgetComponent]) => {
                  if(widgetComponent.annot?.getType() === 'widget') {
                      this.properties.fieldName = widgetComponent.annot.getField().getName();
                  } else {
                      this.properties.fieldName = 'No Widget Selected';
                  }
                  this.digest();
              })
              super.doShown();
          }
      }

- 注册对话框到自定义模块中：

      const customFormDesignerModule = UIExtension.modular.module('custom-form-designer', []);
      customFormDesignerModule.registerComponent(CustomDialogComponent);

- 通过 UI Fragment 配置替换内置的对话框组件

      new UIExtension.PDFUI({
          appearance: UIExtension.appearances.adaptive.extend({
              getDefaultFragments() {
                  return [{
                      target: 'fv--form-designer-widget-properties-dialog',
                      action: 'replace',
                      template: `<custom-form-designer:widget-properties-dialog name="fv--form-designer-widget-properties-dialog"></custom-form-designer:widget-properties-dialog>`
                  }];
              }
          }),
          // ......其他配置
      })

#### # 完整例子

下面是完整的例子，请自行运行并打开一个带表单的文档查看效果：

    <script>
        const libPath = window.top.location.origin + '/lib';
        const FRAGMENT_ACTION = UIExtension.UIConsts.FRAGMENT_ACTION;

        const customFormDesignerModule = UIExtension.modular.module('custom-form-designer', []);

        customFormDesignerModule.registerComponent(
            class CustomDialogComponent extends UIExtension.SeniorComponentFactory.createSuperClass({
                template: `
                    <layer @var.dialog="$component">
                        <layer-header
                            @draggable="{type: 'parent'}"
                            title="Widget Properties Dialog"
                        ></layer-header>
                        <div>
                            Field Name: @{dialog.properties.fieldName}
                        </div>
                    </layer>
                `
            }) {
                static getName() {
                    return 'widget-properties-dialog'
                }
                properties = {
                    fieldName: ''
                }
                doShown() {
                    this.pdfUI.getAllActivatedElements().then(([widgetComponent]) => {
                        if(widgetComponent.annot?.getType() === 'widget') {
                            this.properties.fieldName = widgetComponent.annot.getField().getName();
                        } else {
                            this.properties.fieldName = 'No Widget Selected';
                        }
                        this.digest();
                    })
                    super.doShown();
                }
            }
        );

        const pdfui = new UIExtension.PDFUI({
            viewerOptions: {
                libPath: libPath,
                jr: {
                    licenseSN: licenseSN,
                    licenseKey: licenseKey
                }
            },
            renderTo: document.body,
            appearance: UIExtension.appearances.adaptive.extend({
                getDefaultFragments() {
                    return [{
                        target: 'fv--form-designer-widget-properties-dialog',
                        action: 'replace',
                        template: `<custom-form-designer:widget-properties-dialog
                                        name="fv--form-designer-widget-properties-dialog"
                                    ></custom-form-designer:widget-properties-dialog>`
                    }];
                }
            }),
            addons: libPath + '/uix-addons/allInOne.js'
        });
    </script>

    {
        "iframeOptions": {
            "style": "height: 500px"
        }
    }

### # 2. 替换表单右键菜单项

如果您的对话框不是通过 UIExtension 框架或者 layer 组件实现，如果属性对话框还是需要通过右键菜单来触发显示的话，那么则需要通过下面的方法来替换右键菜单。

#### # 步骤

- 创建自定义表单属性对话框：根据您的需求设计并实现一个自定义对话框组件，这里仅以一个简单的原生`dialog`实现的对话框为例。

  定义对话框的 HTML 结构

      <dialog id="form-properties-dialog">
          <label>
              <span>field name:</span>
              <input type="text" name="fieldName">
          </label>
          <button type="button" id="close-form-properties-dialog-button">Close</button>
      <dialog>

  定义对话框的 JavaScript 实现类

      class CustomFormPropertiesDialog {
          constructor(dialogId, pdfUI) {
              this.dialogId = dialogId;
              this.pdfUI = pdfUI;
              this.dialogElement = document.getElementById(this.dialogId);
              this.fieldNameElement = this.dialogElement.querySelector('input[name="fieldName"]');
              this.closeDialogBtn = this.dialogElement.querySelector('#close-form-properties-dialog-button');
              this.closeDialogBtn.addEventListener('click', () => {
                  this.dialogElement.close();
              })
          }
          show() {
              this.pdfUI.getAllActivatedElements().then(([widgetComponent]) => {
                  let fieldName;
                  if(widgetComponent.annot?.getType() === 'widget') {
                      fieldName = widgetComponent.annot.getField().getName();
                  } else {
                      fieldName = 'No Widget Selected';
                  }
                  this.fieldNameElement.value = fieldName;
              })
              this.dialogElement.showModal();
          }
      }

- 创建一个Controller用于处理右键菜单项点击事件：

      const customFormDesignerModule = UIExtension.modular.module('custom-form-designer', []);
      customFormDesignerModule.registerController(
          class ShowCustomFormPropertiesDialogController extends UIExtension.Controller {
              static getName() {
                  return 'ShowCustomFormPropertiesDialogController'
              }
              mounted() {
                  super.mounted();
                  this.dialog = new CustomFormPropertiesDialog('form-properties-dialog', this.pdfUI);
              }
              handle() {
                  this.dialog.show();
              }
          }
      )

- 替换右键菜单项：

      new UIExtension.PDFUI({
          appearance: UIExtension.appearances.adaptive.extend({
              getDefaultFragments() {
                  return [{
                      target: 'fv--ui-show-widget-properties-contextmenu-item',
                      action: 'replace',
                      template: `<contextmenu-item @controller="custom-form-designer:ShowCustomFormPropertiesDialogController">Show properties</contextmenu-item>`
                  }];
              }
          }),
          // ......其他配置
      })

#### # 完整示例

下面是完整的例子，请自行运行并打开一个带表单的文档查看效果：

    <html>
        <dialog id="form-properties-dialog">
            <label>
                <span>field name:</span>
                <input type="text" name="fieldName">
            </label>
            <button type="button" id="close-form-properties-dialog-button">Close</button>
        <dialog>
    </html>
    <script>
        class CustomFormPropertiesDialog {
            constructor(dialogId, pdfUI) {
                this.dialogId = dialogId;
                this.pdfUI = pdfUI;
                this.dialogElement = document.getElementById(this.dialogId);
                this.fieldNameElement = this.dialogElement.querySelector('input[name="fieldName"]');
                this.closeDialogBtn = this.dialogElement.querySelector('#close-form-properties-dialog-button');
                this.closeDialogBtn.addEventListener('click', () => {
                    this.dialogElement.close();
                })
            }
            show() {
                this.pdfUI.getAllActivatedElements().then(([widgetComponent]) => {
                    let fieldName;
                    if(widgetComponent.annot?.getType() === 'widget') {
                        fieldName = widgetComponent.annot.getField().getName();
                    } else {
                        fieldName = 'No Widget Selected';
                    }
                    this.fieldNameElement.value = fieldName;
                })
                this.dialogElement.showModal();
            }
        }
        const libPath = window.top.location.origin + '/lib';
        const FRAGMENT_ACTION = UIExtension.UIConsts.FRAGMENT_ACTION;

        const customFormDesignerModule = UIExtension.modular.module('custom-form-designer', []);
        customFormDesignerModule.registerController(
            class ShowCustomFormPropertiesDialogController extends UIExtension.Controller {
                static getName() {
                    return 'ShowCustomFormPropertiesDialogController'
                }
                mounted() {
                    super.mounted();
                    this.dialog = new CustomFormPropertiesDialog('form-properties-dialog', this.pdfUI);
                }
                handle() {
                    this.dialog.show();
                }
            }
        )

        const pdfui = new UIExtension.PDFUI({
            viewerOptions: {
                libPath: libPath,
                jr: {
                    licenseSN: licenseSN,
                    licenseKey: licenseKey
                }
            },
            renderTo: document.body,
            appearance: UIExtension.appearances.adaptive.extend({
                getDefaultFragments() {
                    return [{
                        target: 'fv--ui-show-widget-properties-contextmenu-item',
                        action: 'replace',
                        template: `<contextmenu-item
                                        @controller="custom-form-designer:ShowCustomFormPropertiesDialogController"
                                   >Show properties</contextmenu-item>`
                    }];
                }
            }),
            addons: libPath + '/uix-addons/allInOne.js'
        });
    </script>

    {
        "iframeOptions": {
            "style": "height: 500px"
        }
    }

## # 自定义表单属性对话框布局

我们在表单属性对话框上针对不同的属性类型提供了粒度较细的属性编辑组件（参考内置表单属性编辑组件）一章，有了这些细粒度的组件，您可以根据您的需求，复用我们的属性编辑组件，自定义表单属性对话框的布局。