Поделиться через


Создание пользовательских элементов управления для области свойств

SharePoint Framework содержит набор стандартных элементов управления для области свойств, но иногда нужны дополнительные функции. Вам может понадобиться асинхронное обновление данных в элементе управления или определенный пользовательский интерфейс. Создайте пользовательский элемент управления для области свойств, чтобы получить необходимые функции.

В этой статье вы создадите пользовательское раскрывающееся меню, которое асинхронно загружает данные из внешней службы, не блокируя пользовательский интерфейс веб-части.

Раскрывающееся меню элементов загружает доступные элементы после выбора списка в раскрывающемся меню списков

Исходный код рабочей веб-части доступен в репозитории sp-dev-fx-webparts/samples/react-custompropertypanecontrols/ на сайте GitHub.

Примечание.

Прежде чем выполнять действия, описанные в этой статье, настройте среду разработки для создания решений на платформе SharePoint Framework.

Создание проекта

  1. Для начала создайте папку проекта:

    md react-custompropertypanecontrol
    
  2. Перейдите в папку проекта:

    cd react-custompropertypanecontrol
    
  3. В папке проекта запустите генератор Yeoman для SharePoint Framework, чтобы сформировать шаблон проекта на платформе SharePoint Framework:

    yo @microsoft/sharepoint
    
  4. При появлении запроса введите следующие значения (выберите вариант по умолчанию для всех запросов, не перечисленных ниже).

    • Какой тип клиентского компонента нужно создать? WebPart
    • Как называется ваша веб-часть? Элементы списка
    • Какой шаблон следует использовать? React
  5. Откройте папку проекта в редакторе кода.

Определение свойства веб-части для хранения выбранного списка

В веб-части, которую вы создаете, появятся элементы из выбранного списка SharePoint. Пользователи могут выбрать список в свойствах веб-части. Для хранения выбранного списка создайте свойство веб-части с именем listName.

  1. В редакторе кода откройте файл src/webparts/listItems/ListItemsWebPartManifest.json. Замените свойство по умолчанию description на новое свойство listName:

    {
      ...
      "preconfiguredEntries": [{
        ...
        "properties": {
          "listName": ""
        }
      }]
    }
    
  2. Откройте файл src/webparts/listItems/ListItemsWebPart.ts и обновите интерфейс IListItemsWebPartProps следующим образом.

    export interface IListItemsWebPartProps {
      description: string;
      listName: string;
    }
    
  3. В файле src/webparts/listItems/ListItemsWebPart.ts замените метод render() на следующий:

    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. Изменение метода getPropertyPaneConfiguration() на следующий:

    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. В файле src/webparts/listItems/loc/mystrings.d.ts добавьте новое свойство ListFieldLabel типа string в существующий интерфейс IListItemsWebPartStrings:

    declare interface IListItemsWebPartStrings {
      PropertyPaneDescription: string;
      BasicGroupName: string;
      ..
      ListFieldLabel: string;
    }
    
  6. В файле src/webparts/listItems/loc/en-us.js добавьте новое свойство ListFieldLabel в возвращенный объект:

    define([], function() {
      return {
        "PropertyPaneDescription": "Description",
        "BasicGroupName": "Group Name",
        ...
        "ListFieldLabel": "List"
      }
    });
    
  7. Откройте файл src/webparts/listItems/components/IListItemsProps.ts и добавьте свойство listName в интерфейс списка:

    export interface IListItemsProps {
      description: string;
      isDarkTheme: boolean;
      environmentMessage: string;
      hasTeamsContext: boolean;
      userDisplayName: string;
      listName: string;
    }
    
  8. В файле src/webparts/listItems/components/ListItems.tsx замените содержимое метода render() следующим:

    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. Чтобы убедиться, что проект работает, выполните следующую команду:

    gulp serve
    
  10. В веб-браузере добавьте веб-часть List items на полотно и откройте ее свойства. Убедитесь, что значение свойства List отображается в теле веб-части.

    Веб-часть, в которой отображается значение свойства listName

Создание асинхронного раскрывающегося меню области свойств

В SharePoint Framework представлено стандартное раскрывающееся меню, которое позволяет пользователям выбирать определенные значения. Все его значения должны быть известны заранее. Если вы хотите загрузить значения динамически или асинхронно загружаете значения из внешней службы и не хотите блокировать всю веб-часть, можно создать пользовательский раскрывающийся список.

Оно состоит из класса, который регистрирует элемент управления в веб-части, и компонента React в SharePoint Framework, который отрисовывает раскрывающееся меню и управляет его данными.

Добавление компонента React асинхронного раскрывающегося меню области свойств

  1. Создайте папку компонентов. В папке проекта src создайте иерархию из трех новых папок, чтобы получилась следующая структура: src/controls/PropertyPaneAsyncDropdown/components.

    Папка компонентов, выделенная в Visual Studio Code

  2. Определите свойства компонента React асинхронного раскрывающегося меню. В папке src/controls/PropertyPaneAsyncDropdown/components создайте файл IAsyncDropdownProps.ts и введите следующий код:

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

    Класс IAsyncDropdownProps определяет свойства, которые можно настроить в компоненте React, используемом пользовательским элементом управления области свойств.

    • Свойство label указывает подпись для раскрывающегося меню.
    • Для загрузки доступных параметров элемент управления вызывает функцию, связанную с делегатом loadOptions.
    • После выбора параметра в раскрывающемся меню вызывается функция, связанная с делегатом onChanged.
    • Свойство selectedKey указывает выбранное значение, которое может быть строкой или числом.
    • Свойство disabled указывает, отключено ли раскрывающееся меню.
    • Свойство stateKey используется для принудительной повторной отрисовки компонента React.
  3. Определите интерфейс компонента React для асинхронного раскрывающегося меню. В папке src/controls/PropertyPaneAsyncDropdown/components создайте файл IAsyncDropdownState.ts и введите следующий код:

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

    Интерфейс IAsyncDropdownState описывает состояние компонента React.

    • Свойство loading определяет, загружает ли компонент параметры в данный момент.
    • Свойство options содержит все доступные параметры.
    • Если произойдет ошибка, она будет назначена свойству error, из которого она будет передана пользователю.
  4. Определите компонент React асинхронного раскрывающегося меню. В папке src/controls/PropertyPaneAsyncDropdown/components создайте файл AsyncDropdown.tsx и введите следующий код:

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

    Класс AsyncDropdown представляет компонент React, используемый для отрисовки асинхронного раскрывающегося меню области свойств.

    • При первой загрузке компонента изменится метод componentDidMount() или свойства disabled или stateKey, и он загрузит доступные параметры, вызвав метод loadOptions(), переданный через свойства.
    • После загрузки параметров компонент показывает доступные параметры.
    • Раскрывающееся меню отрисовывается с помощью компонента раскрывающегося меню на базе Office UI Fabric React.
    • Когда компонент загружает доступные параметры, он показывает вертушку, используя компонент вертушки на базе Office UI Fabric React.

Далее необходимо определить пользовательский элемент управления области свойств. Этот элемент управления используется в веб-части при определении свойств в области свойств и отрисовывается с помощью заданного компонента React.

Как добавить асинхронное раскрывающееся меню области свойств

  1. Определите свойства асинхронного раскрывающегося меню области свойств. У пользовательского элемента управления области свойств есть два набора свойств.

    Первый из них доступен для всех и используется для определения свойства веб-части. К этим свойствам компонента относятся подпись, которая отображается рядом с элементом управления, минимальное и максимальное значения вертушки и доступные параметры раскрывающегося меню. Если добавляете пользовательский элемент управления области свойств, тип, описывающий эти свойства, необходимо передавать как тип TProperties при реализации интерфейса IPropertyPaneField<TProperties>.

    Второй набор состоит из личных свойств, которые используются в пользовательском элементе управления области свойств. Для правильной отрисовки пользовательского элемента управления эти свойства должны соответствовать требованиям API SharePoint Framework. Эти свойства должны реализовывать IPropertyPaneCustomFieldProps интерфейс из пакета @microsoft/sp-property-pane .

  2. Определите общедоступные свойства для асинхронного раскрывающегося меню области свойств. В папке src/controls/PropertyPaneAsyncDropdown создайте файл IPropertyPaneAsyncDropdownProps.ts и введите следующий код:

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

    В интерфейсе IPropertyPaneAsyncDropdownProps:

    • label: определяет подпись, которая отображается рядом с раскрывающимся меню.
    • loadOptions: определяет метод, который вызывается для загрузки доступных параметров раскрывающегося меню.
    • onPropertyChange: определяет метод, который вызывается, когда пользователь выбирает значение в раскрывающемся меню.
    • selectedKey: возвращает выбранное значение раскрывающегося меню.
    • disabled: указывает, отключен ли элемент управления.
  3. Определите внутренние свойства для асинхронного раскрывающегося меню области свойств. В папке src/controls/PropertyPaneAsyncDropdown создайте файл IPropertyPaneAsyncDropdownInternalProps.ts и введите следующий код:

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

Интерфейс IPropertyPaneAsyncDropdownInternalProps не определяет новые свойства, но он объединяет свойства из заданного интерфейса IPropertyPaneAsyncDropdownProps и стандартного интерфейса SharePoint Framework IPropertyPaneCustomFieldProps, что необходимо для правильной работы пользовательского элемента управления.

  1. Определите асинхронное раскрывающееся меню области свойств. В папке src/controls/PropertyPaneAsyncDropdown создайте файл PropertyPaneAsyncDropdown.ts и введите следующий код:

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

    Класс PropertyPaneAsyncDropdown реализует стандартный интерфейс SharePoint Framework IPropertyPaneField, используя интерфейс IPropertyPaneAsyncDropdownProps как контракт для общедоступных свойств, которые можно настроить в веб-части. Класс содержит следующие три общедоступные свойства, определяемые интерфейсом IPropertyPaneField:

    • type: необходимо установить значение PropertyPaneFieldType.Custom для настраиваемого элемента управления области свойств.
    • targetProperty: позволяет указать имя свойства веб-части, которое необходимо использовать с элементом управления.
    • properties: позволяет определить свойства элемента управления.

    Обратите внимание на то, что свойство properties относится к внутреннему типу IPropertyPaneAsyncDropdownInternalProps, а не к общедоступному интерфейсу IPropertyPaneAsyncDropdownProps, реализованному классом. Это сделано для того, чтобы свойство properties могло определять метод onRender(), необходимый для SharePoint Framework. Если бы метод onRender() был частью общедоступного интерфейса IPropertyPaneAsyncDropdownProps, при использовании асинхронного раскрывающегося меню в веб-части вам потребовалось бы назначить ему значение в веб-части, что нежелательно.

    Класс PropertyPaneAsyncDropdown определяет общедоступный метод render(), который можно использовать для обновления элемента управления. Это удобно при наличии каскадных раскрывающихся меню, когда значение, выбранное в одном, определяет параметры, доступные в другом. Вызов метода render() после выбора элемента позволяет зависимому раскрывающемуся меню загрузить доступные параметры. Чтобы это работало, компонент React должен обнаружить, что элемент управления изменен. Для этого установите текущую дату для свойства stateKey. В результате при каждом вызове метода onRender() компонент будет не только заново отрисовываться, но и обновлять доступные параметры. Не забудьте реализовать метод onDispose(), чтобы отключать элемент при каждом закрытии области свойств.

Как использовать асинхронное раскрывающееся меню области свойств в веб-части

Теперь, когда асинхронное раскрывающееся меню области свойств готово, необходимо использовать его в веб-части, чтобы пользователи могли выбрать список.

Добавление интерфейса сведений о списке

Чтобы передавать сведения о доступных списках согласованно, определите интерфейс, который будет представлять сведения о списке. В папке src/webparts/listItems создайте файл IListInfo.ts и введите следующий код:

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

Как использовать асинхронное раскрывающееся меню области свойств для отрисовки свойства веб-части listName

  1. Добавьте ссылку на обязательные типы. В верхней части файла src/webparts/listItems/ListItemsWebPart.ts импортируйте созданный класс PropertyPaneAsyncDropdown, добавив следующее:

    import { PropertyPaneAsyncDropdown } from '../../controls/PropertyPaneAsyncDropdown/PropertyPaneAsyncDropdown';
    
  2. После этого кода добавьте ссылку на интерфейс IDropdownOption и две вспомогательные функции, необходимые для работы со свойствами веб-части.

    import { IDropdownOption } from 'office-ui-fabric-react/lib/components/Dropdown';
    import { update, get } from '@microsoft/sp-lodash-subset';
    
  3. Добавьте метод для загрузки доступных списков. В классе ListItemsWebPart добавьте следующий метод loadLists() для загрузки доступных списков. В этой статье используются фиктивные данные, но вы также можете вызвать REST API для SharePoint, чтобы извлечь список доступных списков из текущего веб-сайта. Для имитации загрузки параметров из внешней службы метод использует двухсекундную задержку.

    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. Добавьте метод для обработки изменения значения в раскрывающемся меню. В классе ListItemsWebPart добавьте новый метод с именем 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();
    }
    

    После выбора списка в раскрывающемся меню следует сохранять выбранное значение в свойствах веб-части, а веб-часть — отрисовывать повторно для отображения выбранного свойства.

  5. Выполните отрисовку свойства веб-части списка с помощью асинхронного раскрывающегося меню области свойств. В классе ListItemsWebPart измените метод getPropertyPaneConfiguration(), чтобы использовать асинхронное раскрывающееся меню области свойств для отрисовки свойства веб-части 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. На этом этапе вы можете выбрать список, используя новое асинхронное раскрывающееся меню области свойств. Чтобы убедиться, что элемент управления работает должным образом, откройте консоль и выполните следующую команду:

    gulp serve
    

    Асинхронное раскрывающееся меню загружает параметры, не блокируя пользовательский интерфейс веб-части

    Выбор одного из параметров в асинхронном раскрывающемся меню области свойств

Реализация каскадных раскрывающихся списков с помощью раскрывающегося меню области свойств

При создании веб-частей SharePoint Framework может понадобиться реализовать конфигурацию, при которой доступные параметры зависят от другого параметра, выбранного ранее. Например, сделать так, чтобы сначала пользователи выбирали список, а затем — элемент из этого списка. Список доступных элементов будет зависеть от выбранного списка. Ниже описано, как реализовать такой сценарий, используя асинхронное раскрывающееся меню области свойств, реализованное на предыдущих шагах.

Добавление свойства веб-части item

  1. В редакторе кода откройте файл src/webparts/listItems/ListItemsWebPart.manifest.json. В разделе properties добавьте свойство item, чтобы оно выглядело следующим образом:

    {
      ...
      "preconfiguredEntries": [{
        ...
        "properties": {
          "description": "List items",
          "listName": "",
          "item": ""
        }
      }]
    }
    
  2. Измените код в интерфейсе IListItemsWebPartProps в файле src/webparts/listItemsWebPart.ts:

    export interface IListItemsWebPartProps {
      description: string;
      listName: string;
      item: string;
    }
    
  3. Обновите содержимое файла src/webparts/listItems/components/IListItemsProps.ts, чтобы добавить свойство item:

    export interface IListItemsProps {
      ...
      listName: string;
      itemName: string;
    }
    
  4. В файле src/webparts/listItems/ListItemsWebPart.ts измените код метода render(), чтобы включить свойство 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. В файле src/webparts/listItems/loc/mystrings.d.ts измените интерфейс IListItemsWebPartStrings, чтобы включить свойство ItemFieldLabel:

    declare interface IListItemsWebPartStrings {
      ...
      PropertyPaneDescription: string;
      BasicGroupName: string;
      ListFieldLabel: string;
      ItemFieldLabel: string;
    }
    
  6. В файле src/webparts/listItems/loc/en-us.js добавьте отсутствующее определение для строки ItemFieldLabel:

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

Отрисовка значения свойства веб-части item

В файле src/webparts/listItems/components/ListItems.tsx замените метод render() на следующий:

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

Добавление метода для загрузки элементов списка

В файле src/webparts/listItems/ListItemsWebPart.ts добавьте метод в классе ListItemsWebPart для загрузки доступных элементов из выбранного списка. Как и для загрузки доступных списков, здесь используются фиктивные данные. В зависимости от того, какой список выбран ранее, метод loadItems() возвращает фиктивные элементы списка. Если список не выбран, метод разрешает обещание без данных.

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

Добавление метода для обработки выбора элемента

В классе ListItemsWebPart добавьте новый метод с именем onListItemChange(): После выбора элемента в раскрывающемся меню веб-часть должна сохранить новое значение в свойствах и обновиться с учетом изменений в пользовательском интерфейсе.

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

Отрисовка свойства веб-части item в области свойств

  1. В классе ListItemsWebPart добавьте новое свойство класса под названием itemsDropdown:

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      private itemsDropDown: PropertyPaneAsyncDropdown;
      // ...
    }
    
  2. Замените код метода getPropertyPaneConfiguration() следующим:

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

    Раскрывающееся меню для свойства item инициализируется так же, как раскрывающееся меню для свойства listName. Единственное отличие состоит в том, что после выбора списка раскрывающееся меню должно обновиться, экземпляр элемента управления должен быть назначен переменной класса.

Загрузка элементов выбранного списка

Раскрывающееся меню становится доступным после выбора списка. После выбора списка загружаются элементы этого списка в раскрывающемся меню.

  1. Чтобы реализовать эту логику, дополните ранее заданный метод 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; });
      // 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();
    }
    

    После выбора списка выбранный элемент сбрасывается, сохраняется в свойствах веб-части и сбрасывается в раскрывающемся меню элементов. Раскрывающийся список для выбора элемента становится включенным, а раскрывающийся список обновляется для загрузки его параметров.

  2. Чтобы убедиться, что все работает как ожидается, на консоли запустите:

    gulp serve
    

    После первого добавления веб-части на страницу и открытия ее области свойств вы увидите, что оба раскрывающихся меню отключены и загружают параметры.

    Раскрывающиеся меню в области свойств веб-части загружают данные

    После загрузки параметров становится доступным раскрывающееся меню списков. Так как список еще не выбран, раскрывающееся меню элементов остается отключенным.

    Раскрывающееся меню списков в области свойств веб-части активно. Раскрывающееся меню элементов отключено

    После выбора списка раскрывающееся меню элементов загрузит элементы, доступные в этом списке.

    Раскрывающееся меню элементов загружает доступные элементы после выбора списка в раскрывающемся меню списков

    После загрузки доступных элементов станет доступным раскрывающееся меню элементов.

    Выбор элемента в раскрывающемся меню элементов в области свойств веб-части

    После выбора элемента в раскрывающемся меню элементов веб-часть обновляется для отображения выбранного элемента.

    Выбранные список и элемент в веб-части

  3. Остановите локальный веб-сервер, нажав клавиши CTRL+C в консоли.

См. также