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.
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
Comece criando uma nova pasta para o projeto:
md react-custompropertypanecontrol
Vá até a pasta do projeto:
cd react-custompropertypanecontrol
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):
- Que tipo de componente do cliente será criado? WebPart
- Qual é o nome da Web Part? Listar itens
- Qual modelo você deseja usar? React
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
.
No editor de código, abra o arquivo src/webparts/listItems/ListItemsWebPartManifest.json. Substitua a propriedade
description
padrão por uma nova propriedade chamadalistName
:{ ... "preconfiguredEntries": [{ ... "properties": { "listName": "" } }] }
Abra o arquivo src/webparts/listItems/ListItemsWebPart.ts e atualize a interface
IListItemsWebPartProps
para o seguinte:export interface IListItemsWebPartProps { description: string; 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, 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); } // ... }
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 }) ] } ] } ] }; } // ... }
No arquivo src/webparts/listItems/loc/mystrings.d.ts, adicione uma nova propriedade
ListFieldLabel
do tipostring
à interfaceIListItemsWebPartStrings
existente:declare interface IListItemsWebPartStrings { PropertyPaneDescription: string; BasicGroupName: string; .. ListFieldLabel: string; }
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" } });
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; }
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> ); } }
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.
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
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.
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.
- A propriedade
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.
- A propriedade
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 propriedadesdisabled
oustateKey
mudam e ele carrega as opções disponíveis chamando o métodoloadOptions()
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.
- Quando o componente é carregado pela primeira vez, o método
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
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 interfaceIPropertyPaneField<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 .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.
-
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.
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ãoIPropertyPaneField
da Estrutura do SharePoint usando a interfaceIPropertyPaneAsyncDropdownProps
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 interfaceIPropertyPaneField
:-
type
: deve ser definido comoPropertyPaneFieldType.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 tipoIPropertyPaneAsyncDropdownInternalProps
interno em vez da interfaceIPropertyPaneAsyncDropdownProps
pública implementada pela classe. Isso é de propósito para que a propriedadeproperties
possa definir o métodoonRender()
exigido pela Estrutura do SharePoint. Se o métodoonRender()
fosse parte da interfaceIPropertyPaneAsyncDropdownProps
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étodorender()
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étodorender()
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 dostateKey
para a data atual. Usando esse truque, sempre que o métodoonRender()
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étodoonDispose()
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
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';
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';
Adicionar um método para carregar listas disponíveis. Na classe
ListItemsWebPart
, adicione o métodoloadLists()
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); }); }
Adicionar um método para lidar com a alteração do valor na lista suspensa. Na classe
ListItemsWebPart
, adicione um novo método denominadoonListChange()
.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.
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étodogetPropertyPaneConfiguration()
para usar o controle do painel de propriedades de lista suspensa assíncrono para renderizar a propriedade da web partlistName
.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 }) ] } ] } ] }; } // ... }
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
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
No editor de código, abra o arquivo src/webparts/listItems/ListItemsWebPart.manifest.json. Para a seção
properties
, adicione uma nova propriedade denominadaitem
para que ela seja exibida da seguinte forma:{ ... "preconfiguredEntries": [{ ... "properties": { "description": "List items", "listName": "", "item": "" } }] }
Altere o código na interface IListItemsWebPartProps no arquivo src/webparts/listItems/ListItemsWebPart.ts para:
export interface IListItemsWebPartProps { description: string; listName: string; item: string; }
Atualize o conteúdo do arquivo src/webparts/listItems/components/IListItemsProps.ts para adicionar a propriedade
item
:export interface IListItemsProps { ... listName: string; itemName: string; }
No arquivo src/webparts/listItems/ListItemsWebPart.ts, altere o código do método
render()
para incluir a propriedadeitem
: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); } // ... }
No arquivo src/webparts/listItems/loc/mystrings.d.ts, altere a interface
IListItemsWebPartStrings
para incluir a propriedadeItemFieldLabel
:declare interface IListItemsWebPartStrings { ... PropertyPaneDescription: string; BasicGroupName: string; ListFieldLabel: string; ItemFieldLabel: string; }
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
Na classe
ListItemsWebPart
, adicione uma nova propriedade de classe denominadaitemsDropdown
:export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> { private itemsDropDown: PropertyPaneAsyncDropdown; // ... }
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.
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.
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.
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.
Após a seleção de uma lista na lista suspensa, a lista suspensa de itens carregará os itens disponíveis na lista.
Depois que os itens disponíveis tiverem sido carregados, a lista suspensa de itens será habilitada.
Depois de selecionar um item na lista suspensa de itens, a Web Part será atualizada mostrando o item selecionado no corpo.
Interrompa o servidor web local pressionando CTRL+C no console.