Compartilhar via


Usar menus suspensos em cascata nas propriedades da Web Part

Ao projetar o painel de propriedades para suas Web Parts do lado do cliente do SharePoint, você pode ter uma propriedade da Web Part que exibe suas opções com base no valor selecionado em outra propriedade. Este cenário normalmente ocorre ao serem implementados controles de menu suspenso em cascata. Neste artigo, você aprenderá a criar controles de menu suspenso em cascata no painel de propriedades da Web Part sem desenvolver um controle de painel de propriedades personalizado.

Menu suspenso de item desabilitado e espaço reservado de Web Part comunicando o carregamento da lista atualizada de opções de itens

A origem da Web Part em funcionamento está disponível no GitHub em sp-dev-fx-webparts/samples/react-custompropertypanecontrols/.

Criar novo projeto

  1. Comece criando uma nova pasta para o projeto:

    md react-cascadingdropdowns
    
  2. Vá até a pasta do projeto:

    cd react-cascadingdropdowns
    
  3. Na pasta do projeto, execute o gerador Yeoman da Estrutura do SharePoint para estruturar um novo projeto da Estrutura do SharePoint:

    yo @microsoft/sharepoint
    
  4. Quando solicitado, insira os seguintes valores (selecione a opção padrão para todos os avisos omitidos abaixo):

    • Qual é o nome da solução?: react-cascadingdropdowns
    • Que tipo de componente do lado do cliente você deseja criar?: WebPart
    • Qual é o nome da sua Web part?: itens da lista
    • Qual modelo você gostaria de usar?: React
  5. Abra a pasta do projeto no seu editor de código. Este artigo usa o Visual Studio Code nas etapas e capturas de tela, mas você pode usar o editor que preferir.

Definir uma propriedade de Web Part para armazenar a lista selecionada

Você criará uma Web Part que exibe itens de lista em uma lista do SharePoint selecionada. Os usuários poderão selecionar uma lista no painel de propriedades da Web Part. Para armazenar a lista selecionada, crie uma nova propriedade de Web Part denominada listName.

  1. No editor de código, abra o arquivo src/webparts/listItems/ListItemsWebPartManifest.json. Substituir a propriedade padrão description por uma nova propriedade denominada listName.

    {
      ...
      "preconfiguredEntries": [{
        ...
        "properties": {
          "listName": ""
        }
      }]
    }
    
  2. Abra o arquivo src/webparts/listItems/ListItemsWebPart.ts e substitua a interface IListItemsWebPartProps pelo seguinte:

    export interface IListItemsWebPartProps {
      listName: string;
    }
    
  3. No arquivo src/webparts/listItems/ListItemsWebPart.ts, altere o método render() para:

    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. Atualize getPropertyPaneConfiguration() para:

    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. No arquivo src/webparts/listItems/loc/mystrings.d.ts, altere a interface IListItemsStrings para:

    declare interface IListItemsStrings {
      PropertyPaneDescription: string;
      BasicGroupName: string;
      ListNameFieldLabel: string;
    }
    
  6. No arquivo src/webparts/listItems/loc/en-us.js, adicione a definição ausente à cadeia de caracteres de ListNameFieldLabel:

    define([], function() {
      return {
        "PropertyPaneDescription": "Description",
        "BasicGroupName": "Group Name",
        "ListNameFieldLabel": "List"
      }
    });
    
  7. No arquivo src/webparts/listItems/components/ListItems.tsx, altere o conteúdo do método render() para:

    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. No arquivo src/webparts/listItems/components/IListItemsProps.ts, altere a interface IListItemsProps para:

    export interface IListItemsProps {
      listName: string;
    }
    
  9. Execute o seguinte comando para verificar se o projeto está em execução:

    gulp serve
    
  10. No navegador da Web, adicione a Web Part Itens da lista à tela e abra as propriedades dela. Verifique se o valor definido para a propriedade List é exibido no corpo da Web Part.

    Web Part mostrando o valor da propriedade listName

Popular o menu suspenso com listas do SharePoint para escolha

Neste ponto, um usuário especifica qual lista a Web Part deve usar, inserindo manualmente o nome da lista. Podem ocorrer erros, e o ideal é que os usuários escolham uma das listas existentes no site do SharePoint atual.

Usar o controle de menu suspenso para renderizar a propriedade listName

  1. Na classe ListItemsWebPart, adicione uma referência à classe PropertyPaneDropdown na seção superior da Web Part. Substitua a cláusula import que carrega a classe PropertyPaneTextField por:

    import {
      IPropertyPaneConfiguration,
      PropertyPaneTextField,
      PropertyPaneDropdown,
      IPropertyPaneDropdownOption
    } from '@microsoft/sp-property-pane';
    
  2. Na classe ListItemsWebPart, adicione uma nova variável denominada lists para armazenar informações sobre todas as listas disponíveis no site atual:

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      private lists: IPropertyPaneDropdownOption[];
      // ...
    }
    
  3. Adicione uma nova variável de classe chamada listsDropdownDisabled. Essa variável determina se o menu suspenso de lista é habilitado ou não. Até que a Web Part recupere as informações sobre as listas disponíveis no site atual, o menu suspenso deve ser desabilitado.

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
      private listsDropdownDisabled: boolean = true;
      // ...
    }
    
  4. Altere o método getPropertyPaneConfiguration() para usar o controle suspenso para renderizar a propriedade 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. Execute o seguinte comando para verificar se está funcionando conforme o esperado:

    gulp serve
    

    A propriedade listName renderizada no painel de propriedades da Web Part usando um controle de menu suspenso

Mostrar listas disponíveis no menu suspenso de lista

Anteriormente, você associou o controle de menu suspenso da propriedade listName à propriedade de classe lists. Como você ainda não carregou valores nele, o menu suspenso Lista no painel de propriedades da Web Part permanece desabilitado. Nesta seção, você estenderá a Web Part para carregar as informações sobre as listas disponíveis.

  1. Na classe ListItemsWebPart, adicione um método para carregar as listas disponíveis. Você usará dados de simulação, mas também poderá chamar a API REST do SharePoint para recuperar a lista de listas disponíveis da Web atual. Para simular as opções de carregamento de um serviço externo, o método usa um atraso de dois 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. Carregue informações sobre listas disponíveis no menu suspenso de lista. Na classe ListItemsWebPart, substitua o método onPropertyPaneConfigurationStart() usando o seguinte código:

    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();
          });
      }
      // ...
    }
    

    O método onPropertyPaneConfigurationStart() é chamado pela Estrutura do SharePoint após o painel de propriedades da Web Part ser aberto.

    • Primeiro, o método verifica se as informações sobre as listas disponíveis no site atual foram carregadas.
    • Se as informações da lista forem carregadas, o menu suspenso de lista será habilitado.
    • Se as informações sobre as listas da lista ainda não tiverem sido carregadas, o indicador de carregamento será exibido para informar ao usuário que a Web Part está carregando informações sobre listas.

    Indicador de carregamento exibido na Web Part ao carregar informações sobre as listas disponíveis

    Depois que as informações sobre listas disponíveis são carregadas, o método atribui os dados recuperados à variável de classe lists, por meio da qual eles podem ser usados pelo menu suspenso de lista.

    Em seguida, o menu suspenso de lista é habilitado, permitindo que o usuário selecione uma lista. Quando o método this.context.propertyPane.refresh() é chamado, o painel de propriedades da Web Part é atualizado e reflete as mudanças mais recentes para o menu suspenso de lista.

    Depois que as informações da lista são carregadas, o indicador de carregamento é removido por uma chamada ao método clearLoadingIndicator(). Como chamar esse método limpa a interface do usuário da Web Part, o método render() é chamado para forçar a Web Part a realizar a renderização novamente.

  3. Execute o seguinte comando para confirmar que tudo está funcionando conforme o esperado:

    gulp serve
    

    Ao adicionar uma Web Part à tela e abrir o painel de propriedade, você deverá ver o menu suspenso de lista preenchido com as listas disponíveis para que o usuário as escolha.

    Menu suspenso de lista no painel de propriedades da Web Part mostrando as listas disponíveis

Permitir que os usuários selecionem um item da lista selecionada

Ao criar Web Parts, muitas vezes é necessário permitir que os usuários escolham uma opção de um conjunto de valores determinados por um valor selecionado anteriormente, como escolher um país/região com base no continente selecionado ou escolher um item de lista de uma lista selecionada. Essa experiência do usuário é conhecida como menus suspensos em cascata. Usando os recursos de Web Part do lado do cliente padrão da Estrutura do SharePoint, você pode compilar menus suspensos em cascata no painel de propriedades da Web Part. Para fazer isso, você estenderá a Web Part previamente compilada com a capacidade de escolher um item de lista com base na lista selecionada anteriormente.

Menu suspenso de item de lista aberto no painel de propriedades da Web Part

Adicionar a propriedade do item da Web Part

  1. No editor de código, abra o arquivo src/webparts/listItems/ListItemsWebPart.manifest.json. Para a seção properties, adicione uma nova propriedade denominada itemName para que ela seja exibida da seguinte forma:

    {
      // ...
      "properties": {
        "listName": "",
        "itemName": ""
      }
      // ...
    }
    
  2. Altere o código no arquivo src/webparts/listItems/IListItemsWebPartProps.ts para:

    export interface IListItemsWebPartProps {
      listName: string;
      itemName: string;
    }
    
  3. Altere o código no arquivo src/webparts/listItems/components/IListItemsProps.ts para:

    export interface IListItemsProps {
      listName: string;
      itemName: string;
    }
    
  4. No arquivo src/webparts/listItems/ListItemsWebPart.ts, altere o código do método render() para:

    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. No arquivo src/webparts/listItems/loc/mystrings.d.ts, altere a interface IListItemsStrings para:

    declare interface IListItemsStrings {
      PropertyPaneDescription: string;
      BasicGroupName: string;
      ListNameFieldLabel: string;
      ItemNameFieldLabel: string;
    }
    
  6. No arquivo src/webparts/listItems/loc/en-us.js, adicione a definição ausente à cadeia de caracteres de ItemNameFieldLabel.

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

Renderizar o valor da propriedade da Web Part do item

No arquivo src/webparts/listItems/components/ListItems.tsx, altere o método render() para:

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 que os usuários escolham o item em uma lista

Da mesma forma como os usuários podem selecionar uma lista usando um menu suspenso, eles podem selecionar o item da lista de itens disponíveis.

  1. Na classe ListItemsWebPart, adicione uma nova variável denominada items, que você usará para armazenar informações sobre todos os itens disponíveis na lista atualmente selecionada.

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
      private items: IPropertyPaneDropdownOption[];
      // ...
    }
    
  2. Adicione uma nova variável de classe chamada itemsDropdownDisabled. Essa variável determina se o menu suspenso de item deve ser habilitado ou não. Os usuários poderão selecionar um item apenas depois que selecionarem uma lista.

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
      private itemsDropdownDisabled: boolean = true;
      // ...
    }
    
  3. Altere o método getPropertyPaneConfiguration() para usar o controle suspenso para renderizar a propriedade 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. Execute o seguinte comando para verificar se está funcionando conforme o esperado:

    gulp serve
    

    A propriedade itemName renderizada no painel de propriedades da Web Part usando um controle de menu suspenso

Mostrar itens disponíveis na lista selecionada no menu suspenso de item

Anteriormente, você definiu um controle de menu suspenso para renderizar a propriedade itemName no painel de propriedades da Web Part. Em seguida, você estenderá a Web Part para carregar as informações sobre itens disponíveis na lista selecionada e mostrar os itens no menu suspenso de item.

  1. Adicione um método para carregar itens de lista. No arquivo src/webparts/listItems/ListItemsWebPart.ts, na classe ListItemsWebPart, adicione um novo método para carregar itens da lista disponíveis da lista selecionada. (Como o método para carregar listas disponíveis, você usará dados de simulação.)

    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);
        });
      }
    }
    

    O método loadItems() retorna itens de lista fictícios para a lista selecionada anteriormente. Quando nenhuma lista tiver sido selecionada, o método resolverá a promessa sem nenhum dado.

  2. Carregue informações sobre itens disponíveis no menu suspenso de item. Na classe ListItemsWebPart, estenda o método onPropertyPaneConfigurationStart() para carregar itens para a lista selecionada:

    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();
          });
      }
      // ...
    }
    

    Durante a inicialização, primeiro a Web Part determinará se o menu suspenso de item deverá ser habilitado ou não. Se o usuário tiver selecionado anteriormente uma lista, ele poderá selecionar um item da lista. Se nenhuma lista for selecionada, o menu suspenso de item será desabilitado.

    Você estendeu o código definido anteriormente, que carrega as informações sobre listas disponíveis, para carregar as informações sobre itens disponíveis na lista selecionada. O código atribui as informações recuperadas à variável de classe items para uso pelo menu suspenso de item. Por fim, o código limpa o indicador de carregamento e permite que o usuário comece a trabalhar com a Web Part.

  3. Execute o seguinte comando para confirmar que tudo está funcionando conforme o esperado:

    gulp serve
    

    Conforme necessário, inicialmente o menu suspensa de item está desabilitado, exigindo que os usuários selecionem uma lista primeiro. Porém, nesse ponto, mesmo depois de uma lista ter sido selecionada, o menu suspenso de item permanece desabilitado.

    Menu suspenso de item desabilitado mesmo depois que uma lista é selecionada

  4. Atualize o painel de propriedades da Web Part após selecionar uma lista Quando um usuário seleciona uma lista no painel de propriedades, a Web Part deve ser atualizada, habilitando o menu suspenso de item e mostrando a lista de itens disponíveis na lista selecionada.

    No arquivo ListItemsWebPart.ts, na classe ListItemsWebPart, substitua o método onPropertyPaneFieldChanged() pelo seguinte código:

    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);
        }
      }
      // ...
    }
    

    Depois que o usuário seleciona uma lista, a Web Part persiste o valor recém-selecionado. Como a lista selecionada foi alterada, a Web Part redefine o item selecionado anteriormente. Agora que uma lista foi selecionada, o painel de propriedades da Web Part carrega itens de lista para essa lista específica. Durante o carregamento de itens, o usuário não pode selecionar um item.

    Depois que os itens para a lista selecionada são carregados, eles são atribuídos à variável de classe items, da qual podem ser referenciados pelo menu suspenso de item. Agora que as informações sobre itens de lista disponíveis estão disponíveis, o menu suspenso de item está habilitado, permitindo que os usuários escolham um item. O indicador de carregamento é removido, o que limpa o corpo da Web Part. É por isso que a Web Part deve ser renderizada novamente. Por fim, o painel de propriedades da Web Part é atualizado para refletir as alterações mais recentes.

    Menu suspenso de item no painel de propriedades da Web Part mostrando itens de lista disponíveis para a lista selecionada

Confira também