Compartir a través de


Usar menús desplegables en cascada en propiedades de elementos web

Al diseñar el panel de propiedades para los elementos web del lado cliente de SharePoint, es posible que tenga una propiedad de elemento web que muestre sus opciones en función del valor seleccionado en otra propiedad. Esta situación suele producirse al implementar controles desplegables en cascada. En este artículo, aprenderá a crear controles desplegables en cascada en el panel de propiedades del elemento web sin desarrollar un control de panel de propiedades personalizado.

Desplegable de elementos deshabilitado y marcador de posición de un elemento web que comunica la lista actualizada de opciones de elementos que se está cargando

El origen del elemento web de trabajo está disponible en GitHub en sp-dev-fx-webparts/samples/react-custompropertypanecontrols/.

Nota:

Antes de seguir los pasos que se indican en el artículo, asegúrese de configurar el entorno de desarrollo del elemento web del lado cliente de SharePoint.

Crear un proyecto

  1. Empiece por crear una carpeta para el proyecto:

    md react-cascadingdropdowns
    
  2. Vaya a la carpeta del proyecto:

    cd react-cascadingdropdowns
    
  3. En la carpeta del proyecto, ejecute el generador de Yeoman de SharePoint Framework para aplicar scaffolding a un nuevo proyecto de SharePoint Framework:

    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):

    • ¿Cómo se llama su solución? react-cascadingdropdowns
    • ¿Cuál es el tipo de componente del lado cliente que se va a crear?: Elemento web
    • ¿Cómo se llama su elemento web?: Enumerar elementos
    • ¿Qué plantilla desea usar?: React
  5. Abra la carpeta del proyecto en el editor de código. En los pasos y capturas de pantalla de este artículo, se usa Visual Studio Code, pero puede usar el editor que prefiera.

Defina la propiedad de un elemento web para almacenar la lista seleccionada.

Compilará un elemento web que muestra los elementos de una lista de SharePoint seleccionada. Los usuarios pueden seleccionar una lista en el panel de propiedad del elemento web. Para almacenar la lista seleccionada, cree una propiedad de elemento web denominada listName.

  1. En el editor de código, abra el archivo src/webparts/listItems/ListItemsWebPartManifest.json. Reemplace la propiedad de description predeterminada por una nueva propiedad denominada listName.

    {
      ...
      "preconfiguredEntries": [{
        ...
        "properties": {
          "listName": ""
        }
      }]
    }
    
  2. Abra el archivo src/webparts/listItems/ListItemsWebPart.ts y reemplace la interfaz IListItemsWebPartProps por lo siguiente:

    export interface IListItemsWebPartProps {
      listName: string;
    }
    
  3. En el archivo src/webparts/listItems/ListItemsWebPart.ts, cambie el método render() a:

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
      public render(): void {
        const element: React.ReactElement<IListItemsProps> = React.createElement(ListItems, {
          listName: this.properties.listName
        });
    
        ReactDom.render(element, this.domElement);
      }
      // ...
    }
    
  4. Actualizar getPropertyPaneConfiguration() a:

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
    
      protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
        return {
          pages: [
            {
              header: {
                description: strings.PropertyPaneDescription
              },
              groups: [
                {
                  groupName: strings.BasicGroupName,
                  groupFields: [
                    PropertyPaneTextField('listName', {
                      label: strings.ListNameFieldLabel
                    })
                  ]
                }
              ]
            }
          ]
        };
      }
    
      // ...
    }
    
  5. En el archivo src/webparts/listItems/loc/mystrings.d.ts cambie la interfaz IListItemsStrings a:

    declare interface IListItemsStrings {
      PropertyPaneDescription: string;
      BasicGroupName: string;
      ListNameFieldLabel: string;
    }
    
  6. En el archivo src/webparts/listItems/loc/en-us.js agregue la definición que falta para la cadena ListNameFieldLabel:

    define([], function() {
      return {
        "PropertyPaneDescription": "Description",
        "BasicGroupName": "Group Name",
        "ListNameFieldLabel": "List"
      }
    });
    
  7. En el archivo src/webparts/listItems/components/ListItems.tsx, cambie los contenidos del método render() a:

    public render(): JSX.Element {
      const {
        listName
      } = this.props;
    
      return (
        <section className={`${styles.listItems} ${hasTeamsContext ? styles.teams : ''}`}>
          <div className={styles.welcome}>
            <img alt="" src={isDarkTheme ? require('../assets/welcome-dark.png') : require('../assets/welcome-light.png')} className={styles.welcomeImage} />
            <h2>Well done, {escape(userDisplayName)}!</h2>
            <div>{environmentMessage}</div>
            <div>List name: <strong>{escape(listName)}</strong></div>
          </div>
        </section>
      );
    }
    
  8. En el archivo src/webparts/listItems/components/IListItemsProps.ts, cambie la interfaz IListItemsProps a:

    export interface IListItemsProps {
      listName: string;
    }
    
  9. Ejecute el comando siguiente para comprobar que el proyecto se está ejecutando:

    gulp serve
    
  10. En el explorador web, agregue el elemento web Lista de elementos al lienzo y abra sus propiedades. Compruebe que el valor establecido para la propiedad List se muestra en el cuerpo del elemento web.

    Elemento web que muestra el valor de la propiedad listName

Rellenar el desplegable con listas de SharePoint

En este punto el usuario especifica qué lista debe usar el elemento web, para lo que escribe manualmente el nombre de la lista. Este proceso es propenso a los errores. Idealmente, los usuarios deberían elegir una de las listas existentes en el sitio de SharePoint actual.

Usar el control desplegable para representar la propiedad listName

  1. En la clase ListItemsWebPart, agregue una referencia a la clase PropertyPaneDropdown en la sección superior del elemento web. Reemplace la cláusula import que carga la clase PropertyPaneTextField por:

    import {
      IPropertyPaneConfiguration,
      PropertyPaneTextField,
      PropertyPaneDropdown,
      IPropertyPaneDropdownOption
    } from '@microsoft/sp-property-pane';
    
  2. En la clase ListItemsWebPart, agregue una variable llamada lists para almacenar información sobre todas las listas disponibles en el sitio actual:

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      private lists: IPropertyPaneDropdownOption[];
      // ...
    }
    
  3. Agregue una nueva variable de clase denominada listsDropdownDisabled. Esta variable determina si se habilita el desplegable de listas. Hasta que el elemento web recupere la información sobre las listas disponibles en el sitio actual, el desplegable debería estar inhabilitado.

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
      private listsDropdownDisabled: boolean = true;
      // ...
    }
    
  4. Cambie el método getPropertyPaneConfiguration() para usar el control desplegable y representar la propiedad listName:

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
    
      protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
        return {
          pages: [
            {
              header: {
                description: strings.PropertyPaneDescription
              },
              groups: [
                {
                  groupName: strings.BasicGroupName,
                  groupFields: [
                    PropertyPaneDropdown('listName', {
                      label: strings.ListNameFieldLabel,
                      options: this.lists,
                      disabled: this.listsDropdownDisabled
                    })
                  ]
                }
              ]
            }
          ]
        };
      }
    
    }
    
  5. Ejecute el comando siguiente para comprobar que está funcionando como se esperaba:

    gulp serve
    

    Propiedad listName representada en el panel de propiedades del elemento web usando un control desplegable

Mostrar las listas disponibles en el desplegable de lista

Anteriormente, asoció el control desplegable de la propiedad listName a la propiedad de la clase lists. Dado que todavía no ha cargado ningún valor, el desplegable Lista del panel de propiedades del elemento web permanecerá inhabilitado. En esta sección, ampliará el elemento web para cargar la información sobre las listas disponibles.

  1. En la clase ListItemsWebPart, agregue un método para cargar las listas disponibles Usará datos simulados, pero también podría llamar a la API de REST de SharePoint para recuperar la lista de listas disponibles en la web actual. Para simular las opciones de carga desde un servicio externo, el método usa un retraso de dos segundos

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
    
      private loadLists(): Promise<IPropertyPaneDropdownOption[]> {
        return new Promise<IPropertyPaneDropdownOption[]>((resolve: (options: IPropertyPaneDropdownOption[]) => void, reject: (error: any) => void) => {
          setTimeout((): void => {
            resolve([{
              key: 'sharedDocuments',
              text: 'Shared Documents'
            },
            {
              key: 'myDocuments',
              text: 'My Documents'
            }]);
          }, 2000);
        });
      }
    }
    
  2. Cargue información sobre las listas disponibles en el desplegable de lista. En la clase ListItemsWebPart, invalide el método onPropertyPaneConfigurationStart() con el código siguiente:

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
    
      protected onPropertyPaneConfigurationStart(): void {
        this.listsDropdownDisabled = !this.lists;
    
        if (this.lists) {
          return;
        }
    
        this.context.statusRenderer.displayLoadingIndicator(this.domElement, 'lists');
    
        this.loadLists()
          .then((listOptions: IPropertyPaneDropdownOption[]): void => {
            this.lists = listOptions;
            this.listsDropdownDisabled = false;
            this.context.propertyPane.refresh();
            this.context.statusRenderer.clearLoadingIndicator(this.domElement);
            this.render();
          });
      }
      // ...
    }
    

    SharePoint Framework llama al método onPropertyPaneConfigurationStart() después de que el panel de propiedades del elemento web se haya abierto.

    • En primer lugar, el método comprueba si se ha cargado la información sobre las listas disponibles en el sitio actual.
    • Si se ha cargado la información, se habilita el desplegable de listas.
    • Si aún no se ha cargado, se muestra el indicador de carga, que permite al usuario saber si el elemento web está cargando información sobre las listas.

    Indicador de carga que se muestra en el elemento web al cargar información sobre las listas disponibles

    Después de cargar la información sobre las listas disponibles, el método asigna los datos recuperados a la variable de clase lists, desde donde puede usarlos el desplegable de listas.

    Después se habilita el desplegable, lo que permite al usuario seleccionar una lista. Al llamar al método this.context.propertyPane.refresh(), el panel de propiedades del elemento web se actualiza para reflejar los últimos cambios en el desplegable de listas.

    Una vez cargada la información sobre las listas, se quita el indicador de carga mediante una llamada al método clearLoadingIndicator(). Puesto que llamar a este método hace que se borre la interfaz de usuario del elemento web, se llama al método render() para que el elemento web vuelva a representarse.

  3. Ejecute el comando siguiente para confirmar que todo funciona como se esperaba:

    gulp serve
    

    Al agregar un elemento web al lienzo y abrir su panel de propiedades, se mostrará el desplegable de listas, que contiene las listas disponibles para el usuario.

    Desplegable de listas en el panel de propiedades del elemento web que muestra las listas disponibles

Permitir a los usuarios que elijan un elemento de la lista seleccionada

Al compilar elementos web, a menudo es necesario permitir que los usuarios elijan una opción entre un conjunto de valores determinado por un valor seleccionado previamente (por ejemplo, elegir un país o región según el continente seleccionado, o un elemento de lista a partir de una lista seleccionada). Esta experiencia de usuario se conoce a menudo como menús desplegables en cascada. Gracias a las funciones estándar para elementos web del lado cliente de SharePoint Framework, puede compilar desplegables en cascada en el panel de propiedades del elemento web. Para hacerlo, debe expandir el elemento web ya compilado con la función para elegir un elemento de lista determinado por una lista seleccionada previamente.

Desplegable de elementos de lista abierto en el panel de propiedades del elemento web

Agregar propiedad de elemento web del elemento

  1. En el editor de código, abra el archivo src/webparts/listItems/ListItemsWebPart.manifest.json. Agregue una nueva propiedad a la sección properties denominada itemName similar a la siguiente:

    {
      // ...
      "properties": {
        "listName": "",
        "itemName": ""
      }
      // ...
    }
    
  2. Cambie el código en el archivo src/webparts/listItems/IListItemsWebPartProps.ts a:

    export interface IListItemsWebPartProps {
      listName: string;
      itemName: string;
    }
    
  3. Cambie el código en el archivo src/webparts/listItems/components/IListItemsProps.ts a:

    export interface IListItemsProps {
      listName: string;
      itemName: string;
    }
    
  4. En el archivo src/webparts/listItems/ListItemsWebPart.ts, cambie el código del método render() a:

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
      public render(): void {
        const element: React.ReactElement<IListItemsProps> = React.createElement(ListItems, {
          listName: this.properties.listName,
          itemName: this.properties.itemName
        });
    
        ReactDom.render(element, this.domElement);
      }
      // ...
    }
    
  5. En el archivo src/webparts/listItems/loc/mystrings.d.ts cambie la interfaz IListItemsStrings a:

    declare interface IListItemsStrings {
      PropertyPaneDescription: string;
      BasicGroupName: string;
      ListNameFieldLabel: string;
      ItemNameFieldLabel: string;
    }
    
  6. En el archivo src/webparts/listItems/loc/en-us.js agregue la definición que falta para la cadena ItemNameFieldLabel.

    define([], function() {
      return {
        "PropertyPaneDescription": "Description",
        "BasicGroupName": "Group Name",
        "ListNameFieldLabel": "List",
        "ItemNameFieldLabel": "Item"
      }
    });
    

Representar el valor de la propiedad de elemento web del elemento

En el archivo src/webparts/listItems/components/ListItems.tsx, cambie el método render() a:

export default class ListItems extends React.Component<IListItemsProps, {}> {
  public render(): JSX.Element {
    const {
      listName,
      itemName
    } = this.props;

    return (
      <section className={`${styles.listItems} ${hasTeamsContext ? styles.teams : ''}`}>
        <div className={styles.welcome}>
          <img alt="" src={isDarkTheme ? require('../assets/welcome-dark.png') : require('../assets/welcome-light.png')} className={styles.welcomeImage} />
          <h2>Well done, {escape(userDisplayName)}!</h2>
          <div>{environmentMessage}</div>
          <div>List name: <strong>{escape(listName)}</strong></div>
          <div>Item name: <strong>{escape(itemName)}</strong></div>
        </div>
      </section>
    );
  }
}

Permitir a los usuarios elegir un elemento de una lista

Del mismo modo que los usuarios pueden seleccionar una lista mediante un desplegable, pueden seleccionar un elemento de una lista de elementos disponibles.

  1. En la clase ListItemsWebPart, agregue una nueva variable llamada items para almacenar información sobre todos los elementos disponibles en la lista actual.

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
      private items: IPropertyPaneDropdownOption[];
      // ...
    }
    
  2. Agregue una nueva variable de clase denominada itemsDropdownDisabled. Esta variable determina si se habilita el desplegable de elementos. Los usuarios pueden seleccionar un elemento únicamente después de seleccionar una lista.

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
      private itemsDropdownDisabled: boolean = true;
      // ...
    }
    
  3. Cambie el método getPropertyPaneConfiguration() para usar el control desplegable y representar la propiedad itemName.

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
      protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
        return {
          pages: [
            {
              header: {
                description: strings.PropertyPaneDescription
              },
              groups: [
                {
                  groupName: strings.BasicGroupName,
                  groupFields: [
                    PropertyPaneDropdown('listName', {
                      label: strings.ListNameFieldLabel,
                      options: this.lists,
                      disabled: this.listsDropdownDisabled
                    }),
                    PropertyPaneDropdown('itemName', {
                      label: strings.ItemNameFieldLabel,
                      options: this.items,
                      disabled: this.itemsDropdownDisabled
                    })
                  ]
                }
              ]
            }
          ]
        };
      }
    }
    
  4. Ejecute el comando siguiente para comprobar que está funcionando como se esperaba:

    gulp serve
    

    Propiedad itemName representada en el panel de propiedades del elemento web usando un control desplegable

Mostrar los elementos disponibles en la lista seleccionada en el desplegable de listas

Anteriormente, definió un control desplegable para representar la propiedad itemName en el panel de propiedades del elemento web. Después, deberá ampliar el elemento web para cargar la información sobre los elementos disponibles en la lista seleccionada y mostrarlos en el desplegable de elementos.

  1. Agregaue un método para cargar elementos de lista. En el archivo src/webparts/listItems/ListItemsWebPart.ts, en la clase ListItemsWebPart, agregue un nuevo método para cargar los elementos de lista disponibles de la lista seleccionada. Al igual que el método para cargar listas disponibles, usará datos simulados.

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
      private loadItems(): Promise<IPropertyPaneDropdownOption[]> {
        if (!this.properties.listName) {
          // resolve to empty options since no list has been selected
          return Promise.resolve();
        }
    
        const wp: ListItemsWebPart = this;
    
        return new Promise<IPropertyPaneDropdownOption[]>((resolve: (options: IPropertyPaneDropdownOption[]) => void, reject: (error: any) => void) => {
          setTimeout(() => {
            const items = {
              sharedDocuments: [
                {
                  key: 'spfx_presentation.pptx',
                  text: 'SPFx for the masses'
                },
                {
                  key: 'hello-world.spapp',
                  text: 'hello-world.spapp'
                }
              ],
              myDocuments: [
                {
                  key: 'isaiah_cv.docx',
                  text: 'Isaiah CV'
                },
                {
                  key: 'isaiah_expenses.xlsx',
                  text: 'Isaiah Expenses'
                }
              ]
            };
            resolve(items[wp.properties.listName]);
          }, 2000);
        });
      }
    }
    

    El método loadItems() devuelve elementos de lista de simulacro para la lista previamente seleccionada. Cuando no se haya seleccionado ninguna lista, el método resuelve la premisa sin ningún dato.

  2. Cargue información sobre los elementos disponibles en el desplegable de elementos. En la clase ListItemsWebPart, extienda el método onPropertyPaneConfigurationStart() para cargar los elementos de la lista seleccionada:

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
      protected onPropertyPaneConfigurationStart(): void {
        this.listsDropdownDisabled = !this.lists;
        this.itemsDropdownDisabled = !this.properties.listName || !this.items;
    
        if (this.lists) {
          return;
        }
    
        this.context.statusRenderer.displayLoadingIndicator(this.domElement, 'options');
    
        this.loadLists()
          .then((listOptions: IPropertyPaneDropdownOption[]): Promise<IPropertyPaneDropdownOption[]> => {
            this.lists = listOptions;
            this.listsDropdownDisabled = false;
            this.context.propertyPane.refresh();
            return this.loadItems();
          })
          .then((itemOptions: IPropertyPaneDropdownOption[]): void => {
            this.items = itemOptions;
            this.itemsDropdownDisabled = !this.properties.listName;
            this.context.propertyPane.refresh();
            this.context.statusRenderer.clearLoadingIndicator(this.domElement);
            this.render();
          });
      }
      // ...
    }
    

    Al inicializar, el elemento web determina primero si el desplegable de elementos debe habilitarse. Si el usuario ha seleccionado previamente una lista, puede seleccionar un elemento de dicha lista. Si no ha seleccionado ninguna lista, se deshabilita el desplegable de elementos.

    Ha ampliado el código previamente definido, que carga la información acerca de las listas disponibles, para cargar la información acerca de los elementos disponibles en la lista seleccionada. A continuación el código asignará la información obtenida a la variable de clase items para que la use el desplegable de elementos. Por último, el código borrará el indicador de carga y permitirá al usuario comenzar a trabajar con el elemento web.

  3. Ejecute el comando siguiente para confirmar que todo funciona como esperaba:

    gulp serve
    

    Según sea necesario, el desplegable de elementos estará inhabilitado inicialmente, por lo que los usuarios deberán seleccionar una lista primero. En este punto, incluso después de seleccionar una lista, el desplegable de elementos permanecerá inhabilitado.

    Desplegable de elementos inhabilitado incluso después de seleccionar una lista

  4. Actualice el panel de propiedades de un elemento web después de seleccionar una lista. Cuando un usuario selecciona una lista en el panel de propiedades, el elemento web debería actualizarse, de modo que se habilite el desplegable de elementos y se muestre la lista de elementos disponibles para la lista seleccionada.

    En el archivo ListItemsWebPart de la clase ListItemsWebPart, reemplace el método onPropertyPaneFieldChanged() por el código siguiente:

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
      protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void {
        if (propertyPath === 'listName' &&
            newValue) {
          // push new list value
          super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue);
          // get previously selected item
          const previousItem: string = this.properties.itemName;
          // reset selected item
          this.properties.itemName = undefined;
          // push new item value
          this.onPropertyPaneFieldChanged('itemName', previousItem, this.properties.itemName);
          // disable item selector until new items are loaded
          this.itemsDropdownDisabled = true;
          // refresh the item selector control by repainting the property pane
          this.context.propertyPane.refresh();
          // communicate loading items
          this.context.statusRenderer.displayLoadingIndicator(this.domElement, 'items');
    
          this.loadItems()
            .then((itemOptions: IPropertyPaneDropdownOption[]): void => {
              // store items
              this.items = itemOptions;
              // enable item selector
              this.itemsDropdownDisabled = false;
              // clear status indicator
              this.context.statusRenderer.clearLoadingIndicator(this.domElement);
              // re-render the web part as clearing the loading indicator removes the web part body
              this.render();
              // refresh the item selector control by repainting the property pane
              this.context.propertyPane.refresh();
            });
        }
        else {
          super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue);
        }
      }
      // ...
    }
    

    Cuando el usuario selecciona una lista, el elemento web mantiene el valor seleccionado. Puesto que la lista seleccionada ha cambiado, el elemento web restablece el elemento de lista previamente seleccionado. Con una lista seleccionada, el panel de propiedades del elemento web carga elementos de lista para esa lista en particular. Al cargar elementos, el usuario no puede seleccionar un elemento.

    Una vez que los elementos de la lista seleccionada estén cargados, se asignarán a la variable de clase items, lo que permite que se haga referencia a ellos mediante el desplegable de elementos. Ahora que la información sobre los elementos de lista disponibles está presente, el desplegable de elementos se ha habilitado y los usuarios pueden elegir un elemento. Se borra el indicador de carga y, por tanto, también el cuerpo del elemento web. Por este motivo, el elemento web debería volver a representarse. Finalmente, el panel de propiedades del elemento web se actualiza para reflejar los últimos cambios.

    Desplegable de elementos del panel de propiedades del elemento web que muestra los elementos de lista disponibles para la lista seleccionada.

Ver también