Compilar controles personalizados para el panel de propiedades
SharePoint Framework contiene un conjunto de controles estándares para el panel de propiedades. Aun así, a veces es necesaria funcionalidad adicional más allá de los controles básicos. Podría necesitar actualizaciones asincrónicas de los datos en un control o una interfaz de usuario específica. Compile un control personalizado para el panel de propiedades para obtener la funcionalidad que necesita.
En este artículo, compilará un control desplegable personalizado que carga sus datos de forma asincrónica desde un servicio externo sin bloquear la interfaz de usuario del elemento web.
El origen del elemento web de trabajo está disponible en GitHub en sp-dev-fx-webparts/samples/react-custompropertypanecontrols/.
Nota:
Antes de seguir los pasos de este artículo, asegúrese de configurar el entorno de desarrollo para crear soluciones de SharePoint Framework.
Crear un proyecto
Empiece por crear una carpeta para el proyecto:
md react-custompropertypanecontrol
Vaya a la carpeta del proyecto:
cd react-custompropertypanecontrol
En la carpeta del proyecto, ejecute el generador de Yeoman de SharePoint Framework para aplicar scaffolding a un nuevo proyecto de SharePoint Framework:
yo @microsoft/sharepoint
En el momento en que se le solicite, introduzca los siguientes valores (seleccione la opción predeterminada para todas las solicitudes que se omitan a continuación):
- ¿Cuál es el tipo de componente del lado cliente que se va a crear? WebPart
- ¿Cómo se llama su elemento web? Elementos de lista
- ¿Qué plantilla desea usar? React
Abra la carpeta del proyecto en el editor de código.
Definir la propiedad del elemento web para almacenar la lista seleccionada
El elemento web que está generando muestra los elementos de lista de la lista de SharePoint seleccionada. Los usuarios pueden seleccionar una lista en las propiedades del elemento web. Para almacenar la lista seleccionada, cree una propiedad de elemento web denominada listName
.
En el editor de código, abra el archivo src/webparts/listItems/ListItemsWebPartManifest.json. Reemplace la propiedad de
description
predeterminada por una nueva propiedad denominadalistName
:{ ... "preconfiguredEntries": [{ ... "properties": { "listName": "" } }] }
Abra el archivo src/webparts/listItems/ListItemsWebPart.ts y actualice la interfaz
IListItemsWebPartProps
a lo siguiente:export interface IListItemsWebPartProps { description: string; listName: string; }
En el archivo src/webparts/listItems/ListItemsWebPart.ts, cambie el método
render()
a:export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> { // ... public render(): void { const element: React.ReactElement<IListItemsProps> = React.createElement( ListItems, { listName: this.properties.listName, 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); } // ... }
Actualizar el método
getPropertyPaneConfiguration()
a:export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> { // ... protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { return { pages: [ { header: { description: strings.PropertyPaneDescription }, groups: [ { groupName: strings.BasicGroupName, groupFields: [ PropertyPaneTextField('listName', { label: strings.ListFieldLabel }) ] } ] } ] }; } // ... }
En el archivo src/webparts/listItems/loc/mystrings.d.ts, agregue una nueva propiedad
ListFieldLabel
de tipostring
a la interfazIListItemsWebPartStrings
existente:declare interface IListItemsWebPartStrings { PropertyPaneDescription: string; BasicGroupName: string; .. ListFieldLabel: string; }
En el archivo src/webparts/listItems/loc/en-us.js, agregue una nueva propiedad
ListFieldLabel
al objeto devuelto:define([], function() { return { "PropertyPaneDescription": "Description", "BasicGroupName": "Group Name", ... "ListFieldLabel": "List" } });
Abra el archivo src/webparts/listItems/components/IListItemsProps.ts y agregue la propiedad
listName
a la interfaz de lista:export interface IListItemsProps { description: string; isDarkTheme: boolean; environmentMessage: string; hasTeamsContext: boolean; userDisplayName: string; listName: string; }
En el archivo src/webparts/listItems/components/ListItems.tsx, cambie los contenidos del método
render()
a: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> ); } }
Ejecute el comando siguiente para comprobar que el proyecto se está ejecutando:
gulp serve
En el explorador web, agregue el elemento web Lista de elementos al lienzo y abra sus propiedades. Compruebe que el valor establecido para la propiedad List se muestra en el cuerpo del elemento web.
Crear un control de panel de propiedades desplegable asincrónico
SharePoint Framework ofrece un control desplegable estándar que permite a los usuarios seleccionar un valor específico. El control desplegable está construido de modo que requiere que se conozcan todos sus valores por adelantado. Si desea cargar los valores dinámicamente o está cargando valores de forma asincrónica desde un servicio externo y no quiere bloquear todo el elemento web, la creación de un control desplegable personalizado es una opción.
Al crear un control de panel de propiedades personalizado que utiliza React en SharePoint Framework, el control se compone de una clase que registra el control con el elemento web, y de un componente React que representa el desplegable y gestiona sus datos.
Agregar un componente React de control de panel de propiedades desplegable asincrónico
Cree la carpeta de componentes. En la carpeta src del proyecto, cree una jerarquía de tres nuevas carpetas de modo que la estructura de carpetas sea src/controls/PropertyPaneAsyncDropdown/components.
Defina las propiedades del componente React desplegable asincrónico. En la carpeta src/controls/PropertyPaneAsyncDropdown/components cree un archivo denominado IAsyncDropdownProps.ts y escriba el siguiente 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; }
La clase
IAsyncDropdownProps
define propiedades que pueden establecerse en el componente React usado por el control de panel de propiedades personalizado:- La propiedad
label
especifica la etiqueta del control desplegable. - El control llama a la función asociada al delegado
loadOptions
para cargar las opciones disponibles. - Cuando el usuario selecciona una opción del desplegable, se llama a la función asociada al delegado
onChanged
. - La propiedad
selectedKey
especifica el valor seleccionado, que puede ser una cadena o un número. - La propiedad
disabled
especifica si el control desplegable está deshabilitado o no. - La propiedad
stateKey
se usa para forzar el componente React que se va a volver a representar.
- La propiedad
Defina la interfaz del componente React desplegable asincrónico. En la carpeta src/controls/PropertyPaneAsyncDropdown/components cree un archivo denominado IAsyncDropdownState.ts y escriba el código siguiente:
import { IDropdownOption } from 'office-ui-fabric-react/lib/components/Dropdown'; export interface IAsyncDropdownState { loading: boolean; options: IDropdownOption[]; error: string; }
La interfaz
IAsyncDropdownState
describe el estado del componente React:- La propiedad
loading
determina si el componente está cargando sus opciones en el momento dado. - La propiedad
options
contiene todas las opciones disponibles. - Si se ha producido un error, se asigna a la propiedad
error
, desde donde se comunica al usuario.
- La propiedad
Defina el componente React desplegable asincrónico. En la carpeta src/controls/PropertyPaneAsyncDropdown/components, cree un archivo denominado AsyncDropdown.tsx y escriba el siguiente 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); } } }
La clase
AsyncDropdown
representa el componente React usado para representar el control de panel de propiedades desplegable asincrónico:- Cuando el componente se carga por primera vez, el método
componentDidMount()
o sus propiedadesdisabled
ostateKey
cambian, y cargan las opciones disponibles mediante una llamada al métodoloadOptions()
pasado a través de las propiedades. - Una vez que se cargan las opciones, el componente actualiza su estado y muestra las opciones disponibles.
- El desplegable se representa mediante el componente desplegable de Office UI Fabric React.
- Cuando el componente está cargando las opciones disponibles, se muestra un indicador giratorio mediante el componente de indicador giratorio de Office UI Fabric React.
- Cuando el componente se carga por primera vez, el método
El siguiente paso es definir el control de panel de propiedades personalizado. Este control se usa dentro del elemento web al definir propiedades en el panel de propiedades y se representa mediante el componente React previamente definido.
Agregar un control de panel de propiedades desplegable asincrónico
Defina las propiedades del control de panel de propiedades desplegable asincrónico. Un control de panel de propiedades personalizado tiene dos conjuntos de propiedades.
El primer conjunto de propiedades se expone públicamente y se utiliza para definir la propiedad del elemento web dentro del elemento web. Estas propiedades son propiedades específicas de los componentes, como la etiqueta que se muestra al lado del control, los valores mínimos y máximos de un indicador giratorio, o las opciones disponibles para un desplegable. Cuando se define un control de panel de propiedades personalizado, se debe pasar el tipo que describe estas propiedades como el tipo
TProperties
al implementar la interfazIPropertyPaneField<TProperties>
.El segundo conjunto de propiedades son propiedades privadas que se utilizan internamente dentro del control de panel de propiedades personalizado. Estas propiedades deben adherirse a las API de SharePoint Framework para que el control personalizado se represente correctamente. Estas propiedades deben implementar la
IPropertyPaneCustomFieldProps
interfaz desde el paquete @microsoft/sp-property-pane .Defina las propiedades públicas del control de panel de propiedades desplegable asincrónico. En la carpeta src/controls/PropertyPaneAsyncDropdown, cree un archivo denominado IPropertyPaneAsyncDropdownProps.ts y escriba el siguiente 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; }
En la interfaz
IPropertyPaneAsyncDropdownProps
:-
label
: define la etiqueta que aparece junto al desplegable. -
loadOptions
: define el método al que se llama para cargar las opciones del desplegable disponibles. -
onPropertyChange
: define un método al que se llama cuando el usuario selecciona un valor en la lista desplegable. -
selectedKey
: devuelve el valor del desplegable seleccionado. -
disabled
: especifica si el control está deshabilitado o no.
-
Defina las propiedades internas del control de panel de propiedades desplegable asincrónico. En la carpeta src/controls/PropertyPaneAsyncDropdown, cree un archivo denominado IPropertyPaneAsyncDropdownInternalProps.ts y escriba el siguiente código:
import { IPropertyPaneCustomFieldProps } from '@microsoft/sp-property-pane'; import { IPropertyPaneAsyncDropdownProps } from './IPropertyPaneAsyncDropdownProps'; export interface IPropertyPaneAsyncDropdownInternalProps extends IPropertyPaneAsyncDropdownProps, IPropertyPaneCustomFieldProps { }
Aunque la interfaz IPropertyPaneAsyncDropdownInternalProps
no define propiedades nuevas, combina las propiedades de la interfaz IPropertyPaneAsyncDropdownProps
definida anteriormente y de la interfaz estándar IPropertyPaneCustomFieldProps
de SharePoint Framework que es necesaria para que un control personalizado se ejecute correctamente.
Defina el control de panel de propiedades desplegable asincrónico. En la carpeta src/controls/PropertyPaneAsyncDropdown, cree un archivo denominado PropertyPaneAsyncDropdown.ts y escriba el siguiente 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); } }
La clase
PropertyPaneAsyncDropdown
implementa la interfaz estándarIPropertyPaneField
de SharePoint Framework mediante la interfazIPropertyPaneAsyncDropdownProps
como un contrato para sus propiedades públicas que se puede establecer desde el elemento web. La clase contiene las tres propiedades públicas siguientes definidas por la interfazIPropertyPaneField
:-
type
: debe establecerse enPropertyPaneFieldType.Custom
para un control de panel de propiedades personalizado. -
targetProperty
: se utiliza para especificar el nombre de la propiedad del elemento web que se utilizará con el control. -
properties
: se utiliza para definir propiedades específicas del control.
Observe cómo la propiedad
properties
es del tipo deIPropertyPaneAsyncDropdownInternalProps
interno en lugar de la interfaz deIPropertyPaneAsyncDropdownProps
pública implementada por la clase. Esto se hace de forma intencionada para que la propiedadproperties
pueda definir el métodoonRender()
requerido por SharePoint Framework. Si el métodoonRender()
formaba parte de la interfaz públicaIPropertyPaneAsyncDropdownProps
, cuando use el control desplegable asincrónico en el elemento web, tendrá que asignarle un valor en el elemento web, lo cual no es deseable.La clase
PropertyPaneAsyncDropdown
define un método públicorender()
que se puede usar para volver a dibujar el control. Esto es útil en situaciones tales como cuando haya desplegables en cascada donde el valor establecido en uno determina las opciones disponibles en otro. Si llama al métodorender()
después de seleccionar un elemento, puede hacer que el desplegable dependiente cargue las opciones disponibles. Para que esto funcione, tiene que hacer que React detecte que el control ha cambiado. Esto se consigue mediante el establecimiento del valor destateKey
en la fecha actual. Gracias a este truco, cada vez que se llame al métodoonRender()
el componente no solo se vuelve a representar, sino que también actualiza las opciones disponibles. No olvide implementar el métodoonDispose()
para desmontar el elemento cada vez cierre el panel de propiedades.-
Usar el control de panel de propiedades desplegable asincrónico en el elemento web
Con el control de panel de propiedades desplegable asincrónico listo, el siguiente paso es usarlo en el elemento web para permitir a los usuarios seleccionar una lista.
Agregar la interfaz de información de listas
Para pasar información sobre las listas disponibles de una manera coherente, defina una interfaz que represente la información sobre una lista. En la carpeta src/webparts/listItems, cree un archivo denominado IListInfo.ts y escriba el siguiente código:
export interface IListInfo {
Id: string;
Title: string;
}
Usar el control de panel de propiedades desplegable asincrónico para representar la propiedad de elemento web listName
Haga referencia a los tipos necesarios. En la sección superior del archivo src/webparts/listItems/ListItemsWebPart.ts, importe la clase
PropertyPaneAsyncDropdown
creada anteriormente. Para ello, agregue lo siguiente:import { PropertyPaneAsyncDropdown } from '../../controls/PropertyPaneAsyncDropdown/PropertyPaneAsyncDropdown';
Después de ese código, agregue una referencia a la interfaz
IDropdownOption
y dos funciones auxiliares necesarias para trabajar con propiedades de elementos web.import { IDropdownOption } from 'office-ui-fabric-react/lib/components/Dropdown'; import { update, get } from '@microsoft/sp-lodash-subset';
Agregue un método para cargar las listas disponibles. En la clase
ListItemsWebPart
, agregue el siguiente métodoloadLists()
para cargar las listas disponibles. En este artículo, se usan datos simulados, pero también podría llamar a la API de REST de SharePoint para recuperar la lista de listas disponibles en la web actual. Para simular las opciones de carga desde un servicio externo, el método usa un retraso de dos segundosprivate 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); }); }
Agregue un método para controlar el cambio del valor del desplegable. En la clase
ListItemsWebPart
, agregue un nuevo 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(); }
Después de seleccionar una lista en el desplegable, debe conservarse el valor seleccionado en las propiedades del elemento web y este debería representarse de nuevo para reflejar la propiedad seleccionada.
Represente la propiedad de elemento web de lista mediante el control de panel de propiedades desplegable asincrónico. En la clase
ListItemsWebPart
, cambie el métodogetPropertyPaneConfiguration()
para usar el control de panel de propiedades desplegable asincrónico para representar la propiedad de elemento weblistName
.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 }) ] } ] } ] }; } // ... }
En este momento, puede seleccionar una lista mediante el control de panel de propiedades desplegable asincrónico recién creado. Para comprobar que el control funciona como se esperaba, abra la consola y ejecute:
gulp serve
Implementar desplegables en cascada mediante el control de panel de propiedades desplegable asincrónico
Al compilar elementos web de SharePoint Framework, es posible que necesite implementar una configuración en la que las opciones disponibles dependan de otra opción elegida anteriormente. Un ejemplo común es comenzar dejando que los usuarios elijan una lista y, en esa lista, seleccionar un elemento de lista. La lista de elementos disponibles dependerá de la lista seleccionada. Aquí puede ver cómo implementar un escenario como este mediante el control de panel de propiedades desplegable asincrónico implementado en pasos anteriores.
Agregar propiedad de elemento web del elemento
En el editor de código, abra el archivo src/webparts/listItems/ListItemsWebPart.manifest.json. Agregue una nueva propiedad a la sección
properties
denominadaitem
similar a la siguiente:{ ... "preconfiguredEntries": [{ ... "properties": { "description": "List items", "listName": "", "item": "" } }] }
Cambie el código de la interfaz IListItemsWebPartProps en el archivo src/webparts/listItems/ListItemsWebPart.ts a:
export interface IListItemsWebPartProps { description: string; listName: string; item: string; }
Actualice el contenido del archivo src/webparts/listItems/components/IListItemsProps.ts para agregar la propiedad
item
:export interface IListItemsProps { ... listName: string; itemName: string; }
En el archivo src/webparts/listItems/ListItemsWebPart.ts, cambie el código del método
render()
para incluir la propiedaditem
: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); } // ... }
En el archivo src/webparts/listItems/loc/mystrings.d.ts cambie la interfaz
IListItemsWebPartStrings
para incluir la propiedadItemFieldLabel
:declare interface IListItemsWebPartStrings { ... PropertyPaneDescription: string; BasicGroupName: string; ListFieldLabel: string; ItemFieldLabel: string; }
En el archivo src/webparts/listItems/loc/en-us.js agregue la definición que falta para la cadena
ItemFieldLabel
:define([], function() { return { ... "PropertyPaneDescription": "Description", "BasicGroupName": "Group Name", "ListFieldLabel": "List", "ItemFieldLabel": "Item" } });
Representar el valor de la propiedad de elemento web del elemento
En el archivo src/webparts/listItems/components/ListItems.tsx, cambie el método render()
a:
export default class ListItems extends React.Component<IListItemsProps, {}> {
public render(): 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>
);
}
}
Agregar un método para cargar elementos de lista
En el archivo src/webparts/listItems/ListItemsWebPart.ts, en la clase ListItemsWebPart
, agregue un nuevo método para cargar los elementos de lista disponibles de la lista seleccionada. Al igual que el método para cargar listas disponibles, usará datos simulados. En función de la lista seleccionada anteriormente, el método loadItems()
devuelve elementos de lista ficticia. Cuando no se haya seleccionado ninguna lista, el método resuelve la premisa sin ningún dato.
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);
});
}
Agregar un método para controlar la selección de un elemento
En la clase ListItemsWebPart
, agregue un nuevo método denominado onListItemChange()
. Después de seleccionar un elemento en el desplegable de elementos, el elemento web debería almacenar el nuevo valor en las propiedades del elemento web y volver a representar el elemento web para reflejar los cambios en la interfaz de usuario.
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();
}
Representar la propiedad de elemento web del elemento en el panel de propiedades
En la clase
ListItemsWebPart
, agregue una nueva propiedad de clase llamadaitemsDropdown
:export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> { private itemsDropDown: PropertyPaneAsyncDropdown; // ... }
Cambie el código del método
getPropertyPaneConfiguration()
por: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 ] } ] } ] }; } // ... }
La lista desplegable de la propiedad item se inicializa de forma similar al desplegable de la propiedad listName. La única diferencia es que, dado que después de seleccionar una lista el desplegable de elementos tiene que actualizarse, se tiene que asignar una instancia del control a la variable de clase.
Cargar elementos de la lista seleccionada
Inicialmente, cuando no se selecciona ninguna lista, el desplegable de elementos se deshabilita y se habilita después de que el usuario seleccione una lista. Después de seleccionar una lista, el desplegable de elementos también carga elementos de la lista de ella.
Para implementar esta lógica, extienda el método
onListChange()
definido anteriormente a: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(); }
Después de seleccionar una lista, el elemento seleccionado se restablece, se conserva en propiedades de elementos web y se restablece en el desplegable de elementos. La lista desplegable para seleccionar un elemento se habilita y la lista desplegable se actualiza para cargar sus opciones.
Para comprobar que todo funciona como se esperaba, en la consola ejecute:
gulp serve
Después de agregar el elemento web a la página por primera vez y abrir su panel de propiedades, debería ver los desplegables deshabilitados y cargando sus opciones.
Una vez que se hayan cargado las opciones, se habilita el cuadro de lista desplegable. Como aún no ha seleccionado ninguna lista, el desplegable de elementos permanece deshabilitado.
Después de seleccionar una lista en el desplegable de lista, el desplegable de elementos cargará elementos disponibles en ella.
Una vez que se hayan cargado los elementos disponibles, se habilita el desplegable de elementos.
Después de seleccionar un elemento en el desplegable de elementos, el elemento web se actualiza mostrando el elemento seleccionado en su cuerpo.
Presione CTRL+C en la consola para detener el servidor web local.