Compartir a través de


Compilación de la primera extensión de Form Customizer

Los personalizadores de formularios son SharePoint Framework componentes que le ofrecen la opción de invalidar la experiencia de formulario en un nivel de lista o biblioteca mediante la asociación del componente al tipo de contenido usado. Los componentes del personalizador de formularios se pueden usar en SharePoint Online y se crean mediante herramientas y bibliotecas modernas de JavaScript.

Importante

El personalizador de formularios se publicó como parte de la SharePoint Framework 1.15, por lo que debe asegurarse de que usa la versión correcta en su entorno. Consulte las notas de la versión v1.15 para obtener más información.

Sugerencia

Puede encontrar la salida de este tutorial desde GitHub.

Crear un proyecto de extensión

  1. Cree un nuevo directorio del proyecto en su ubicación favorita.

    md form-customizer
    
  2. Vaya al directorio del proyecto.

    cd form-customizer
    
  3. Para crear una extensión HelloWorld, ejecute el generador de SharePoint de Yeoman.

    yo @microsoft/sharepoint
    
  4. En el momento en que se le solicite, introduzca los siguientes valores (seleccione la opción predeterminada para todas las solicitudes que se omitan a continuación):

    • ¿Cuál es el nombre de la solución?: form-customizer
    • ¿Cuál es el tipo de componente del lado cliente que se va a crear?: Extensión
    • ¿Qué tipo de extensión del lado cliente se va a crear? Personalizador de formularios
    • ¿Cuál es el nombre del personalizador de formularios? HolaMundo
    • ¿Qué plantilla le gustaría usar?: Sin JavaScript Framework

    En este momento, Yeoman instalará las dependencias necesarias y aplicará scaffolding a los archivos de la solución con la extensión HelloWorld. Esta operación puede tardar unos minutos.

  5. Luego, escriba lo siguiente en la consola para iniciar Visual Studio Code.

    code .
    

    Nota:

    Como la solución del lado cliente de SharePoint se basa en HTML/TypeScript, puede usar cualquier editor de código que admita el desarrollo del lado cliente para compilar la extensión.

  6. Abra el archivo ./src/extensions/helloWorld/HelloWorldFormCustomizer.manifest.json .

    Este archivo define el tipo de extensión y un identificador id único para la extensión que se puede usar para establecerse en el nivel de tipo de contenido para habilitar una representación personalizada con este componente.

Codificar el personalizador de formularios

Abra el archivo ./src/extensions/helloWorld/HelloWorldFormCustomizer.ts .

Observe que la clase base del personalizador de formularios se importa desde el paquete sp-listview-extensibility, que contiene SharePoint Framework código requerido por el personalizador de formularios.

import { Log } from '@microsoft/sp-core-library';
import {
  BaseFormCustomizer
} from '@microsoft/sp-listview-extensibility';

La lógica del personalizador de formularios está contenida en los onInit()métodos , render()y onDispose() .

  • onInit() es donde ejecutará la configuración necesaria para la extensión. Este evento se produce después this.context de y this.properties se asignan, pero antes de que la página DOM esté lista. Al igual que con los elementos web, onInit() devuelve una promesa que puede usar para realizar operaciones asincrónicas; render() no se llama a hasta que la promesa se haya resuelto. Si no lo necesita, simplemente devuelva Promise.resolve<void>();.
  • render() se produce cuando se representa el componente. Proporciona un event.domElement elemento HTML donde el código puede escribir su contenido.
  • onDispose() se produce inmediatamente antes de que se elimine el elemento host del formulario. Se puede usar para liberar todos los recursos asignados durante la representación del formulario. Por ejemplo, si render() se monta un elemento React, onDispose() se debe usar para liberarlo; de lo contrario, se produciría una fuga de recursos.

A continuación se muestra el contenido de render() y onDispose() en la solución predeterminada:

  public render(): void {
    // Use this method to perform your custom rendering.
    this.domElement.innerHTML = `<div class="${ styles.helloWorld }"></div>`;
  }

  public onDispose(): void {
    // This method should be used to free any resources that were allocated during rendering.
    super.onDispose();
  }

Como de forma predeterminada, el componente customzier del formulario no representa ninguna información, vamos a actualizar el método de representación como se indica a continuación.

  public render(): void {
    // Use this method to perform your custom rendering.
    this.domElement.innerHTML = `<div class="${ styles.helloWorld }"><h1>Super cool custom form component</h1></div>`;
  }

Depuración del personalizador de formularios

Puede probar y depurar el personalizador de formularios en un sitio de SharePoint Online en directo. No es necesario implementar las personalizaciones en el catálogo de aplicaciones de inquilino para hacerlo, lo que hace que la experiencia de depuración sea sencilla y eficaz.

  1. Para probar la extensión, primero debe crear una lista en la que probar el personalizador. Por lo tanto, vaya al sitio del inquilino de SharePoint Online donde desea probar el personalizador de formularios.

  2. En la barra de herramientas, seleccione Nuevo y luego Lista.

    Crear una lista

  3. Elija Lista en blanco en la nueva experiencia de creación de listas

    Selección de la lista en blanco

  4. Cree una nueva lista denominada Empresa y, a continuación, seleccione Crear.

    Crear una lista con el nombre Pedidos

  5. En Visual Studio Code, abra el archivo ./config/serve.json.

    Actualice los pageUrl atributos para que coincidan con una dirección URL de la lista que hemos creado en los pasos de vista previa. Después de los cambios, el archivo serve.json debe tener un aspecto similar al código siguiente:

    {
      "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/spfx-serve.schema.json",
      "port": 4321,
      "https": true,
      "serveConfigurations": {
        "default": {
          "pageUrl": "https://yourtenant.sharepoint.com/sites/demo/_layouts/15/SPListForm.aspx",
          "formCustomizer": {
            "componentId": "fb3e7dee-a6fa-4e80-add1-e06081167bb5",
            "PageType": 8,
            "RootFolder": "/sites/demo/Lists/business",
            "properties": {
              "sampleText": "Value"
            }
          }
    

    Vamos a llamar a algunos temas específicos del archivo serve.json.

    • Puede ver varias configuraciones diferentes que se pueden usar para depurar nuevos formularios, editarlos y verlos con diferencias específicas de parámetros de consulta. Puede definir la configuración usada en el comando gulp serve, por ejemplo, como gulp serve --config=helloWorld_EditForm
    • componentId se asocia automáticamente para ser el primer componente de formato de lista de la solución (si tiene varios componentes)
    • Para simplificar la depuración, no es necesario definir el identificador de tipo de contenido de destino al que está asociado el componente, pero en tiempo de ejecución la asociación se realiza en el nivel de tipo de contenido actualizando al menos una de las siguientes propiedades en el tipo de contenido:
      • Contenttype. NewFormClientSideComponentId : id. de componente para el nuevo formulario
      • Contenttype. NewFormClientSideComponentProperties : detalles de configuración opcionales
      • Contenttype. DispFormClientSideComponentId : id. de componente para el formulario de edición
      • Contenttype. DispFormClientSideComponentProperties : detalles de configuración opcionales
      • Contenttype. EditFormClientSideComponentId : formulario de presentación del identificador de componente
      • Contenttype. EditFormClientSideComponentProperties : detalles de configuración opcionales
  6. Compile el código y hospede los archivos compilados en la máquina local ejecutando este comando:

    gulp serve
    

    Después de compilar el código sin errores, el manifiesto resultante se entrega desde https://localhost:4321.

    gulp serve

    Esto iniciará el explorador predeterminado y cargará la página definida en el archivo serve.json .

  7. Para aceptar la carga de los manifiestos de depuración, seleccione Cargar scripts de depuración cuando se le pida.

    Aceptar la carga de scripts de depuración

    Observe cómo se representa el componente personalizado en la página en función del contenido personalizado que hemos actualizado al método render.

    Vista de lista con desde personalizador representado con outpu predeterminado

Adición de funcionalidades de edición de elementos de formulario al ejemplo

Ahora que hemos creado el componente de línea base y hemos probado que funciona correctamente. Vamos a crear una lógica de representación independiente para mostrar, editar y nuevos formularios, y para permitir guardar nuevos elementos en la lista.

  1. Abra el archivo ./src/extensions/helloWorld/loc/myStrings.d.ts y agregue un nuevo título a la interfaz IHelloWorldFormCustomizerStrings . La interfaz debe ser la siguiente después de las modificaciones.

    declare interface IHelloWorldFormCustomizerStrings {
      Save: string;
      Cancel: string;
      Close: string;
      Title: string;
    }
    
  2. Abra el archivo ./src/extensions/helloWorld/loc/en-us.js y agregue una nueva cadena title al archivo. El contenido del archivo debe ser el siguiente después de las modificaciones.

    define([], function() {
      return {
        "Save": "Save",
        "Cancel": "Cancel",
        "Close": "Close",
        "Title": "Title"
      }
    });
    
  3. Abra el archivo ./src/extensions/helloWorld/HelloWorldFormCustomizer.module.scss y actualice la definición de estilo de la siguiente manera. Vamos a agregar estilo de error para el componente.

    .helloWorld {
      background-color: "[theme:white, default:#ffffff]";
      color: "[theme:themePrimary, default:#0078d4]";
      padding: 0.5rem;
    
      .error {
        color: red;
      }
    }
    
  4. Vaya a la parte superior del archivo HelloWorldFormCustomizer.ts .

  5. Busque la línea import styles from './HelloWorldFormCustomizer.module.scss'; y agregue las siguientes líneas inmediatamente después de ella:

    import { FormDisplayMode } from '@microsoft/sp-core-library';
    import {
      SPHttpClient,
      SPHttpClientResponse
    } from '@microsoft/sp-http';
    
  6. Incluya _item y _etag tipos privados dentro de la clase HelloWorldFormCustomizer , como se muestra en este fragmento de código. Observe que la definición de clase ya existe en el código.

    // This already exists in YOUR code
    export default class HelloWorldFormCustomizer
      extends BaseFormCustomizer<IHelloWorldFormCustomizerProperties> {
    
    // Added for the item to show in the form; use with edit and view form
    private _item: {
      Title?: string;
    };
    // Added for item's etag to ensure integrity of the update; used with edit form
    private _etag?: string;
    
  7. Actualice el método onInit() de la siguiente manera. Este código usa this.displayMode para determinar el estado de la representación y, a continuación, captura el elemento de lista seleccionado si es necesario.

    public onInit(): Promise<void> {
      if (this.displayMode === FormDisplayMode.New) {
        // we're creating a new item so nothing to load
        return Promise.resolve();
      }
    
      // load item to display on the form
      return this.context.spHttpClient
        .get(this.context.pageContext.web.absoluteUrl + `/_api/web/lists/getbytitle('${this.context.list.title}')/items(${this.context.itemId})`, SPHttpClient.configurations.v1, {
          headers: {
            accept: 'application/json;odata.metadata=none'
          }
        })
        .then(res => {
          if (res.ok) {
            // store etag in case we'll need to update the item
            this._etag = res.headers.get('ETag');
            return res.json();
          }
          else {
            return Promise.reject(res.statusText);
          }
        })
        .then(item => {
          this._item = item;
          return Promise.resolve();
        });
    }
    
  8. Actualice el método render() de la siguiente manera. Represente el formulario solo en pantalla o en el modo de edición, en función del modo de presentación del formulario. En este caso, usamos el mismo renderig para la experiencia nueva y de edición, pero podría tener fácilmente una opción dedicada si es necesario.

      public render(): void {
        // render view form
        if (this.displayMode === FormDisplayMode.Display) {
    
          this.domElement.innerHTML =
                        `<div class="${styles.basics}">
                          <label for="title">${strings.Title}</label>
                          <br />
                            ${this._item?.Title}
                          <br />
                          <br />
                          <input type="button" id="cancel" value="${strings.Close}" />
                        </div>`;
    
          document.getElementById('cancel').addEventListener('click', this._onClose.bind(this));
        }
        // render new/edit form
        else {
          this.domElement.innerHTML =
                      `<div class="${styles.basics}">
                        <label for="title">${strings.Title}</label><br />
                        <input type="text" id="title" value="${this._item?.Title || ''}"/>
                        <br />
                        <br />
                        <input type="button" id="save" value="${strings.Save}" />
                        <input type="button" id="cancel" value="${strings.Cancel}" />
                        <br />
                        <br />
                        <div class="${styles.error}"></div>
                      </div>`;
    
          document.getElementById('save').addEventListener('click', this._onSave.bind(this));
          document.getElementById('cancel').addEventListener('click', this._onClose.bind(this));
        }
      }
    
  9. Actualice los métodos _onSave de la clase HelloWorldFormCustomizer de la siguiente manera.

    private _onSave = async (): Promise<void> => {
      // disable all input elements while we're saving the item
      this.domElement.querySelectorAll('input').forEach(el => el.setAttribute('disabled', 'disabled'));
      // reset previous error message if any
      this.domElement.querySelector(`.${styles.error}`).innerHTML = '';
    
      let request: Promise<SPHttpClientResponse>;
      const title: string = (document.getElementById('title') as HTMLInputElement).value;
    
      switch (this.displayMode) {
        case FormDisplayMode.New:
          request = this._createItem(title);
          break;
        case FormDisplayMode.Edit:
          request = this._updateItem(title);
      }
    
      const res: SPHttpClientResponse = await request;
    
      if (res.ok) {
        // You MUST call this.formSaved() after you save the form.
        this.formSaved();
      }
      else {
        const error: { error: { message: string } } = await res.json();
    
        this.domElement.querySelector(`.${styles.error}`).innerHTML = `An error has occurred while saving the item. Please try again. Error: ${error.error.message}`;
        this.domElement.querySelectorAll('input').forEach(el => el.removeAttribute('disabled'));
      }
    }
    
  10. Agregue un nuevo método _createItem a la clase HelloWorldFormCustomizer .

    private _createItem(title: string): Promise<SPHttpClientResponse> {
      return this.context.spHttpClient
        .post(this.context.pageContext.web.absoluteUrl + `/_api/web/lists/getByTitle('${this.context.list.title}')/items`, SPHttpClient.configurations.v1, {
          headers: {
            'content-type': 'application/json;odata.metadata=none'
          },
          body: JSON.stringify({
            Title: title
          })
        });
    }
    
  11. Agregue un nuevo método _updateItem a la clase HelloWorldFormCustomizer .

    private _updateItem(title: string): Promise<SPHttpClientResponse> {
      return this.context.spHttpClient
        .post(this.context.pageContext.web.absoluteUrl + `/_api/web/lists/getByTitle('${this.context.list.title}')/items(${this.context.itemId})`, SPHttpClient.configurations.v1, {
          headers: {
            'content-type': 'application/json;odata.metadata=none',
            'if-match': this._etag,
            'x-http-method': 'MERGE'
          },
          body: JSON.stringify({
            Title: title
          })
        });
    }
    

Ahora el código está completo para admitir experiencias mínimas nuevas, de edición y de visualización, y puede probar las distintas experiencias mediante diferentes configuraciones para la depuración.

Formulario personalizado en el contexto de SharePoint

Implementación de la extensión

Siempre que esté listo para empezar a usar el componente, hay algunos pasos para considerar relacionados con la asociación del componente con el tipo de contenido. Los pasos para la implementación son los siguientes:

  1. Implementación de una solución en el catálogo de aplicaciones de SharePoint
  2. Instale la solución en la colección de sitios donde desea usar la extensión si no usa la implementación con ámbito de inquilino.
  3. Asocie el componente personalizado al tipo de contenido mediante las propiedades específicas del objeto ContentType. Hay pocas opciones para hacer esto:
    1. Puede aprovisionar la lista usada y el tipo de contenido de la solución si usa la opción de implementación con ámbito de sitio.
    2. Puede asociar el componente a tipos de contenido mediante las API REST o CSOM. Tenga en cuenta que si asocia el componente en la colección de sitios o en el nivel del centro de tipos de contenido, se hereda automáticamente a todas las nuevas instancias de tipo de contenido.