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.
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 deste artigo, configure seu ambiente de desenvolvimento de Web Parts do lado do cliente do SharePoint.
Criar novo projeto
Comece criando uma nova pasta para o projeto:
md react-cascadingdropdowns
Vá até a pasta do projeto:
cd react-cascadingdropdowns
Na pasta do projeto, execute o gerador Yeoman da Estrutura do SharePoint para estruturar um novo projeto da Estrutura do SharePoint:
yo @microsoft/sharepoint
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
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
.
No editor de código, abra o arquivo src/webparts/listItems/ListItemsWebPartManifest.json. Substituir a propriedade padrão
description
por uma nova propriedade denominadalistName
.{ ... "preconfiguredEntries": [{ ... "properties": { "listName": "" } }] }
Abra o arquivo src/webparts/listItems/ListItemsWebPart.ts e substitua a interface
IListItemsWebPartProps
pelo seguinte:export interface IListItemsWebPartProps { listName: string; }
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); } // ... }
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 }) ] } ] } ] }; } // ... }
No arquivo src/webparts/listItems/loc/mystrings.d.ts, altere a interface
IListItemsStrings
para:declare interface IListItemsStrings { PropertyPaneDescription: string; BasicGroupName: string; ListNameFieldLabel: string; }
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" } });
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> ); }
No arquivo src/webparts/listItems/components/IListItemsProps.ts, altere a interface
IListItemsProps
para:export interface IListItemsProps { listName: string; }
Execute o seguinte comando para verificar se o projeto está em execução:
gulp serve
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.
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
Na classe
ListItemsWebPart
, adicione uma referência à classePropertyPaneDropdown
na seção superior da Web Part. Substitua a cláusula import que carrega a classePropertyPaneTextField
por:import { IPropertyPaneConfiguration, PropertyPaneTextField, PropertyPaneDropdown, IPropertyPaneDropdownOption } from '@microsoft/sp-property-pane';
Na classe
ListItemsWebPart
, adicione uma nova variável denominadalists
para armazenar informações sobre todas as listas disponíveis no site atual:export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> { private lists: IPropertyPaneDropdownOption[]; // ... }
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; // ... }
Altere o método
getPropertyPaneConfiguration()
para usar o controle suspenso para renderizar a propriedadelistName
: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 }) ] } ] } ] }; } }
Execute o seguinte comando para verificar se está funcionando conforme o esperado:
gulp serve
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.
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); }); } }
Carregue informações sobre listas disponíveis no menu suspenso de lista. Na classe
ListItemsWebPart
, substitua o métodoonPropertyPaneConfigurationStart()
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.
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étodorender()
é chamado para forçar a Web Part a realizar a renderização novamente.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.
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.
Adicionar a propriedade do item da Web Part
No editor de código, abra o arquivo src/webparts/listItems/ListItemsWebPart.manifest.json. Para a seção
properties
, adicione uma nova propriedade denominadaitemName
para que ela seja exibida da seguinte forma:{ // ... "properties": { "listName": "", "itemName": "" } // ... }
Altere o código no arquivo src/webparts/listItems/IListItemsWebPartProps.ts para:
export interface IListItemsWebPartProps { listName: string; itemName: string; }
Altere o código no arquivo src/webparts/listItems/components/IListItemsProps.ts para:
export interface IListItemsProps { listName: string; itemName: string; }
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); } // ... }
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; }
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.
Na classe
ListItemsWebPart
, adicione uma nova variável denominadaitems
, 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[]; // ... }
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; // ... }
Altere o método
getPropertyPaneConfiguration()
para usar o controle suspenso para renderizar a propriedadeitemName
.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 }) ] } ] } ] }; } }
Execute o seguinte comando para verificar se está funcionando conforme o esperado:
gulp serve
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.
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.Carregue informações sobre itens disponíveis no menu suspenso de item. Na classe
ListItemsWebPart
, estenda o métodoonPropertyPaneConfigurationStart()
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.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.
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étodoonPropertyPaneFieldChanged()
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.