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


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

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

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

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

Примечание.

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

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

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

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

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

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

    • Как называется решение? react-cascadingdropdowns
    • Какой тип клиентского компонента нужно создать? WebPart
    • Как называется веб-часть? Элементы списка
    • Какой шаблон вы хотите использовать?: React
  5. Откройте папку проекта в редакторе кода. В этой статье в инструкциях и на снимках экрана используется Visual Studio Code, но вы можете использовать любой другой редактор.

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

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

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

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

    export interface IListItemsWebPartProps {
      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
        });
    
        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.ListNameFieldLabel
                    })
                  ]
                }
              ]
            }
          ]
        };
      }
    
      // ...
    }
    
  5. В файле src/webparts/listItems/loc/mystrings.d.ts измените интерфейс IListItemsStrings на следующий:

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

    define([], function() {
      return {
        "PropertyPaneDescription": "Description",
        "BasicGroupName": "Group Name",
        "ListNameFieldLabel": "List"
      }
    });
    
  7. В файле src/webparts/listItems/components/ListItems.tsx замените содержимое метода render() следующим:

    public render(): JSX.Element {
      const {
        listName
      } = this.props;
    
      return (
        <section className={`${styles.listItems} ${hasTeamsContext ? styles.teams : ''}`}>
          <div className={styles.welcome}>
            <img alt="" src={isDarkTheme ? require('../assets/welcome-dark.png') : require('../assets/welcome-light.png')} className={styles.welcomeImage} />
            <h2>Well done, {escape(userDisplayName)}!</h2>
            <div>{environmentMessage}</div>
            <div>List name: <strong>{escape(listName)}</strong></div>
          </div>
        </section>
      );
    }
    
  8. В файле src/webparts/listItems/components/IListItemsProps.ts замените интерфейс IListItemsProps следующим:

    export interface IListItemsProps {
      listName: string;
    }
    
  9. Чтобы убедиться, что проект работает, выполните следующую команду:

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

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

Заполнение раскрывающегося меню списками SharePoint, из которых следует выбирать

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

Отрисовка свойства listName с использованием раскрывающегося меню

  1. В классе ListItemsWebPart добавьте ссылку на класс PropertyPaneDropdown в верхнем разделе веб-части. Замените предложение импорта, загружающее класс PropertyPaneTextField, следующим кодом:

    import {
      IPropertyPaneConfiguration,
      PropertyPaneTextField,
      PropertyPaneDropdown,
      IPropertyPaneDropdownOption
    } from '@microsoft/sp-property-pane';
    
  2. В классе ListItemsWebPart добавьте новую переменную lists для хранения сведений обо всех доступных списках на текущем сайте.

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      private lists: IPropertyPaneDropdownOption[];
      // ...
    }
    
  3. Добавьте новую переменную класса под названием listsDropdownDisabled. Она определяет, включено ли раскрывающееся меню со списками. Пока веб-часть не получит сведения о доступных списках на текущем сайте, раскрывающееся меню должно быть отключено.

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
      private listsDropdownDisabled: boolean = true;
      // ...
    }
    
  4. Измените метод getPropertyPaneConfiguration(), чтобы использовать раскрывающееся меню для отрисовки свойства listName:

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
    
      protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
        return {
          pages: [
            {
              header: {
                description: strings.PropertyPaneDescription
              },
              groups: [
                {
                  groupName: strings.BasicGroupName,
                  groupFields: [
                    PropertyPaneDropdown('listName', {
                      label: strings.ListNameFieldLabel,
                      options: this.lists,
                      disabled: this.listsDropdownDisabled
                    })
                  ]
                }
              ]
            }
          ]
        };
      }
    
    }
    
  5. Чтобы проверить правильность работы, выполните указанную ниже команду.

    gulp serve
    

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

Отображение доступных списков в раскрывающемся меню

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

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

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
    
      private loadLists(): Promise<IPropertyPaneDropdownOption[]> {
        return new Promise<IPropertyPaneDropdownOption[]>((resolve: (options: IPropertyPaneDropdownOption[]) => void, reject: (error: any) => void) => {
          setTimeout((): void => {
            resolve([{
              key: 'sharedDocuments',
              text: 'Shared Documents'
            },
            {
              key: 'myDocuments',
              text: 'My Documents'
            }]);
          }, 2000);
        });
      }
    }
    
  2. Загрузите сведения о доступных списках в раскрывающееся меню со списками. В классе ListItemsWebPart переопределите метод onPropertyPaneConfigurationStart(), используя следующий код.

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
    
      protected onPropertyPaneConfigurationStart(): void {
        this.listsDropdownDisabled = !this.lists;
    
        if (this.lists) {
          return;
        }
    
        this.context.statusRenderer.displayLoadingIndicator(this.domElement, 'lists');
    
        this.loadLists()
          .then((listOptions: IPropertyPaneDropdownOption[]): void => {
            this.lists = listOptions;
            this.listsDropdownDisabled = false;
            this.context.propertyPane.refresh();
            this.context.statusRenderer.clearLoadingIndicator(this.domElement);
            this.render();
          });
      }
      // ...
    }
    

    SharePoint Framework вызывает метод onPropertyPaneConfigurationStart() после открытия области свойств веб-части.

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

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

    Когда информация о доступных списках загрузится, метод назначит полученные данные переменной класса lists, из которой они попадут в раскрывающееся меню со списками.

    После этого включается раскрывающееся меню, где пользователь сможет выбрать список. Благодаря вызову метода this.context.propertyPane.refresh() обновляется область свойств веб-части, чтобы отобразить последние изменения в раскрывающемся меню со списками.

    После загрузки сведений индикатор загрузки удаляется вызовом метода clearLoadingIndicator(). Так как вызов этого метода очищает интерфейс веб-части, вызывается метод render() для повторной отрисовки веб-части.

  3. Выполните следующую команду, чтобы убедиться, что всё работает правильно:

    gulp serve
    

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

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

Предоставление пользователям возможности выбрать элемент в указанном списке

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

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

Добавление свойства веб-части для выбора элементов

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

    {
      // ...
      "properties": {
        "listName": "",
        "itemName": ""
      }
      // ...
    }
    
  2. Замените код в файле src/webparts/listItems/IListItemsWebPartProps.ts следующим:

    export interface IListItemsWebPartProps {
      listName: string;
      itemName: string;
    }
    
  3. Замените код в файле src/webparts/listItems/components/IListItemsProps.ts следующим:

    export interface IListItemsProps {
      listName: string;
      itemName: string;
    }
    
  4. В файле 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,
          itemName: this.properties.itemName
        });
    
        ReactDom.render(element, this.domElement);
      }
      // ...
    }
    
  5. В файле src/webparts/listItems/loc/mystrings.d.ts измените интерфейс IListItemsStrings на следующий:

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

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

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

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

export default class ListItems extends React.Component<IListItemsProps, {}> {
  public render(): JSX.Element {
    const {
      listName,
      itemName
    } = this.props;

    return (
      <section className={`${styles.listItems} ${hasTeamsContext ? styles.teams : ''}`}>
        <div className={styles.welcome}>
          <img alt="" src={isDarkTheme ? require('../assets/welcome-dark.png') : require('../assets/welcome-light.png')} className={styles.welcomeImage} />
          <h2>Well done, {escape(userDisplayName)}!</h2>
          <div>{environmentMessage}</div>
          <div>List name: <strong>{escape(listName)}</strong></div>
          <div>Item name: <strong>{escape(itemName)}</strong></div>
        </div>
      </section>
    );
  }
}

Предоставление пользователям возможности выбрать элемент списка

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

  1. В классе ListItemsWebPart добавьте новую переменную items для хранения информации обо всех доступных элементах в выбранном списке.

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
      private items: IPropertyPaneDropdownOption[];
      // ...
    }
    
  2. Добавьте новую переменную класса под названием itemsDropdownDisabled. Она определяет, следует ли включать раскрывающееся меню. Пользователи могут выбрать элемент только после выбора соответствующего списка.

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
      private itemsDropdownDisabled: boolean = true;
      // ...
    }
    
  3. Измените метод getPropertyPaneConfiguration(), чтобы использовать раскрывающееся меню для отрисовки свойства itemName.

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
      protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
        return {
          pages: [
            {
              header: {
                description: strings.PropertyPaneDescription
              },
              groups: [
                {
                  groupName: strings.BasicGroupName,
                  groupFields: [
                    PropertyPaneDropdown('listName', {
                      label: strings.ListNameFieldLabel,
                      options: this.lists,
                      disabled: this.listsDropdownDisabled
                    }),
                    PropertyPaneDropdown('itemName', {
                      label: strings.ItemNameFieldLabel,
                      options: this.items,
                      disabled: this.itemsDropdownDisabled
                    })
                  ]
                }
              ]
            }
          ]
        };
      }
    }
    
  4. Чтобы проверить правильность работы, выполните указанную ниже команду.

    gulp serve
    

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

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

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

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

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
      private loadItems(): Promise<IPropertyPaneDropdownOption[]> {
        if (!this.properties.listName) {
          // resolve to empty options since no list has been selected
          return Promise.resolve();
        }
    
        const wp: ListItemsWebPart = this;
    
        return new Promise<IPropertyPaneDropdownOption[]>((resolve: (options: IPropertyPaneDropdownOption[]) => void, reject: (error: any) => void) => {
          setTimeout(() => {
            const items = {
              sharedDocuments: [
                {
                  key: 'spfx_presentation.pptx',
                  text: 'SPFx for the masses'
                },
                {
                  key: 'hello-world.spapp',
                  text: 'hello-world.spapp'
                }
              ],
              myDocuments: [
                {
                  key: 'isaiah_cv.docx',
                  text: 'Isaiah CV'
                },
                {
                  key: 'isaiah_expenses.xlsx',
                  text: 'Isaiah Expenses'
                }
              ]
            };
            resolve(items[wp.properties.listName]);
          }, 2000);
        });
      }
    }
    

    Метод loadItems() возвращает фиктивные элементы списка для ранее выбранного списка. Если список не выбран, метод разрешает обещание без данных.

  2. Загрузите сведения о доступных элементах в раскрывающееся меню. В классе ListItemsWebPart измените метод onPropertyPaneConfigurationStart() так, чтобы он загружал элементы выбранного списка:

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
      protected onPropertyPaneConfigurationStart(): void {
        this.listsDropdownDisabled = !this.lists;
        this.itemsDropdownDisabled = !this.properties.listName || !this.items;
    
        if (this.lists) {
          return;
        }
    
        this.context.statusRenderer.displayLoadingIndicator(this.domElement, 'options');
    
        this.loadLists()
          .then((listOptions: IPropertyPaneDropdownOption[]): Promise<IPropertyPaneDropdownOption[]> => {
            this.lists = listOptions;
            this.listsDropdownDisabled = false;
            this.context.propertyPane.refresh();
            return this.loadItems();
          })
          .then((itemOptions: IPropertyPaneDropdownOption[]): void => {
            this.items = itemOptions;
            this.itemsDropdownDisabled = !this.properties.listName;
            this.context.propertyPane.refresh();
            this.context.statusRenderer.clearLoadingIndicator(this.domElement);
            this.render();
          });
      }
      // ...
    }
    

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

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

  3. Выполните следующую команду, чтобы убедиться, что всё работает правильно:

    gulp serve
    

    При необходимости раскрывающееся меню может быть изначально отключено, чтобы пользователи сначала выбирали список. На данном этапе, даже после выбора списка, раскрывающееся меню элементов остается отключенным.

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

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

    Откройте файл ListItemsWebPart.ts и переопределите в классе ListItemsWebPart метод onPropertyPaneFieldChanged(), добавив следующий код:

    export default class ListItemsWebPart extends BaseClientSideWebPart<IListItemsWebPartProps> {
      // ...
      protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void {
        if (propertyPath === 'listName' &&
            newValue) {
          // push new list value
          super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue);
          // get previously selected item
          const previousItem: string = this.properties.itemName;
          // reset selected item
          this.properties.itemName = undefined;
          // push new item value
          this.onPropertyPaneFieldChanged('itemName', previousItem, this.properties.itemName);
          // disable item selector until new items are loaded
          this.itemsDropdownDisabled = true;
          // refresh the item selector control by repainting the property pane
          this.context.propertyPane.refresh();
          // communicate loading items
          this.context.statusRenderer.displayLoadingIndicator(this.domElement, 'items');
    
          this.loadItems()
            .then((itemOptions: IPropertyPaneDropdownOption[]): void => {
              // store items
              this.items = itemOptions;
              // enable item selector
              this.itemsDropdownDisabled = false;
              // clear status indicator
              this.context.statusRenderer.clearLoadingIndicator(this.domElement);
              // re-render the web part as clearing the loading indicator removes the web part body
              this.render();
              // refresh the item selector control by repainting the property pane
              this.context.propertyPane.refresh();
            });
        }
        else {
          super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue);
        }
      }
      // ...
    }
    

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

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

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

См. также