Значения полей списков: чтение и обновление
Руководство по программной работе с field_values элементов списка — REST API, JavaScript WebPart, C# SDK (WebPart, Event Receiver, Timer Job).
Пошаговая форма WebPart и удобный API PortalListItem (item["title"], UpdateAsync): Кастомная форма: все типы полей.
См. также: API списков (эндпоинты, типы полей, фильтрация).
Как устроены данные
Заголовок раздела «Как устроены данные»-
Схема полей —
GET /lists/{listId}/fieldsилиGetFieldsAsync. У каждого поля естьid(UUID) иinternal_name(title,status…). -
Значения в ответах — объект
field_valuesс ключами UUID. Для доступа по имени используйтеGetListItemAsync/PortalListItem. -
Запись (
POST/PATCH,CreateItemAsync/UpdateItemAsync) — ключи могут быть UUID илиinternal_name; сервер нормализует в UUID.PortalListItemпишет поinternal_name.
Сырой вид ответа:
{ "id": 12, "guid": "uuid-элемента", "field_values": { "uuid-поля-title": "Заголовок", "uuid-поля-status": "Новая" }}-
idэлемента в ответе API — публичный номер (12). ДляGetItem/PATCHможно передать номер илиguid. -
Обновление — merge: только переданные поля; затем сервер проверяет все обязательные поля схемы.
-
Фильтр (
filter,QueryItemsAsync) — вcolumnдопустимinternal_name("title"). -
Версии — при включённом версионировании у версии полный снимок
fieldValues(UUID-ключи).
Форматы значений по типам полей
Заголовок раздела «Форматы значений по типам полей»| Тип | Значение в fieldValues / field_values |
|---|---|
text, multiline_text, choice | строка |
number | число |
date | "YYYY-MM-DD" |
datetime | ISO 8601 |
boolean | true / false |
person | массив `[{ “principalType”: “user” |
lookup | `{ “itemId”: “12” |
link | { "url": "https://...", "description": "..." } |
REST API
Заголовок раздела «REST API»# Схема полейGET /api/v1/lists/{listId}/fields
# Найти элемент по заголовкуGET /api/v1/lists/{listId}/items?filter={"logic":"and","conditions":[{"column":"title","operator":"eq","value":"GlobalSettings"}]}&limit=1
# Один элемент (itemId = 12 или guid)GET /api/v1/lists/{listId}/items/12Создать:
POST /api/v1/lists/{listId}/itemsContent-Type: application/json
{ "fieldValues": { "uuid-поля-title": "Новая задача", "uuid-поля-status": "Новая", "uuid-поля-due": "2026-07-15" }}Обновить одно поле (merge):
PATCH /api/v1/lists/{listId}/items/12Content-Type: application/json
{ "fieldValues": { "uuid-поля-status": "Готово" }}Сложные типы:
{ "fieldValues": { "uuid-assignees": [ { "principalType": "user", "id": "uuid-пользователя", "display": "Иван Иванов" }, { "principalType": "group", "id": "uuid-группы", "display": "Редакторы HR" } ], "uuid-room": { "itemId": "uuid-элемента-справочника", "display": "Переговорная 301" } }}JavaScript (WebPart, клиент)
Заголовок раздела «JavaScript (WebPart, клиент)»// Чтениеconst fields = await ctx.lists.getFields(listId);const titleField = fields.find(f => f.internal_name === 'title');const titleFieldId = titleField.id;
const items = await ctx.lists.getItems(listId, { filter: { logic: 'and', conditions: [{ column: 'title', operator: 'eq', value: 'A' }] }, top: 1,});const item = items[0];const title = item.field_values?.[titleFieldId];
// Запись (через REST)await PortalApi.patch(`/lists/${listId}/items/${item.id}`, { fieldValues: { [statusFieldId]: 'Готово' },});В фильтрах — column: "title" (internal_name). При записи в fieldValues допустимы UUID и internal_name.
WebPart (C#)
Заголовок раздела «WebPart (C#)»Права проверяются от имени пользователя страницы.
Рекомендуемый путь — PortalListItem (подробно: кастомная форма; типы геттеров: § Типы за var):
using Portal.Contracts.Lists;using Portal.WebPart.Sdk;
var item = await ctx.Lists.GetListItemAsync(listId, "12", ct); // PortalListItemvar title = item.GetString("title"); // string?item["status"] = "Готово";await item.UpdateAsync(ct);
var created = await ctx.Lists.NewListItemAsync(listId, ct);created["title"] = "Новая задача";await created.CreateAsync(ct);
var found = await ctx.Lists.QueryListItemsAsync(listId, ListItemQueryBuilder.ForList(listId) .WhereField("title", f => f.Eq("GlobalSettings")) .Take(1) .Build(), ct);Больше примеров фильтров (AND/OR, даты, person, lookup, пагинация): Выборка элементов списка.
HandleActionAsync:
var item = await ctx.Lists.GetListItemAsync(listId, itemId, ct);item["status"] = "Готово";await item.UpdateAsync(ct);return await RenderAsync(ctx, ct);Низкоуровнево по-прежнему доступны GetItemAsync + UUID-ключи и PortalItemJson (версии). См. SDK WebPart.
Event Receiver (C#)
Заголовок раздела «Event Receiver (C#)»Права — от пользователя события (context.User).
Рекомендуемый путь — PortalListItem:
using Portal.Contracts.Lists;using Portal.EventReceiver.Sdk;
public async Task<object?> HandleAsync(EventContext context, IReceiverApi api, CancellationToken ct){ // Текущий элемент события — без разбора Details вручную var item = await context.GetListItemAsync(api, ct); if (item is null) return new { skipped = true };
var currentTitle = item.GetString("title"); item["status"] = "Готово"; await item.UpdateAsync(ct);
return new { ok = true, was = currentTitle, itemRef = context.GetItemRef() };}Для listItem.deleted элемент уже удалён — берите снимок: await context.GetListItemSnapshotAsync(api, ct).
Справка: SDK Event Receiver § текущий элемент.
Низкоуровнево (UUID / версии):
using System.Text.Json;
var listId = context.GetListId()!.Value;var itemRef = context.GetItemRef()!;
var fieldsJson = (JsonElement)await api.Lists.GetFieldsAsync(listId.ToString(), ct);var titleFieldId = fieldsJson.EnumerateArray() .First(f => f.GetProperty("internal_name").GetString() == "title") .GetProperty("id").GetString()!;
var raw = (JsonElement)await api.Lists.GetItemAsync(listId.ToString(), itemRef, ct);var currentTitle = raw.GetProperty("field_values").GetProperty(titleFieldId).GetString();
var v1 = (JsonElement)await api.Lists.GetItemVersionAsync(listId.ToString(), itemRef, 1, ct);var titleV1 = v1.GetProperty("fieldValues").GetProperty(titleFieldId).GetString();
await api.Lists.UpdateItemAsync(listId.ToString(), itemRef, new Dictionary<string, object?>{ [titleFieldId] = currentTitle, // или ["status"] = "Готово"}, ct);См. SDK Event Receiver.
Timer Job (C#)
Заголовок раздела «Timer Job (C#)»Тот же API, что у Event Receiver (GetListItemAsync / PortalListItem). Права — от системной учётки portal-system (полный доступ).
using Portal.Contracts.Lists;using Portal.TimerJob.Sdk;
public async Task<object?> ExecuteAsync(TimerJobContext context, ITimerJobApi api, CancellationToken ct){ var listId = Guid.Parse(context.Config.GetProperty("settingsListId").GetString()!);
var item = (await api.Lists.QueryListItemsAsync(listId, ListItemQueryBuilder.ForList(listId) .WhereField("title", f => f.Eq("GlobalSettings")) .Take(1) .Build(), ct)).First();
var oldValue = item.GetString("config_value"); item["config_value"] = "новое значение"; await item.UpdateAsync(ct);
return new { ok = true, oldValue };}См. SDK Timer Jobs.
Версии элементов
Заголовок раздела «Версии элементов»При включённом версионировании списка (versioningEnabled / Настройки списка → Версионирование) у каждой версии хранится полный снимок fieldValues с теми же UUID-ключами, что у текущего элемента.
Подробности REST и формат ответа: API списков — Версии элементов.
GET /api/v1/lists/{listId}/items/12/versionsconst res = await PortalApi.get(`/lists/${listId}/items/${itemId}/versions`);const { versioningEnabled, versions, fields } = res.data;if (!versioningEnabled) return;
const v2 = versions.find(v => v.versionNumber === 2);const titleThen = v2?.fieldValues?.[titleFieldId];WebPart (C#)
Заголовок раздела «WebPart (C#)»// Одна версия — сразу снимок fieldValuesvar v2 = await ctx.Lists.GetItemVersionAsync(listId, "12", versionNumber: 2, ct);var titleThen = PortalItemJson.GetFieldString(v2, titleFieldId);var dueThen = PortalItemJson.ReadDateTimeLocal(v2, dueFieldId);
// Вся историяvar history = await ctx.Lists.GetItemVersionsAsync(listId, "12", ct);if (!PortalItemJson.IsVersioningEnabled(history)) return;
foreach (var version in PortalItemJson.EnumerateVersions(history)){ var n = PortalItemJson.GetVersionNumber(version); var status = PortalItemJson.GetFieldString(version, statusFieldId);}
// Версия из ответа истории без повторного запросаif (PortalItemJson.TryFindVersion(history, 1, out var v1)){ var createdTitle = PortalItemJson.GetFieldString(v1, titleFieldId);}Хелперы PortalItemJson.TryGetFieldValue / GetFieldString / ReadLookupGuid / ReadDateTimeLocal принимают и текущий элемент (field_values), и снимок версии (fieldValues).
Event Receiver / Timer Job (C#)
Заголовок раздела «Event Receiver / Timer Job (C#)»var v1 = (JsonElement)await api.Lists.GetItemVersionAsync(listId, itemRef, 1, ct);var titleV1 = v1.GetProperty("fieldValues").GetProperty(titleFieldId).GetString();
var history = (JsonElement)await api.Lists.GetItemVersionsAsync(listId, itemRef, ct);var versions = history.GetProperty("versions");Timer Job выполняется от системной учётки portal-system (полный доступ).
Сводка: где что доступно
Заголовок раздела «Сводка: где что доступно»| Способ | Читать | Создать | Обновить | Версии |
|---|---|---|---|---|
| REST API | ✅ | ✅ | ✅ PATCH (merge) | ✅ GET .../versions |
JS portalContext | ✅ | через PortalApi.post | через PortalApi.patch | через PortalApi.get |
| WebPart C# | ✅ | ✅ | ✅ UpdateItemAsync | ✅ GetItemVersion(s)Async |
| Event Receiver | ✅ | ✅ | ✅ | ✅ |
| Timer Job | ✅ (portal-system) | ✅ | ✅ | ✅ |
Паттерн (рекомендуемый): GetListItemAsync / NewListItemAsync → item["internal_name"] → CreateAsync / UpdateAsync.
Низкоуровневый: схема → UUID → field_values[fieldId] (для истории версий — PortalItemJson).
Подробные кейсы: Кейсы работы с сущностями, кастомная форма.