创建代码组件简介

已完成

Power Apps 提供了大量开箱即用型功能,供应用开发者自行构建应用,但有时他们需要生成其中未包含的 UI。 例如,将百分比值替换成仪表,显示条码而非 ID,或者将现有控件替换为具有更多功能(例如拖放网格视图)的控件。 此外,您还可以用 Power Apps component framework 继续执行您在 React 或 Angular 等其他 Web 框架中编写的现有组件。

通过创建这些组件,您可以使用整个新式 Web 开发生态系统,其中包括您可能已经熟悉的库、框架和其他工具,还可将这种功能封装到窗体中,使应用制作者可以用您的代码构建应用,就像此代码是平台现成可用的部分。

注意

您可以在我们的文档中找到其他示例组件,包括使用 Angular 或 React 的组件。

自定义 Power Apps 组件通常被称为代码组件,因为它们需要使用自定义代码才能实施。 它们由三个元素组成:清单、实现和资源。 在下列练习中,您将编写您自己的自定义代码组件,类似下图的“hello world”消息。

Hello pcf 自定义代码组件的屏幕截图。

该组件将侦听来自主机应用的更改,并允许用户进行更改,然后将这些更改推送到主机应用。 以下步骤可帮助您构建这个组件。

安装 Power Platform CLI

要让您的计算机做好构建代码组件的准备,请遵循以下步骤:

  1. 安装 Npm(Node.js 随附)或 Node.js(npm 随附)。 我们建议您使用 LTS(长期支持)版本。

  2. 安装 Visual Studio Code

  3. 安装 Power Platform Tools 扩展

创建新的组件项目

要创建新的组件项目,请按照以下步骤操作:

创建一个用于构建组件的目录。 在本示例中,您需将组件放在 C:\source\hello-pcf 中;不过,您也可以另行创建目录。 要另行创建目录,请使用 Visual Studio Code。

  1. 启动 Visual Studio Code。

  2. 选择终端并选择新建终端

    显示 Visual Studio Code 中的“新建终端”选项的屏幕截图。

  3. 将目录更改为您的源文件夹。

    cd source
    
  4. 从您的源目录创建名为 hello-pcf 的目录。

    md hello-pcf
    
  5. 将目录更改为 hello-pcf。

    cd hello-pcf
    

    显示带有创建和更改目录命令的终端屏幕截图。

  6. 通过以下命令使用 Power Platform CLI 初始化您的组件项目:

    pac pcf init --namespace SampleNamespace --name HelloPCF --template field
    

    下图显示了一个您应该会看到的输出示例。

    显示带有 pcf init 命令的终端屏幕截图。

  7. 使用npm install 命令安装项目生成工具。 您可能会在屏幕上看到一些警告,不过您可以忽略。

    npm install
    
  8. 运行以下命令,以便在 Visual Studio Code 中打开项目。

    code -a .
    
  9. 项目内容应如下图所示。

    显示项目文件的屏幕截图。

更新代码组件的清单

更新清单文件,以准确表示您的控件。

  1. 展开 HelloPCF 文件夹并打开 ControlManifest.Input.xml 文件。

    显示 ControlManifest.Input.xml 文件的屏幕截图。

  2. 将 version 更改为 1.0.0,并将 description-key 更改为 Says hello

    显示对控件所做更改的屏幕截图。

  3. 查找 <property 节点。

  4. 将 name 值更改为 Name,将 display-name-key 更改为Name,将 description-key 更改为 A name

    显示对 property 节点所做更改的屏幕截图。

  5. 查找 <resources> 节点。

  6. 附上对您将要创建的名为 hello-pcf.css 的 CSS 文件的引用。

    <css path="css/hello-pcf.css" order="1" />
    

    显示对 resources 节点所做更改的屏幕截图

  7. 选择文件,然后选择保存,以保存所做的更改。

将样式添加到代码组件

要将样式添加到您的代码组件,请按照以下步骤操作:

  1. 请确保您仍选中 ControlManifest.Input.xml 文件,然后选择新建文件夹

    显示添加新文件夹按钮的屏幕截图。

  2. 将新文件夹命名为 css

  3. 选择您创建的 css 文件夹,然后选择新建文件

  4. 将新文件命名为 hello-pcf.css

  5. 打开您新建的 hello-pcf.css 文件,并粘贴以下 CSS 片段。

    .SampleNamespace\.HelloPCF {
          font-size: 1.5em;
        }
    
  6. 现在 CSS 文件的内容应如下图所示。

    显示 CSS 文件内容的屏幕截图。

  7. 选择文件,然后选择保存

生成您的代码组件

在您可以实施组件逻辑之前,您需要在组件上运行内部版本。 这可确保生成正确的 TypeScript 类型,以匹配 ControlManifest.xml 文档中的属性。

使用以下命令返回到终端,并构建您的项目。

npm run build

该组件已编译到 out/controls/HelloPCF 目录中。 生成工件包括:

  • css 文件夹

  • bundle.js - 已捆绑的组件源代码

  • ControlManifest.xml - 上传到 Microsoft Dataverse 组织的实际组件清单文件

    显示 out 文件夹内容的屏幕截图。

实现代码组件的逻辑

要实现代码组件的逻辑,请按照以下步骤操作:

  1. 打开 index.ts 文件。

  2. constructor方法上方,插入以下专用变量:

    // The PCF context object\
    private context: ComponentFramework.Context<IInputs>;
    // The wrapper div element for the component\
    private container: HTMLDivElement;
    // The callback function to call whenever your code has made a change to a bound or output property\
    private notifyOutputChanged: () => void;
    // Flag to track if the component is in edit mode or not\
    private isEditMode: boolean;
    // Tracking variable for the name property\
    private name: string | null;
    

    显示添加到 index.ts 文件的专用变量的屏幕截图。

  3. 查找 init 方法并将其替换为下述方法。

    public init(context: ComponentFramework.Context<IInputs>, notifyOutputChanged: () => void, state: ComponentFramework.Dictionary, container: HTMLDivElement) {
    
    // Track all the things
    
    this.context = context;
    
    this.notifyOutputChanged = notifyOutputChanged;
    
    this.container = container;
    
    this.isEditMode = false;
    
    }
    
  4. 将以下代码片段添加到 init 方法中。 本片段会创建包含“hello”消息的 span。

    // Create the span element to hold the hello message
    
    const message = document.createElement("span");
    
    message.innerText = `Hello ${this.isEditMode ? "" :context.parameters.Name.raw}`;
    
  5. 将以下代码片段添加到 init 方法中。 本代码将创建文本框,以编辑名称。

    // Create the textbox to edit the name
    
    const textbox = document.createElement("input");
    
    textbox.type = "text";
    
    textbox.style.display = this.isEditMode ? "block" : "none";
    
  6. 将以下 if 语句添加到 init 方法中。

    if (context.parameters.Name.raw) {
    }
    
  7. if 语句中添加以下代码片段。 本片段将设置文本框值,并将文本框和消息封装在 div 中。

    textbox.value = context.parameters.Name.raw;
    
    // Wrap the two above elements in a div to box out the content
    
    const messageContainer = document.createElement("div");
    
    messageContainer.appendChild(message);
    
    messageContainer.appendChild(textbox);
    
  8. if 语句中添加以下代码片段。 本片段将创建一个在编辑和读取模式之间切换的按钮。

    // Create the button element to switch between edit and read modes
    
    const button = document.createElement("button");
    
    button.textContent = this.isEditMode ? "Save" : "Edit";
    
    button.addEventListener("click", () => { this.buttonClick(); });
    
  9. if 语句中添加以下代码片段。 本片段会将消息容器和按钮添加到主容器。

    // Add the message container and button to the overall control container
    
    this.container.appendChild(messageContainer);
    
    this.container.appendChild(button);
    
  10. init 方法现在应如下图所示。

    显示 init 方法内容的屏幕截图。

  11. 添加按钮选择处理程序方法。 将以下方法添加到 init 方法中。

    public buttonClick() {
    }
    
  12. buttonClick 方法中添加以下代码片段。 本片段将通过 DOM 查询获取控件。

    // Get our controls via DOM queries
    
    const textbox = this.container.querySelector("input")!;
    
    const message = this.container.querySelector("span")!;
    
    const button = this.container.querySelector("button")!;
    
  13. buttonClick 方法中添加以下代码片段。 本片段会将文本值复制到名称中,如果处于编辑模式,则调用通知方法。

    // If not in edit mode, copy the current name value to the textbox
    
    if (!this.isEditMode) {
    
    textbox.value = this.name ?? "";
    
    } else if (textbox.value != this.name) {
    
    // if in edit mode, copy the textbox value to name and call the notify callback
    
    this.name = textbox.value;
    
    this.notifyOutputChanged();
    }
    
  14. buttonClick 方法中添加以下代码片段。 本片段将翻转模式标志。

    // flip the mode flag
    this.isEditMode = !this.isEditMode;
    
    
  15. buttonClick 方法中添加以下代码片段。 本片段将基于更改设置新输出。

    // Set up the new output based on changes
    
    message.innerText = `Hello ${this.isEditMode ? "" : this.name}`;
    
    textbox.style.display = this.isEditMode ? "inline" : "none";
    
    textbox.value = this.name ?? "";
    
    button.textContent = this.isEditMode ? "Save" : "Edit";
    
    
  16. buttonClick 方法现在应如下图所示。

    显示 buttonClick 方法内容的屏幕截图。

  17. 查找 updateView 方法并将其替换为下述方法。

    public updateView(context: ComponentFramework.Context<IInputs>): void {
    
    // Checks for updates coming in from outside
    
    this.name = context.parameters.Name.raw;
    const message = this.container.querySelector("span")!;
    message.innerText = `Hello ${this.name}`;
    }
    
  18. 查找替换 getOutputs 并将其替换为下述方法。

    public getOutputs(): IOutputs {
    return {
    // If our name variable is null, return undefined instead
    Name: this.name ?? undefined
    };
    }
    
  19. 查找 destroy 方法并将其替换为下述方法。

    public destroy() {
    // Remove the event listener we created in init
    this.container.querySelector("button")!.removeEventListener("click", this.buttonClick);
    }
    
  20. updateView、getOutputs 和 destroy 方法现在应分别如下图所示。

    显示 updateView、getOutputs 和 destroy 方法内容的屏幕截图。

重新构建并运行您的代码组件

要重新构建并运行您的代码组件,请遵循以下步骤:

  1. 至此,您的组件逻辑已实施,接下来请返回到终端,并使用以下命令进行重新构建。

    npm run build
    
  2. 生成应成功。

    显示构建结果的屏幕截图

  3. 运行以下命令,以在 Node 的测试工具中运行您的组件。

    npm start
    

    注意

    您还可以启用观看模式,以确保自动对以下资产进行任何更改,而无需使用 npm start watch 命令重启测试工具。

    • index.ts 文件。

    • ControlManifest.Input.xml 文件。

    • index.ts 中已导入的库。

    • 清单文件中列出的所有资源

  4. 新的浏览器窗口应加载测试工具。 (窗口应自动打开,不过您也可以引用在命令窗口中找到的地址)。

  5. 选择编辑

    显示测试工具中的编辑按钮的屏幕截图

  6. 输入 World,然后选择保存

  7. 您可以更改容器大小。

  8. 测试工具现在应如下图所示。

    显示测试工具中的控件的屏幕截图

  9. 关闭测试工具浏览器窗口。

  10. 返回到终端,并按住 [CONTROL] + C,以阻止观察程序。

  11. 键入 Y,然后键入 [ENTER]。