Compartilhar via


Criar controles personalizados para o painel de propriedades

A Estrutura do SharePoint contém um conjunto de controles padrão para o painel de propriedades. No entanto, às vezes, você precisa de funcionalidade adicional além dos controles básicos. Talvez seja preciso fazer atualizações assíncronas dos dados em um controle ou em uma interface de usuário específica. Crie um controle personalizado para o painel de propriedades para obter a funcionalidade de que você precisa.

Neste artigo, você criará um controle suspenso personalizado que carregue seus dados de forma assíncrona a partir de um serviço externo sem bloquear a interface do usuário da web part.

Menu suspenso de itens carregando itens disponíveis após a seleção de uma lista no menu suspenso de listas

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

Observação

Antes de seguir as etapas neste artigo, configure seu ambiente de desenvolvimento para criar soluções da Estrutura do SharePoint.

Criar novo projeto

  1. Comece criando uma nova pasta para o projeto:

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

    cd react-custompropertypanecontrol
    
  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):

    • Que tipo de componente do cliente será criado? WebPart
    • Qual é o nome da Web Part? Listar itens
    • Qual modelo você deseja usar? React
  5. Abra a pasta do projeto no seu editor de código.

Definir a propriedade da web part para armazenar a lista selecionada

A web part que você está criando mostrará os itens da lista a partir da lista selecionada. Os usuários podem selecionar uma lista nas propriedades da web part. Para armazenar a lista selecionada, crie uma nova propriedade da web part denominada listName.

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

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

    export interface IListItemsWebPartProps {
      description: string;
      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,
            description: this.properties.description,
            isDarkTheme: this._isDarkTheme,
            environmentMessage: this._environmentMessage,
            hasTeamsContext: !!this.context.sdks.microsoftTeams,
            userDisplayName: this.context.pageContext.user.displayName
          }
        );
    
        ReactDom.render(element, this.domElement);
      }
    
      // ...
    }
    
  4. Atualize o método 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.ListFieldLabel
                    })
                  ]
                }
              ]
            }
          ]
        };
      }
    
      // ...
    }
    
  5. No arquivo src/webparts/listItems/loc/mystrings.d.ts, adicione uma nova propriedade ListFieldLabel do tipo string à interface IListItemsWebPartStrings existente:

    declare interface IListItemsWebPartStrings {
      PropertyPaneDescription: string;
      BasicGroupName: string;
      ..
      ListFieldLabel: string;
    }
    
  6. No arquivo src/webparts/listItems/loc/en-us.js, ListFieldLabel adicione uma nova propriedade ao objeto retornado:

    define([], function() {
      return {
        "PropertyPaneDescription": "Description",
        "BasicGroupName": "Group Name",
        ...
        "ListFieldLabel": "List"
      }
    });
    
  7. Abra o arquivo src/webparts/listItems/components/IListItemsProps.ts, adicione a propriedade listName à interface de lista:

    export interface IListItemsProps {
      description: string;
      isDarkTheme: boolean;
      environmentMessage: string;
      hasTeamsContext: boolean;
      userDisplayName: string;
      listName: string;
    }
    
  8. No arquivo src/webparts/listItems/components/ListItems.tsx, altere o conteúdo do método render() para:

    export default class ListItems extends React.Component<IListItemsProps, {}> {
      public render(): React.ReactElement<IListItemsProps> {
        const {
          description,
          isDarkTheme,
          environmentMessage,
          hasTeamsContext,
          userDisplayName,
          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>
        );
      }
    }
    
  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”

Criar um controle do painel de propriedades de lista suspensa assíncrono

O SharePoint Framework oferece a você um controle de lista suspensa padrão que permite que os usuários selecionem um valor específico. O controle de lista suspensa é criado de um modo que requer que todos seus valores sejam conhecidos antecipadamente. Se você quiser carregar os valores dinamicamente ou estiver carregando valores de forma assíncrona de um serviço externo e não quiser bloquear toda a Web Part, criar um controle suspenso personalizado é uma opção.

Ao criar um controle do painel de propriedades personalizado que utilize o React na Estrutura do SharePoint, o controle consiste em uma classe que registra o controle com a Web Part e um componente do React que renderiza a lista suspensa e gerencia seus dados.

Adicionar um componente do React do controle do painel de propriedades de lista suspensa assíncrono

  1. Criar a pasta de componentes. Na pasta src do projeto, crie uma hierarquia de três novas pastas para que sua estrutura de pastas seja exibida como src/controls/PropertyPaneAsyncDropdown/components.

    A pasta de componentes realçada no Visual Studio Code

  2. Defina propriedades assíncronas de componentes do React de lista suspensa. Na pasta src/controls/PropertyPaneAsyncDropdown/components crie um novo arquivo denominado IAsyncDropdownProps.ts e digite o seguinte código:

    import { IDropdownOption } from 'office-ui-fabric-react/lib/components/Dropdown';
    
    export interface IAsyncDropdownProps {
      label: string;
      loadOptions: () => Promise<IDropdownOption[]>;
      onChanged: (option: IDropdownOption, index?: number) => void;
      selectedKey: string | number;
      disabled: boolean;
      stateKey: string;
    }
    

    A classe IAsyncDropdownProps define propriedades que podem ser definidas no componente React usado pelo controle do painel de propriedades personalizado:

    • A propriedade label especifica o rótulo do controle suspenso.
    • A função associada ao representante loadOptions é chamada pelo controle para carregar as opções disponíveis.
    • A função associada ao representante onChanged é chamada depois que o usuário seleciona uma opção no menu suspenso.
    • A propriedade selectedKey especifica o valor selecionado que pode ser uma cadeia de caracteres ou um número.
    • A propriedade disabled especifica se o controle da lista suspensa está desabilitado ou não.
    • A propriedade stateKey é usada para forçar uma nova renderização do componente React.
  3. Defina a interface de componente do React de lista suspensa assíncrono. Na pasta src/controls/PropertyPaneAsyncDropdown/components, crie um novo arquivo denominado IAsyncDropdownState.ts e digite o seguinte código:

    import { IDropdownOption } from 'office-ui-fabric-react/lib/components/Dropdown';
    
    export interface IAsyncDropdownState {
      loading: boolean;
      options: IDropdownOption[];
      error: string;
    }
    

    A interface IAsyncDropdownState descreve o estado do componente React:

    • A propriedade loading determina se o componente carrega suas opções no momento determinado.
    • A propriedade options contém todas as opções disponíveis.
    • Se ocorrer um erro, ele será atribuído à propriedade error de onde ele será comunicado ao usuário.
  4. Defina o componente do React de lista suspensa assíncrono. Na pasta src/controls/PropertyPaneAsyncDropdown/components crie um novo arquivo denominado AsyncDropdown.tsx e digite o seguinte código:

    import * as React from 'react';
    import { Dropdown, IDropdownOption } from 'office-ui-fabric-react/lib/components/Dropdown';
    import { Spinner } from 'office-ui-fabric-react/lib/components/Spinner';
    import { IAsyncDropdownProps } from './IAsyncDropdownProps';
    import { IAsyncDropdownState } from './IAsyncDropdownState';
    
    export default class AsyncDropdown extends React.Component<IAsyncDropdownProps, IAsyncDropdownState> {
      private selectedKey: React.ReactText;
    
      constructor(props: IAsyncDropdownProps, state: IAsyncDropdownState) {
        super(props);
        this.selectedKey = props.selectedKey;
    
        this.state = {
          loading: false,
          options: undefined,
          error: undefined
        };
      }
    
      public componentDidMount(): void {
        this.loadOptions();
      }
    
      public componentDidUpdate(prevProps: IAsyncDropdownProps, prevState: IAsyncDropdownState): void {
        if (this.props.disabled !== prevProps.disabled ||
          this.props.stateKey !== prevProps.stateKey) {
          this.loadOptions();
        }
      }
    
      private loadOptions(): void {
        this.setState({
          loading: true,
          error: undefined,
          options: undefined
        });
    
        this.props.loadOptions()
          .then((options: IDropdownOption[]): void => {
            this.setState({
              loading: false,
              error: undefined,
              options: options
            });
          }, (error: any): void => {
            this.setState((prevState: IAsyncDropdownState, props: IAsyncDropdownProps): IAsyncDropdownState => {
              prevState.loading = false;
              prevState.error = error;
              return prevState;
            });
          });
      }
    
      public render(): JSX.Element {
        const loading: JSX.Element = this.state.loading ? <div><Spinner label={'Loading options...'} /></div> : <div />;
        const error: JSX.Element = this.state.error !== undefined ? <div className={'ms-TextField-errorMessage ms-u-slideDownIn20'}>Error while loading items: {this.state.error}</div> : <div />;
    
        return (
          <div>
            <Dropdown label={this.props.label}
              disabled={this.props.disabled || this.state.loading || this.state.error !== undefined}
              onChanged={this.onChanged.bind(this)}
              selectedKey={this.selectedKey}
              options={this.state.options} />
            {loading}
            {error}
          </div>
        );
      }
    
      private onChanged(option: IDropdownOption, index?: number): void {
        this.selectedKey = option.key;
        // reset previously selected options
        const options: IDropdownOption[] = this.state.options;
        options.forEach((o: IDropdownOption): void => {
          if (o.key !== option.key) {
            o.selected = false;
          }
        });
        this.setState((prevState: IAsyncDropdownState, props: IAsyncDropdownProps): IAsyncDropdownState => {
          prevState.options = options;
          return prevState;
        });
        if (this.props.onChanged) {
          this.props.onChanged(option, index);
        }
      }
    }
    

    A classe AsyncDropdown representa o componente React utilizado para renderizar o controle do painel de propriedades de lista suspensa assíncrono:

    • Quando o componente é carregado pela primeira vez, o método componentDidMount() ou suas propriedades disabled ou stateKey mudam e ele carrega as opções disponíveis chamando o método loadOptions() passado pelas propriedades.
    • Depois que as opções são carregadas, o componente atualiza seu estado mostrando as opções disponíveis.
    • A lista suspensa em si é renderizada utilizando o Componente de lista suspensa do React do Office UI Fabric.
    • Quando o componente está carregando as opções disponíveis, ele exibe um controle giratório usando o Componente de controle giratório do React do Office UI Fabric.

O próximo passo é definir o controle do painel de propriedades personalizadas. Esse controle é usado dentro da Web Part ao definir propriedades no painel de propriedades e é renderizado usando o componente do React definido anteriormente.

Adicionar um controle do painel de propriedades de lista suspensa assíncrono

  1. Defina as propriedades de um controle do painel de propriedades de lista suspensa assíncrono. Um controle do painel de propriedades personalizado tem dois conjuntos de propriedades.

    O primeiro conjunto de propriedades está exposto publicamente e é usado para definir a propriedade da Web Part dentro da Web Part. Essas propriedades são específicas do componente, como o rótulo exibido ao lado do controle, valores mínimos e máximos para um controle giratório ou opções disponíveis para uma lista suspensa. Ao definir um controle do painel de propriedades personalizado, o tipo que descreve essas propriedades deve ser transmitido como o tipo TProperties durante a implementação da interface IPropertyPaneField<TProperties>.

    O segundo conjunto de propriedades é composto por propriedades particulares usadas internamente dentro do controle do painel de propriedades personalizado. Essas propriedades devem aderir às APIs do SharePoint Framework para que o controle personalizado seja renderizado corretamente. Essas propriedades devem implementar a IPropertyPaneCustomFieldProps interface do pacote @microsoft/sp-property-pane .

  2. Defina as propriedades públicas para o controle do painel de propriedades de lista suspensa assíncrono. Na pasta src/controls/PropertyPaneAsyncDropdown, crie um novo arquivo denominado IPropertyPaneAsyncDropdownProps.ts e digite o seguinte código:

    import { IDropdownOption } from 'office-ui-fabric-react/lib/components/Dropdown';
    
    export interface IPropertyPaneAsyncDropdownProps {
      label: string;
      loadOptions: () => Promise<IDropdownOption[]>;
      onPropertyChange: (propertyPath: string, newValue: any) => void;
      selectedKey: string | number;
      disabled?: boolean;
    }
    

    Na interface IPropertyPaneAsyncDropdownProps:

    • label: define o rótulo exibido ao lado da lista suspensa.
    • loadOptions: define o método que é chamado para carregar as opções suspensas disponíveis.
    • onPropertyChange: define um método que é chamado quando o usuário seleciona um valor na lista suspensa.
    • selectedKey: retorna o valor suspenso selecionado.
    • disabled: especifica se o controle está desabilitado ou não.
  3. Defina as propriedades internas do controle do painel de propriedades de lista suspensa assíncrono. Na pasta src/controls/PropertyPaneAsyncDropdown, crie um novo arquivo denominado IPropertyPaneAsyncDropdownInternalProps.ts e digite o seguinte código:

    import { IPropertyPaneCustomFieldProps } from '@microsoft/sp-property-pane';
    import { IPropertyPaneAsyncDropdownProps } from './IPropertyPaneAsyncDropdownProps';
    
    export interface IPropertyPaneAsyncDropdownInternalProps extends IPropertyPaneAsyncDropdownProps, IPropertyPaneCustomFieldProps {
    }
    

Embora a interface IPropertyPaneAsyncDropdownInternalProps não defina novas propriedades, ela combina as propriedades da interface IPropertyPaneAsyncDropdownProps definida anteriormente e da interface padrão IPropertyPaneCustomFieldProps da Estrutura do SharePoint, que é necessária para que um controle personalizado seja executado corretamente.

  1. Defina o controle do painel de propriedades de lista suspensa assíncrono. Na pasta src/controls/PropertyPaneAsyncDropdown crie um novo arquivo denominado PropertyPaneAsyncDropdown.ts e digite o seguinte código:

    import * as React from 'react';
    import * as ReactDom from 'react-dom';
    import {
      IPropertyPaneField,
      PropertyPaneFieldType
    } from '@microsoft/sp-property-pane';
    import { IDropdownOption } from 'office-ui-fabric-react/lib/components/Dropdown';
    import { IPropertyPaneAsyncDropdownProps } from './IPropertyPaneAsyncDropdownProps';
    import { IPropertyPaneAsyncDropdownInternalProps } from './IPropertyPaneAsyncDropdownInternalProps';
    import AsyncDropdown from './components/AsyncDropdown';
    import { IAsyncDropdownProps } from './components/IAsyncDropdownProps';
    
    export class PropertyPaneAsyncDropdown implements IPropertyPaneField<IPropertyPaneAsyncDropdownProps> {
      public type: PropertyPaneFieldType = PropertyPaneFieldType.Custom;
      public targetProperty: string;
      public properties: IPropertyPaneAsyncDropdownInternalProps;
      private elem: HTMLElement;
    
      constructor(targetProperty: string, properties: IPropertyPaneAsyncDropdownProps) {
        this.targetProperty = targetProperty;
        this.properties = {
          key: properties.label,
          label: properties.label,
          loadOptions: properties.loadOptions,
          onPropertyChange: properties.onPropertyChange,
          selectedKey: properties.selectedKey,
          disabled: properties.disabled,
          onRender: this.onRender.bind(this),
          onDispose: this.onDispose.bind(this)
        };
      }
    
      public render(): void {
        if (!this.elem) {
          return;
        }
    
        this.onRender(this.elem);
      }
    
      private onDispose(element: HTMLElement): void {
        ReactDom.unmountComponentAtNode(element);
      }
    
      private onRender(elem: HTMLElement): void {
        if (!this.elem) {
          this.elem = elem;
        }
    
        const element: React.ReactElement<IAsyncDropdownProps> = React.createElement(AsyncDropdown, {
          label: this.properties.label,
          loadOptions: this.properties.loadOptions,
          onChanged: this.onChanged.bind(this),
          selectedKey: this.properties.selectedKey,
          disabled: this.properties.disabled,
          // required to allow the component to be re-rendered by calling this.render() externally
          stateKey: new Date().toString()
        });
        ReactDom.render(element, elem);
      }
    
      private onChanged(option: IDropdownOption, index?: number): void {
        this.properties.onPropertyChange(this.targetProperty, option.key);
      }
    }
    

    A classe PropertyPaneAsyncDropdown implementa a interface padrão IPropertyPaneField da Estrutura do SharePoint usando a interface IPropertyPaneAsyncDropdownProps como um contrato para suas propriedades públicas que podem ser definidas de dentro da web part. A classe contém as três propriedades públicas a seguir definidas pela interface IPropertyPaneField:

    • type: deve ser definido como PropertyPaneFieldType.Custom para um controle de painel de propriedades personalizado.
    • targetProperty: usado para especificar o nome da propriedade da web part a ser usada com o controle.
    • properties: usado para definir as propriedades específicas do controle.

    Observe como a propriedade properties é do tipo IPropertyPaneAsyncDropdownInternalProps interno em vez da interface IPropertyPaneAsyncDropdownProps pública implementada pela classe. Isso é de propósito para que a propriedade properties possa definir o método onRender() exigido pela Estrutura do SharePoint. Se o método onRender() fosse parte da interface IPropertyPaneAsyncDropdownProps pública, ao usar o controle suspenso assíncrono na web part, você precisaria atribuir um valor a ele dentro da web part, o que não é desejável.

    A classe PropertyPaneAsyncDropdown define um método render() público, que pode ser usado para redesenhar o controle. Isso é útil em situações como quando você tem listas suspensas em cascatas, em que o valor definido em uma determina as opções disponíveis em outra. Ao chamar o método render() depois de selecionar um item, você pode fazer a lista suspensa dependente carregar as opções disponíveis. Para que isso funcione, você tem que fazer o React detectar que o controle mudou. Isso é feito definindo o valor do stateKey para a data atual. Usando esse truque, sempre que o método onRender() for chamado, o componente não apenas será renderizado novamente, mas também atualizará suas opções disponíveis. Não se esqueça de implementar o método onDispose() para desmontar o elemento sempre que você fechar o painel de propriedades.

Usar o controle do painel de propriedades de lista suspensa assíncrono na Web Part

Com o controle do painel de propriedades de lista suspensa assíncrono pronto, a próxima etapa é usá-lo na Web Part, permitindo que os usuários selecionem uma lista.

Adicionar a interface de informações da lista

Para transmitir informações sobre as listas disponíveis de forma consistente, defina uma interface que representará as informações sobre uma lista. Na pasta src/webparts/listItems, crie um novo arquivo denominado IListInfo.ts e digite o código a seguir:

export interface IListInfo {
  Id: string;
  Title: string;
}

Usar o controle do painel de propriedades de lista suspensa assíncrono para renderizar a propriedade da Web Part listName

  1. Tipos de referência obrigatórios. Na seção superior do arquivo src/webparts/listItems/ListItemsWebPart.ts, importe a classe PropertyPaneAsyncDropdown criada anteriormente adicionando:

    import { PropertyPaneAsyncDropdown } from '../../controls/PropertyPaneAsyncDropdown/PropertyPaneAsyncDropdown';
    
  2. Depois desse código, adicione uma referência à interface IDropdownOption e duas funções auxiliares necessárias para trabalhar com as propriedades da web part.

    import { IDropdownOption } from 'office-ui-fabric-react/lib/components/Dropdown';
    import { update, get } from '@microsoft/sp-lodash-subset';
    
  3. Adicionar um método para carregar listas disponíveis. Na classe ListItemsWebPart, adicione o método loadLists() a seguir para carregar as listas disponíveis. Neste artigo, 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.

    private loadLists(): Promise<IDropdownOption[]> {
      return new Promise<IDropdownOption[]>((resolve: (options: IDropdownOption[]) => void, reject: (error: any) => void) => {
        setTimeout(() => {
          resolve([{
            key: 'sharedDocuments',
            text: 'Shared Documents'
          },
            {
              key: 'myDocuments',
              text: 'My Documents'
            }]);
        }, 2000);
      });
    }
    
  4. Adicionar um método para lidar com a alteração do valor na lista suspensa. Na classe ListItemsWebPart, adicione um novo método denominado onListChange().

    private onListChange(propertyPath: string, newValue: any): void {
      const oldValue: any = get(this.properties, propertyPath);
      // store new value in web part properties
      update(this.properties, propertyPath, (): any => { return newValue; });
      // refresh web part
      this.render();
    }
    

    Depois de selecionar uma lista na lista suspensa, o valor selecionado deve ser persistido nas propriedades da Web Part, e a Web Part deve ser renderizada novamente para refletir a propriedade selecionada.

  5. Renderize a propriedade da Web Part da lista usando o controle do painel de propriedades de lista suspensa assíncrono. Na classe ListItemsWebPart, altere o método getPropertyPaneConfiguration() para usar o controle do painel de propriedades de lista suspensa assíncrono para renderizar a propriedade da web part listName.

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
    
      protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
        return {
          pages: [
            {
              header: {
                description: strings.PropertyPaneDescription
              },
              groups: [
                {
                  groupName: strings.BasicGroupName,
                  groupFields: [
                    new PropertyPaneAsyncDropdown('listName', {
                      label: strings.ListFieldLabel,
                      loadOptions: this.loadLists.bind(this),
                      onPropertyChange: this.onListChange.bind(this),
                      selectedKey: this.properties.listName
                    })
                  ]
                }
              ]
            }
          ]
        };
      }
    
      // ...
    }
    
  6. A essa altura, você poderá selecionar uma lista usando o controle do painel de propriedades de lista suspensa assíncrono recém-criado. Para verificar se o controle está funcionando conforme o esperado, abra o console e execute:

    gulp serve
    

    Controle do painel de propriedades de lista suspensa assíncrono carregando suas opções sem bloquear a interface de usuário da Web Part

    Selecionando uma das opções no controle do painel de propriedades de lista suspensa assíncrono

Implementar listas suspensas em cascata usando o controle do painel de propriedades de lista suspensa assíncrono

Ao criar as Web Parts da Estrutura do SharePoint, talvez você precise implementar uma configuração em que as opções disponíveis dependam de outra opção escolhida anteriormente. Um exemplo comum é permitir que os usuários escolham uma lista primeiro e, a partir dela, selecionem um item da lista. A lista de itens disponíveis dependeria da lista selecionada. Veja como implementar esse cenário usando o controle do painel de propriedades de lista suspensa assíncrono implementado em etapas anteriores.

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 item para que ela seja exibida da seguinte forma:

    {
      ...
      "preconfiguredEntries": [{
        ...
        "properties": {
          "description": "List items",
          "listName": "",
          "item": ""
        }
      }]
    }
    
  2. Altere o código na interface IListItemsWebPartProps no arquivo src/webparts/listItems/ListItemsWebPart.ts para:

    export interface IListItemsWebPartProps {
      description: string;
      listName: string;
      item: string;
    }
    
  3. Atualize o conteúdo do arquivo src/webparts/listItems/components/IListItemsProps.ts para adicionar a propriedade item:

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

    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.item,
          ...
        });
    
        ReactDom.render(element, this.domElement);
      }
    
      // ...
    }
    
  5. No arquivo src/webparts/listItems/loc/mystrings.d.ts, altere a interface IListItemsWebPartStrings para incluir a propriedade ItemFieldLabel:

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

    define([], function() {
      return {
        ...
        "PropertyPaneDescription": "Description",
        "BasicGroupName": "Group Name",
        "ListFieldLabel": "List",
        "ItemFieldLabel": "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(): React.ReactElement<IListItemsProps> {
    const {
      description,
      isDarkTheme,
      environmentMessage,
      hasTeamsContext,
      userDisplayName,
      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>
    );
  }
}

Adicionar método para carregar itens da lista

No arquivo src/webparts/listItems/ListItemsWebPart.ts, na classe ListItemsWebPart, adicione um novo método para carregar itens da lista disponíveis na lista selecionada. Como no método para carregar listas disponíveis, você usará dados de simulação. Dependendo da lista selecionada anteriormente, o método loadItems() retorna itens de lista fictícios. Quando nenhuma lista tiver sido selecionada, o método resolverá a promessa sem nenhum dado.

private loadItems(): Promise<IDropdownOption[]> {
  if (!this.properties.listName) {
    // resolve to empty options since no list has been selected
    return Promise.resolve([]);
  }

  const wp: ListItemsWebPart = this;

  return new Promise<IDropdownOption[]>((resolve: (options: IDropdownOption[]) => 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);
  });
}

Adicionar um método para lidar com a seleção de um item

Na classe ListItemsWebPart, adicione um novo método denominado onListItemChange(). Depois de selecionar um item na lista suspensa de itens, a Web Part deve armazenar o novo valor nas propriedades da Web Part e renderizar novamente a Web Part para refletir as alterações na interface do usuário.

private onListItemChange(propertyPath: string, newValue: any): void {
  const oldValue: any = get(this.properties, propertyPath);
  // store new value in web part properties
  update(this.properties, propertyPath, (): any => { return newValue; });
  // refresh web part
  this.render();
}

Renderizar a propriedade da Web Part do item no painel de propriedades

  1. Na classe ListItemsWebPart, adicione uma nova propriedade de classe denominada itemsDropdown:

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      private itemsDropDown: PropertyPaneAsyncDropdown;
      // ...
    }
    
  2. Altere o conteúdo do método getPropertyPaneConfiguration() para:

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
    
      protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
        // reference to item dropdown needed later after selecting a list
        this.itemsDropDown = new PropertyPaneAsyncDropdown('item', {
          label: strings.ItemFieldLabel,
          loadOptions: this.loadItems.bind(this),
          onPropertyChange: this.onListItemChange.bind(this),
          selectedKey: this.properties.item,
          // should be disabled if no list has been selected
          disabled: !this.properties.listName
        });
    
        return {
          pages: [
            {
              header: {
                description: strings.PropertyPaneDescription
              },
              groups: [
                {
                  groupName: strings.BasicGroupName,
                  groupFields: [
                    new PropertyPaneAsyncDropdown('listName', {
                      label: strings.ListFieldLabel,
                      loadOptions: this.loadLists.bind(this),
                      onPropertyChange: this.onListChange.bind(this),
                      selectedKey: this.properties.listName
                    }),
                    this.itemsDropDown
                  ]
                }
              ]
            }
          ]
        };
      }
      // ...
    }
    

    A lista suspensa para a propriedade do item é inicializada de forma similar à lista suspensa da propriedade listName. A única diferença é que depois de selecionar uma lista, a lista suspensa de itens precisa ser atualizada, uma instância do controle precisa ser atribuída à variável da classe.

Carregar itens da lista selecionada

Inicialmente, quando nenhuma lista está selecionada, a lista suspensa de itens é desabilitada e fica habilitada depois que o usuário seleciona uma lista. Depois de selecionar uma lista, a lista suspensa de itens também carregará itens de lista dessa lista.

  1. Para implementar essa lógica, estenda o método onListChange() definido anteriormente para:

    private onListChange(propertyPath: string, newValue: any): void {
      const oldValue: any = get(this.properties, propertyPath);
      // store new value in web part properties
      update(this.properties, propertyPath, (): any => { return newValue; });
      // reset selected item
      this.properties.item = undefined;
      // store new value in web part properties
      update(this.properties, 'item', (): any => { return this.properties.item; });
      // refresh web part
      this.render();
      // reset selected values in item dropdown
      this.itemsDropDown.properties.selectedKey = this.properties.item;
      // allow to load items
      this.itemsDropDown.properties.disabled = false;
      // load items and re-render items dropdown
      this.itemsDropDown.render();
    }
    

    Após selecionar uma lista, o item selecionado é redefinido, persistido nas propriedades da Web Part e redefinido na lista suspensa de itens. A lista suspensa para selecionar um item fica habilitada e a lista suspensa é atualizada para carregar suas opções.

  2. Para verificar se tudo está funcionando conforme o esperado, no console, execute:

    gulp serve
    

    Depois de adicionar a Web Part à página pela primeira vez e abrir o painel de propriedades, você verá ambas as listas suspensas desabilitadas e carregando suas opções.

    Listas suspensas no painel de propriedades da Web Part carregando seus dados

    Depois que as opções tiverem sido carregadas, a lista suspensa será habilitada. Como nenhuma lista foi selecionada ainda, a lista suspensa de itens permanece desabilitada.

    Lista suspensa no painel de propriedades da Web Part habilitada. Lista suspensa de itens desabilitada

    Após a seleção de uma lista na lista suspensa, a lista suspensa de itens carregará os itens disponíveis na lista.

    Menu suspenso de itens carregando itens disponíveis após a seleção de uma lista no menu suspenso de listas

    Depois que os itens disponíveis tiverem sido carregados, a lista suspensa de itens será habilitada.

    Selecionar um item da lista na lista suspensa de itens no painel de propriedades da Web Part

    Depois de selecionar um item na lista suspensa de itens, a Web Part será atualizada mostrando o item selecionado no corpo.

    Lista selecionada e item renderizado na Web Part

  3. Interrompa o servidor web local pressionando CTRL+C no console.

Confira também