Introducción a la creación de un componente de código

Completado

Power Apps proporciona numerosas funcionalidades integradas para que los creadores de aplicaciones creen sus aplicaciones, pero a veces, necesitará crear experiencias de usuario personalizadas para satisfacer sus requisitos únicos. Por ejemplo, reemplazar un valor porcentual con un medidor, proporcionar un código de barras en lugar de un identificador o reemplazar los controles existentes por controles que tengan más funcionalidades, como una vista de cuadrícula de arrastrar y colocar. También puede ajustar Power Apps Component Framework según los componentes que ya haya creado en otros marcos web, como React o Angular.

La creación de estos componentes le permite usar todas las características del ecosistema de desarrollo web moderno, como bibliotecas, marcos de trabajo y otras herramientas que es posible que ya conozca; además, empaqueta esa capacidad en un formulario que permite a los creadores de aplicaciones crear aplicaciones con su código, como si fuera una parte de la plataforma lista para usar.

Los componentes personalizados de Power Apps se denominan con frecuencia componentes de código, ya que requieren un código personalizado para crearlos. Constan de tres elementos: un manifiesto, una implementación y recursos.

En el siguiente ejercicio, creará un componente de código personalizado para manejar un escenario para su empresa. A su empresa le gustaría tener algunos campos en el formulario de la aplicación que sean de solo lectura hasta que el usuario inicie manualmente una edición en el valor de los datos. El equipo ha identificado que ninguno de los controles integrados funcionará, por lo que le han pedido que cree un componente de código personalizado.

Para resolver este requisito, creará un componente personalizado de campo editable que se verá como la siguiente imagen. El valor será de solo lectura hasta que el usuario seleccione la edición.

Captura de pantalla del componente de código personalizado.

Este componente escuchará los cambios provenientes de la aplicación host y permitirá al usuario realizar cambios que luego se envían a la aplicación host. Los siguientes pasos le ayudarán a crear este componente.

Instalar Power Platform CLI

Para que su equipo esté preparado para compilar componentes de código, siga estos pasos:

  1. Instale Node.js (viene con npm). Le recomendamos que utilice una versión LTS (soporte a largo plazo) como la que se encuentra aquí. Es recomendable comprobar que no tenga Node/NPM ya instalado. Para ello, vaya a un símbolo del sistema y escriba lo siguiente:

    // Launch a standard command prompt and type both of the following
    
    npm --version
    Node --version
    

    Si recibe un error al ejecutar estos comandos, deberá instalar Node.js utilizando los vínculos anteriores.

    Una instalación correcta del nodo devolverá los números de versión en la ventana de comandos al introducir los comandos anteriores, como se ve a continuación:

    // If installed properly you will see something like the output below. The version numbers may be slightly different based on the version installed.
    
    C:\npm --version
    10.5.0
    
    C:\Node --version
    v20.12.2
    
  2. Instale Visual Studio Code.

  3. Instale la extensión de Power Platform Tools. Asegúrese de haber completado la instalación de Visual Studio Code antes de instalar Power Platform Tools.

    Power Platform Tools no le permite ejecutar comandos CLI para Power Platform en un símbolo del sistema fuera de Visual Studio Code. Es recomendable que instale también el MSI de la CLI para Power Platform.

    Para instalar la CLI en el nivel de Windows, siga estas instrucciones. Puede tener la CLI y la extensión de Power Platform Tools instaladas simultáneamente.

Crear un nuevo proyecto de componente

Antes de comenzar la compilación de componentes, compruebe que los componentes instalados enumerados anteriormente estén funcionando de manera correcta. Los comandos NPM y CLI funcionarán desde una ventana de terminal en Visual Studio Code. Si tiene problemas para ejecutarlos correctamente desde VS Code, puede ejecutar los comandos de Terminal que se indican en los pasos siguientes en un símbolo del sistema si ha instalado la CLI para Power Platform.

En Visual Studio Code, vaya hasta Terminal > Nueva Terminal (o presione CTRL+MAYÚS+`). En la ventana Terminal en VS Code, repita las declaraciones de versión de NPM y Node. Si todo devuelve los números de versión correctos, puede continuar con la creación de la solución.

// Test installs from Steps 1-3 listed above

// This will verify NPM installation
C:\npm --version
10.5.0

// This will verify Node installation
C:\Node --version
v20.12.2

// This will launch Visual Studio Code from the command line.
C:\Code

Ahora que ya está listo para crear un nuevo proyecto de componente, siga estos pasos para comenzar:

Cree un directorio en el que creará el componente. En este ejemplo colocará el componente en C:\source\Editable-pcf. Para crear su propio directorio, deberá usar Visual Studio Code. Si tiene problemas con el uso de Terminal de VS Code, también puede crear las carpetas usando el símbolo del sistema.

  1. Inicie Visual Studio Code.

  2. Seleccione Terminal y, luego, Nuevo terminal.

    Captura de pantalla que muestra la nueva selección de terminal en Visual Studio Code

  3. Su sesión de Terminal se establecerá de forma predeterminada en la última carpeta que utilizó. Esto se mostrará en el área del símbolo del sistema TERMINAL, como se puede ver a continuación:

    //Note Your PS will not list exactly what is seen below, but will be specific to your starting path.
    
    PS C:\Users\Name\Folder
    

    Cambie el directorio a una ubicación donde desee crear esta solución. Puede utilizar el comando CD para navegar a una ubicación adecuada.

    Nota

    Tenga en cuenta que la carpeta donde ejecuta NPM u otros comandos es importante. Debe estar siempre en su carpeta de Proyectos antes de ejecutar los comandos de compilación. De lo contrario, podría dañar la compilación y dificultar la obtención de resultados óptimos.

    Para crear una nueva carpeta en la ubicación predeterminada, use md (directorio crear), como se muestra a continuación, en la ventana de su Terminal de VS Code.

    md source
    cd source
    

    Esto creará un directorio llamado origen, y lo llevará a ese directorio usando el comando cd (directorio cambiar).

  4. Desde el directorio de origen creado, cree un directorio llamado editable-pcf. Este será su directorio PROYECTO, donde se almacenarán todos los archivos de su proyecto. También cambiaremos el directorio a nuestro nuevo directorio Proyecto.

    md editable-pcf
    cd editable-pcf
    

    Captura de pantalla con el terminal con comandos para crear y cambiar el directorio

  5. Inicialice el proyecto de componente mediante Power Platform CLI con el siguiente comando:

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

    En la siguiente imagen se ilustra un ejemplo de resultado que debería aparecer.

    Precaución

    Si el comando PAC PCF INIT no se ejecuta en una ventana de terminal en VS Code y ha instalado la Power Platform CLI, puede ejecutar un símbolo del sistema y un CD en su directorio editable-pcf. Cuando esté allí, puede introducir el comando en el símbolo del sistema: funcionará correctamente. Debería ver el mismo resultado que el mencionado anteriormente.

  6. Instale las herramientas de compilación del proyecto mediante el comando npm install. Es posible que aparezca alguna advertencia, pero puede ignorarla. Asegúrese de estar en el directorio de su PROYECTO antes de emitir este comando.

    npm install
    

    Precaución

    Si el comando npm install no se ejecuta en una ventana de terminal en VS Code y ha instalado la Power Platform CLI, puede ejecutar un símbolo del sistema y un CD en su directorio editable-pcf. Cuando esté allí, puede introducir el comando en el símbolo del sistema: funcionará correctamente.

    Para comprobar si todo funciona, ejecute un comando DIR en la ventana de Terminal en VS Code o en el símbolo del sistema (si optó por compilar fuera de Visual Studio Code). Debería ver una serie de archivos y carpetas en su directorio editable-pcf. Este es el proyecto que creó en los pasos anteriores y que compilaremos con VS Code.

  7. Ejecute el siguiente comando para abrir el proyecto en Visual Studio Code o en la ventana del símbolo del sistema (si está utilizando un símbolo del sistema). Esto debería iniciar su proyecto creado en VS Code.

    code -a .
    
  8. El contenido del proyecto debería ser similar al de la imagen siguiente.

    Captura de pantalla que muestra los archivos del proyecto

Actualizar el manifiesto del componente de código

Actualice el archivo de manifiesto para representar el control con precisión.

  1. Expanda la carpeta EditablePCF y abra el archivo ControlManifest.Input.xml.

    Captura de pantalla que muestra el archivo ControlManifest.Input.xml.

  2. Cambie la versión a 1.0.0 y description-key a Edita el nombre del proyecto.

  3. Localice el nodo property.

  4. Cambie el valor del nombre a Nombre, display-name-key a Nombre y description-key a Un nombre.

  5. Localice el nodo resources.

  6. Incluya una referencia a un archivo CSS denominado editable-pcf.css que creará en los siguientes pasos.

    <css path="css/EditablePCF.css" order="1" />
    

    Captura de pantalla que muestra los cambios realizados en el nodo resources

  7. Guarde sus cambios seleccionando Archivo y, luego, Guardar, o pulse CTRL+S para guardar el archivo.

Agregar estilo al componente de código

Para agregar estilo al componente de código, siga estos pasos:

  1. Asegúrese de que el archivo ControlManifest.Input.xml sigue seleccionado y, a continuación, seleccione Nueva carpeta.

    Captura de pantalla que muestra el botón Agregar nueva carpeta

  2. Llame a la nueva carpeta css.

  3. Seleccione la carpeta css que creó y, después, Archivo nuevo.

  4. Póngale al nuevo archivo el nombre EditablePCF.css (o el nombre que le haya puesto al archivo css del paso 6 anterior).

  5. Abra el archivo EditablePCF.css que ha creado y pegue el siguiente fragmento de CSS. Este es el nombre de referencia del recurso que utilizó anteriormente cuando agregó el código de ruta CSS al archivo de manifiesto.

    .SampleNamespace\.HelloPCF {
          font-size: 1.5em;
        }
    
  6. El contenido del archivo CSS debería ser similar al contenido de la siguiente imagen.

    Captura de pantalla que muestra el contenido del archivo CSS

  7. Seleccione Archivo y, luego, Guardar o pulse CTRL+S para guardar el archivo.

Crear el componente de código

Para poder implementar la lógica de su componente, debe ejecutar una compilación en su componente. Esto garantiza la generación de tipos de TypeScript correctos que se correspondan con las propiedades del documento ControlManifest.xml.

Vuelva al terminal en VS Code y cree el proyecto mediante el siguiente comando. Si, por alguna razón, tiene problemas para usar Terminal en Visual Studio Code, puede ir a su carpeta usando el símbolo del sistema y ejecutar el comando desde allí.

Precaución

Asegúrese de estar en su carpeta PROYECTO antes de emitir este comando.

npm run build

El componente se compila en el directorio out/controls/EditablePCF. Entre los artefactos de compilación se incluyen:

  • Carpeta css

  • bundle.js: código de origen del componente compilado

  • ControlManifest.xml: archivo de manifiesto del componente real que se carga en la organización de Microsoft Dataverse.

    Captura de pantalla que muestra el contenido de la carpeta

Advertencia

El error más común que suele recibirse aquí es un error tipográfico en el nombre del archivo CSS que ha creado anteriormente. Si eso ocurre, solo tiene que cambiar el nombre de los archivos como corresponda y ejecutar de nuevo el comando npm run build hasta que se ejecute por completo y sin errores. Revise la sección RECURSO en el archivo COntrolManifest.Input.xml comparándola con el archivo creado en la carpeta CSS. Deben coincidir al 100 %.

Implementar la lógica del componente del código

Para implementar la lógica de su componente de código, siga estos pasos tras finalizar la compilación en los pasos anteriores. En Visual Studio Code, en el EXPLORADOR, busque un archivo llamado index.ts. Ahí es donde comenzaremos a escribir el código de nuestro componente.

  1. Abra el archivo index.ts en Visual Studio Code.

  2. Encima del método constructor, inserte las siguientes variables privadas:

    // 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;
    
  3. Localice el método public init y reemplácelo con el siguiente método.

    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;
    
        // Create the span element to hold the project name
        const message = document.createElement("span");
        message.innerText = `Project name ${this.isEditMode ? "" :context.parameters.Name.raw}`;
    
        // Create the textbox to edit the name
        const text = document.createElement("input");
        text.type = "text";
        text.style.display = this.isEditMode ? "block" : "none";
    
        if (context.parameters.Name.raw) {
            text.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(text);
    
            // 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(); });
    
            // Add the message container and button to the overall control container
            this.container.appendChild(messageContainer);
            this.container.appendChild(button);
        }
    
    }
    

    Advertencia

    Quizá vea que el EventListener para buttonClick está subrayado en rojo. No se preocupe, crearemos el método para ese evento a continuación. Si ve otras secciones en rojo, es recomendable que compruebe que todo se haya copiado o introducido correctamente.

  4. Agregue método de controlador de selección de botón. Agregue el siguiente método debajo del método init.

    public buttonClick() {
        // Get our controls via DOM queries
        const text = this.container.querySelector("input")!;
        const message = this.container.querySelector("span")!;
        const button = this.container.querySelector("button")!;
    
        // If not in edit mode, copy the current name value to the textbox
        if (!this.isEditMode) {
            text.value = this.name ?? "";
        } 
        else if (text.value != this.name) {
            // if in edit mode, copy the textbox value to name and call the notify callback
            this.name = text.value;
            this.notifyOutputChanged();
        }
    
        // flip the mode flag
        this.isEditMode = !this.isEditMode;
    
        // Set up the new output based on changes
        message.innerText = `Project name ${this.isEditMode ? "" : this.name}`;
        text.style.display = this.isEditMode ? "inline" : "none";
        text.value = this.name ?? "";
        button.textContent = this.isEditMode ? "Save" : "Edit";
    }
    
  5. Localice el método updateView y reemplácelo con el siguiente método.

    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 = `Project name ${this.name}`;
    }
    
  6. Localice el método getOutputs y reemplácelo con el siguiente método.

    public getOutputs(): IOutputs {
    return {
    // If our name variable is null, return undefined instead
    Name: this.name ?? undefined
    };
    }
    
  7. Localice el método destroy y reemplácelo con el siguiente método.

    public destroy() {
    // Remove the event listener we created in init
    this.container.querySelector("button")!.removeEventListener("click", this.buttonClick);
    }
    
  8. El Index.ts final debería tener un aspecto similar al del siguiente código:

    import { IInputs, IOutputs } from "./generated/ManifestTypes";
    
    export class EditablePCF implements ComponentFramework.StandardControl<IInputs, IOutputs> {
    
        /**
        * Empty 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;
    
        constructor()
        {
    
        }
    
        /**
        * Used to initialize the control instance. Controls can kick off remote server calls and other initialization actions here.
        * Data-set values are not initialized here, use updateView.
        * @param context The entire property bag available to control via Context Object; It contains values as set up by the customizer mapped to property names defined in the manifest, as well as utility functions.
        * @param notifyOutputChanged A callback method to alert the framework that the control has new outputs ready to be retrieved asynchronously.
        * @param state A piece of data that persists in one session for a single user. Can be set at any point in a controls life cycle by calling 'setControlState' in the Mode interface.
        * @param container If a control is marked control-type='standard', it will receive an empty div element within which it can render its content.
        */
        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;
    
            // Create the span element to hold the project name
    
            const message = document.createElement("span");
            message.innerText = `Project name ${this.isEditMode ? "" :context.parameters.Name.raw}`;
    
            // Create the textbox to edit the name
            const text = document.createElement("input");
            text.type = "text";
            text.style.display = this.isEditMode ? "block" : "none";
    
            if (context.parameters.Name.raw) {
                text.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(text);
    
                // 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(); });
    
                // Add the message container and button to the overall control container
                this.container.appendChild(messageContainer);
                this.container.appendChild(button);
            }
    
        }
    
        public buttonClick() {
            // Get our controls via DOM queries
    
            const text = this.container.querySelector("input")!;
            const message = this.container.querySelector("span")!;
            const button = this.container.querySelector("button")!;
    
            // If not in edit mode, copy the current name value to the textbox
    
            if (!this.isEditMode) {
                text.value = this.name ?? "";
            } 
            else if (text.value != this.name) {
    
                // if in edit mode, copy the textbox value to name and call the notify callback
                this.name = text.value;
                this.notifyOutputChanged();
            }
    
            // flip the mode flag
            this.isEditMode = !this.isEditMode;
    
            // Set up the new output based on changes
            message.innerText = `Project name ${this.isEditMode ? "" : this.name}`;
            text.style.display = this.isEditMode ? "inline" : "none";
            text.value = this.name ?? "";
            button.textContent = this.isEditMode ? "Save" : "Edit";
        }
    
        /**
        * Called when any value in the property bag has changed. This includes field values, data-sets, global values such as container height and width, offline status, control metadata values such as label, visible, etc.
        * @param context The entire property bag available to control via Context Object; It contains values as set up by the customizer mapped to names defined in the manifest, as well as utility functions
        */
        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 = `Project name ${this.name}`;
        }
    
        /**
        * It is called by the framework prior to a control receiving new data.
        * @returns an object based on nomenclature defined in manifest, expecting object[s] for property marked as "bound" or "output"
        */
        public getOutputs(): IOutputs {
            return {
            // If our name variable is null, return undefined instead
            Name: this.name ?? undefined
            };
        }
    
        /**
        * Called when the control is to be removed from the DOM tree. Controls should use this call for cleanup.
        * i.e. cancelling any pending remote calls, removing listeners, etc.
        */
        public destroy() {
            // Remove the event listener we created in init
            this.container.querySelector("button")!.removeEventListener("click", this.buttonClick);
        }
    }
    

Volver a crear y ejecutar el componente de código

Para volver a crear y ejecutar el componente de código, siga estos pasos:

  1. Ahora que ha implementado la lógica del componente, regrese al terminal y vuelva a crearlo mediante el siguiente comando. Puede ejecutar esto directamente en VS Code o mediante el símbolo del sistema, siempre que vaya primero a su carpeta editable-pcf.

    npm run build
    
  2. La compilación debería realizarse correctamente.

    Captura de pantalla que muestra el resultado de la creación

  3. Ejecute el siguiente comando para ejecutar el componente en la herramienta de ejecución de pruebas de Node. Si es la primera vez que hace esto, debería iniciarse un navegador donde aparezca el componente recién creado.

    npm start
    

    Nota

    También puede habilitar el modo de observación para garantizar que cualquier cambio en los siguientes activos se realice automáticamente sin tener que reiniciar la herramienta de ejecución de pruebas mediante el comando npm start watch.

    • Archivo index.ts.

    • Archivo ControlManifest.Input.xml

    • Bibliotecas importadas en index.ts

    • Todos los recursos enumerados en el archivo de manifiesto

  4. Una nueva ventana del navegador debería cargar la herramienta de ejecución de pruebas. (La ventana debería abrirse automáticamente, pero también puede hacer referencia a la dirección según aparece en la ventana de comandos).

  5. Seleccione Editar.

    Captura de pantalla que muestra el botón de edición en la herramienta de ejecución de pruebas

  6. Escriba Proyecto Uno y seleccione Guardar.

  7. Puede cambiar el tamaño del contenedor.

  8. La herramienta de ejecución de pruebas debería tener un aspecto similar al de la imagen siguiente.

    Captura de pantalla que muestra el control dentro de la herramienta de ejecución de pruebas.

  9. Cierre la ventana del explorador de la herramienta de ejecución de pruebas.

  10. Vuelva al terminal o al símbolo del sistema (si no está usando VS Code Terminal) y detenga el monitor manteniendo presionado [CONTROL] + C.

  11. Escriba Y y, luego, presione [INTRO].