Перейти к содержимому

Параметры WebPart (Property Pane)

Параметры — настройки экземпляра WebPart: какой список читать, сколько строк показать, какой заголовок. Редактор задаёт их в панели «Настройки». Схема объявляется в manifest.json, значения читаются в C# через ctx.Properties.

Эта страница — полный справочник: сценарии выбора данных, каждый тип параметра, dependsOn, методы чтения.

Краткий список типов в манифесте.


Схема (manifest.jsonproperties)Значения экземпляра
ГдеВ пакете .portalpartВ разметке страницы (zones_content) или в form_config формы списка
Что задаётКакие поля показать в панелиЧто выбрал редактор для этой вставки
Одинаково для всех вставок?ДаНет — у каждого экземпляра свои значения

Параметры ≠ поля элемента списка.
listId — «из какого списка читать» (настройка виджета).
title элемента — данные записи (см. Кастомная форма: типы полей).


1. Объявите параметр в manifest.json:

"properties": {
"listId": { "type": "listPicker", "label": "Список", "required": true }
}

2. Пересоберите и переустановите пакет (Админка → WebPart).

3. На странице: Редактировать → WebPart → Настроить → выберите список → Применить → сохраните страницу.

4. В коде:

var listId = ctx.Properties.GetGuid("listId");
if (listId == Guid.Empty)
return new WebPartResult("""<p class="webpart-hint">Укажите список в настройках WebPart.</p>""");
var items = await ctx.Lists.GetItemsAsync(listId, limit: 20, ct: ct);

Этого достаточно, если список лежит в том же узле, что и страница. Список из другого узла — см. сценарий 3.2.


Ниже — готовые шаблоны для Property Pane. Подставьте свои имена ключей (listId, sourceNodeId…).

Редактор видит списки того узла, на странице которого стоит WebPart.

"listId": {
"type": "listPicker",
"label": "Список",
"required": true
}
var listId = ctx.Properties.GetGuid("listId");

dependsOn не указывайте.


Нужен список, который живёт не в узле текущей страницы (например, адресная книга в отдельном разделе, а виджет — на главной).

Сделайте два параметра:

  1. nodePicker с "scope": "subtree" — выбрать любой узел портала;
  2. listPicker с "dependsOn" на этот параметр — подгрузятся списки выбранного узла.
"sourceNodeId": {
"type": "nodePicker",
"label": "Узел со списком",
"scope": "subtree",
"required": true,
"description": "Узел, в котором лежит нужный список"
},
"listId": {
"type": "listPicker",
"label": "Список",
"required": true,
"dependsOn": "sourceNodeId"
}
var sourceNodeId = ctx.Properties.GetGuid("sourceNodeId"); // откуда брали списки в панели
var listId = ctx.Properties.GetGuid("listId"); // достаточно для GetItemsAsync / CreateItemAsync

Для чтения/записи элементов обычно нужен только listId. sourceNodeId полезен для ссылок на узел или повторной настройки.

Эталон: пакет korport.base.birthdays (sourceNodeId + employeesListId).


Если источник данных — один из дочерних узлов относительно узла страницы (не весь портал):

"sourceNodeId": {
"type": "nodePicker",
"label": "Подраздел",
"required": true
},
"listId": {
"type": "listPicker",
"label": "Список",
"required": true,
"dependsOn": "sourceNodeId"
}

Без "scope": "subtree" в nodePicker показываются только дочерние узлы; пустое значение в UI подписано как «Текущий узел» (списки при пустом родителе не подгрузятся, пока родитель не выбран — поле с dependsOn скрыто).


"listId": { "type": "listPicker", "label": "Список", "required": true },
"viewId": {
"type": "viewPicker",
"label": "Представление",
"dependsOn": "listId"
}
var listId = ctx.Properties.GetGuid("listId");
var viewId = ctx.Properties.GetGuid("viewId"); // Guid.Empty = представление по умолчанию
Guid? viewRef = viewId == Guid.Empty ? null : viewId;
var items = await ctx.Lists.GetItemsAsync(listId, viewRef, limit: 50, ct);

viewPicker загружает представления через GET /lists/{listId}/views — узел списка не важен. Можно сочетать с сценарием 3.2.


3.5. Узел → список → представление (цепочка)

Заголовок раздела «3.5. Узел → список → представление (цепочка)»
"sourceNodeId": {
"type": "nodePicker",
"label": "Узел",
"scope": "subtree",
"required": true
},
"listId": {
"type": "listPicker",
"label": "Список",
"required": true,
"dependsOn": "sourceNodeId"
},
"viewId": {
"type": "viewPicker",
"label": "Представление",
"dependsOn": "listId"
}

Порядок в properties лучше такой же: родитель выше потомка.


Каждый список — отдельный параметр со своим ключом:

"tasksListId": { "type": "listPicker", "label": "Список задач", "required": true },
"bookingsListId": { "type": "listPicker", "label": "Список бронирований", "required": true }

Если оба списка в другом узле, заведите один sourceNodeId и два listPicker с одним и тем же dependsOn:

"sourceNodeId": {
"type": "nodePicker",
"label": "Узел данных",
"scope": "subtree",
"required": true
},
"tasksListId": {
"type": "listPicker",
"label": "Задачи",
"required": true,
"dependsOn": "sourceNodeId"
},
"bookingsListId": {
"type": "listPicker",
"label": "Бронирования",
"required": true,
"dependsOn": "sourceNodeId"
}

"libraryId": {
"type": "libraryPicker",
"label": "Библиотека",
"required": true
}
var libraryId = ctx.Properties.GetGuid("libraryId");

Показываются библиотеки узла страницы (как у listPicker без dependsOn).


ЗадачаПоддержка
Библиотека другого узла (libraryPicker + dependsOn на nodePicker)Нет. Платформа не перезагружает библиотеки по узлу. Обход: хранить UUID библиотеки в text / задать список через модуль provisioner
Произвольный UUID списка без выбора в панелиДа, тип text — редактор вводит GUID вручную (удобно только админам)
Выбор элемента списка как параметраНет отдельного типа; используйте text (номер/GUID) или логику в UI WebPart

Секция properties — объект: ключ = имя в коде, значение = описание поля панели.

{
"id": "contoso.demo-widget",
"title": "Демо",
"version": "1.0.0",
"category": "Списки",
"icon": "fa-sliders",
"runtime": "dotnet",
"entry": "dist/DemoWidget.dll",
"entryType": "Contoso.Demo.DemoWebPart",
"styles": ["dist/main.css"],
"scripts": ["dist/main.js"],
"properties": {
"title": {
"type": "text",
"label": "Заголовок",
"default": "Мой виджет",
"description": "Показывается над списком"
},
"listId": { "type": "listPicker", "label": "Список", "required": true },
"viewId": { "type": "viewPicker", "label": "Представление", "dependsOn": "listId" },
"pageSize": { "type": "number", "label": "Записей", "default": 10, "min": 1, "max": 100 },
"showTitle": { "type": "boolean", "label": "Показывать заголовок", "default": true },
"mode": {
"type": "choice",
"label": "Режим",
"default": "week",
"choices": [
{ "value": "week", "label": "Неделя" },
{ "value": "month", "label": "Месяц" }
]
}
}
}

Имена ключей — латиница, camelCase (listId, не ListId). Тот же ключ передаёте в GetGuid("listId").

После изменения схемы: пересборка + переустановка пакета. Уже сохранённые значения не сбрасываются, если ключи не переименовали.

АтрибутГде применимНазначение
typeвсеТип контрола (обязателен)
labelвсеПодпись в панели
descriptionвсеПодсказка под полем
requiredвсе кроме boolean (для boolean пустота не проверяется)Нельзя нажать «Применить» с пустым значением
defaultвсеЗначение до первого сохранения редактором
dependsOnобычно listPicker, viewPickerИмя родительского параметра (см. §5)
choicesтолько choice[{ "value", "label" }]
min / maxтолько numberОграничения ввода в панели
scopeтолько nodePicker"subtree" — все узлы портала; иначе — дочерние узла страницы

dependsOn связывает два параметра.

Поведение панели:

  1. Пока у родителя пустое значение — дочернее поле скрыто.
  2. Когда родитель выбран — дочернее поле показывается.
  3. Для listPicker: вызывается GET /api/v1/webparts/context?nodeId={значение родителя} → в select попадают списки этого узла.
  4. Для viewPicker: вызывается GET /api/v1/lists/{listId}/views → представления выбранного списка.
  5. При смене родителя список опций дочернего поля перезагружается (старое значение может стать невалидным — редактор выбирает заново).

Рабочие пары:

Дочерний typeРодительСмысл
listPickernodePicker (или параметр с UUID узла)Списки выбранного узла
viewPickerlistPicker (или параметр с UUID списка)Представления выбранного списка

Не работает:

ДочернийРодительПочему
libraryPickernodePickerКод панели не обновляет библиотеки по dependsOn
listPickerlistPickerРодителем должен быть узел, не список

Значение dependsOnимя ключа родителя в том же объекте properties (строка "sourceNodeId", не путь).


Для каждого типа: UI → схема → значение → чтение в C# → нюансы.

UIОднострочное поле
Схемаlabel, description, required, default
Значениестрока
Чтениеctx.Properties.GetString("title", "По умолчанию")

Подходит для заголовков, произвольного GUID, номера элемента.


UIМногострочное (textarea)
Схемаlabel, description, required, default
Значениестрока
ЧтениеGetString("intro", "")

Особый случай в UI: если в label или description есть подстрока JSON, панель при сохранении пытается разобрать текст как JSON (массив/объект). Для обычного текста этого лучше не указывать.


UIinput type="number"
Схемаdefault, min, max, required, …
Значениечисло
Чтениеctx.Properties.GetInt32("pageSize", 10)

Пустое/нечисловое в панели часто сохраняется как 0 — всегда задавайте default в схеме и fallback во втором аргументе GetInt32.


UIФлажок
Схемаlabel, default (true/false)
Значениеtrue / false
Чтениеctx.Properties.GetBoolean("showTitle", true)

Для required пустота не считается ошибкой (флажок всегда «есть»).


UIВыпадающий список
Схемаобязательно choices: [{ "value", "label" }], плюс default
Значениестрока value выбранного пункта
ЧтениеGetString("mode", "week")
"mode": {
"type": "choice",
"label": "Режим",
"default": "week",
"choices": [
{ "value": "week", "label": "Неделя" },
{ "value": "month", "label": "Месяц" }
]
}

В коде сравнивайте с value, не с label.


UIВыбор цвета
Схемаdefault (например "#2563eb")
Значениестрока #rrggbb
ЧтениеGetString("accent", "#2563eb")

UISelect со списками
Схемаrequired, dependsOn (опционально)
ЗначениеUUID списка (строка)
Чтениеctx.Properties.GetGuid("listId")Guid.Empty, если не выбран

Варианты:

КонфигОткуда списки
без dependsOnУзел текущей страницы (/webparts/context?nodeId= страницы)
"dependsOn": "sourceNodeId"Узел из значения родителя (другой или дочерний — см. §3.2, §3.3)

UISelect с библиотеками узла страницы
Схемаrequired, default
ЗначениеUUID библиотеки
ЧтениеGetGuid("libraryId")

Ограничение: только библиотеки узла страницы. Связка с nodePicker через dependsOn не поддерживается (§3.8).


UISelect с узлами
Схемаscope, required, description
ЗначениеUUID узла (строка); может быть пустым
ЧтениеGetGuid("sourceNodeId")

Варианты scope:

scopeСписок в панелиПодпись пустого пункта
не указанДочерние узлы узла страницы«Текущий узел»
"subtree"Узлы портала (дерево от корня, с путём в названии)«Выберите узел»

Для выбора списка из произвольного раздела почти всегда нужен "scope": "subtree" + listPicker с dependsOn.


UISelect представлений
Схемаобычно dependsOn на параметр со списком
ЗначениеUUID представления или пусто («по умолчанию»)
ЧтениеGetGuid("viewId")Guid.Empty = default view

Без выбранного родителя-списка поле скрыто / пустое. Работает и со списком текущего узла, и со списком из другого узла.


UIТаблица: название, URL, иконка FA + кнопки добавить/удалить
Схемаlabel, description, required
Значениемассив объектов { "title", "url", "icon" }
ЧтениеTryGetValue + разбор JsonElement (не GetString)
if (ctx.Properties.TryGetValue("links", out var linksEl)
&& linksEl.ValueKind == JsonValueKind.Array)
{
foreach (var link in linksEl.EnumerateArray())
{
var linkTitle = link.TryGetProperty("title", out var t) ? t.GetString() ?? "" : "";
var url = link.TryGetProperty("url", out var u) ? u.GetString() ?? "" : "";
var icon = link.TryGetProperty("icon", out var i) ? i.GetString() ?? "" : "";
}
}

Контекст: IWebPartContext ctx в RenderAsync и HandleActionAsync.
Тип: IReadOnlyDictionary<string, JsonElement>.
Хелперы: WebPartPropertiesExtensions в Portal.WebPart.Sdk.

МетодТипы параметровЕсли ключа нет / значение невалидно
GetString(key, defaultValue = "")text, multiline, choice, color; также приводит number/bool к текстуdefaultValue
GetGuid(key)listPicker, libraryPicker, nodePicker, viewPickerGuid.Empty
GetInt32(key, defaultValue = 0)numberdefaultValue
GetBoolean(key, defaultValue = false)booleandefaultValue
TryGetValue(key, out JsonElement)linksEditor, любой нестандартный JSONfalse

Полный пример:

using System.Text.Json;
using Portal.WebPart.Sdk;
public async Task<WebPartResult> RenderAsync(IWebPartContext ctx, CancellationToken ct)
{
var title = ctx.Properties.GetString("title", "Мой виджет");
var mode = ctx.Properties.GetString("mode", "week");
var listId = ctx.Properties.GetGuid("listId");
var viewId = ctx.Properties.GetGuid("viewId");
var libraryId = ctx.Properties.GetGuid("libraryId");
var pageSize = ctx.Properties.GetInt32("pageSize", 10);
var showTitle = ctx.Properties.GetBoolean("showTitle", true);
if (listId == Guid.Empty)
return new WebPartResult("""<p class="webpart-hint">Укажите список в настройках WebPart.</p>""");
// дальше — Lists / Libraries API
await Task.CompletedTask;
return new WebPartResult($"<p>{System.Net.WebUtility.HtmlEncode(title)}</p>");
}

В HandleActionAsync те же ctx.Properties — платформа передаёт актуальные настройки экземпляра вместе с action.

Сырой доступ:

if (ctx.Properties.TryGetValue("pageSize", out var raw))
{
// raw.ValueKind — Number / String / …
}

  1. Редактировать страницу → на WebPart Настроить.
  2. Заполнить панель → Применить.
  3. Сохранить страницу (иначе значения пропадут при уходе).

Хранилище: properties блока webpart в zones_content.

Настройки списка → Формы → WebPart на форме → те же поля схемы; значения в form_configwebParts[].properties.

listPicker без dependsOn берёт списки узла списка (контекст формы). Для списка из другого раздела снова используйте §3.2.

Изменение в схемеЭффект
Добавили новый ключУ старых экземпляров ключа нет → сработает default / fallback в Get*
Переименовали ключСтарое значение остаётся под старым именем; новый ключ пустой
Удалили ключЗначение может остаться в JSON страницы, код его больше не читает

СимптомПричинаЧто сделать
В панели нет полейНет properties в манифесте или пакет не переустановленПроверить manifest.json, переустановить .portalpart
В listPicker только списки «этой» страницы, а нужен другой узелНет связки с nodePicker§3.2: scope: "subtree" + dependsOn
GetGuidEmptyНе нажали «Применить» / не сохранили страницу; опечатка в ключеlistId vs ListId; сохранить страницу
viewPicker пустойНет dependsOn на параметр списка или список ещё не выбранКак в §3.4
libraryPicker не зависит от выбранного узлаТак устроена панель§3.8
После обновления пакета «пропали» настройкиПереименовали ключи в propertiesОставить старые имена или мигрировать значения на страницах
GetInt32 всегда defaultВ панели пусто / не числоdefault в схеме + fallback в коде
Дочернее поле не видноРодитель пуст — так работает dependsOnСначала выбрать узел/список-родитель