Ask the assistant
0/400
Your question is processed by an AI service and kept to improve answers. Do not enter personal data.

    Records

    This page has not been translated into English yet. The Russian original is below.

    Хранилище структурированных записей — журналов, заметок, любых списков, которые растут со временем. В отличие от `Storage` , где один ключ хранит одно значение, Records даёт записям стабильные id , даты и мягкое удаление, поэтому одну запись можно отредактировать или удалить, не переписывая весь список.

    Тип: multi-child (генератор виджетов, поддерживает слоты жизненного цикла)

    Records collection:"workouts" data:@rows count:@n
      ListView.builder itemCount:$n
        @Macros id:itemBuilder index:0
          Text {rows[index]['title'] ?? ""}

    Правило выбора: если элементы ДОБАВЛЯЮТСЯ со временем — это Records . Скаляр, который пользователь задаёт один раз (тема, дневная цель, флаг), остаётся в Storage .

    Параметры

    Параметр Тип Default Описание
    collection string — Имя коллекции. Алиас — name
    includeDeleted bool false Публиковать и мягко удалённые записи (экран корзины)
    data @ -переменная — Список записей
    count @ -переменная — Сколько записей опубликовано
    loaded @ -переменная — true , когда чтение завершилось
    error @ -переменная — Текст ошибки или null

    У каждой записи всегда есть служебные поля id , createdAt , updatedAt , deletedAt — их проставляет рантайм, писать их самому не нужно.

    Дочерние элементы

    Дети размечаются по состоянию загрузки, и монтируется только подходящий — так id:ready никогда не строится на отсутствующих данных:

    Records collection:"notes" data:@notes count:@n
      CircularProgressIndicator id:loading
      Text "Не удалось прочитать" id:error
      ListView.builder id:ready itemCount:$n
        ...

    id:ready можно не указывать — им становится первый неразмеченный ребёнок.

    Запись: ambient-объект records

    Чтение — это элемент, запись — это методы: в выражении нет присваиваемой левой части. Вызывать их нужно из обработчика в форме (){…} ; голое {records.add(…)} — это ВЫРАЖЕНИЕ-ЗНАЧЕНИЕ, оно выполнится один раз при монтировании экрана.

    Метод Что делает
    records.add(collection, fields) добавить запись
    records.update(collection, id, patch) изменить одну запись; неизвестный id — no-op
    records.remove(collection, id) МЯГКОЕ удаление одной записи (ставит deletedAt )
    records.purge(collection, id) ЖЁСТКОЕ удаление одной записи
    records.removeWhere(collection, (r){…}) мягко удалить ВСЕ подходящие под предикат
    records.purgeWhere(collection, (r){…}) жёстко удалить все подходящие
    records.clear(collection) очистить одну коллекцию
    records.clearAll() очистить ВСЕ коллекции

    Полные сигнатуры и документация доступны через reeel_schema {name:"records"} .

    Примеры

    Форма, которая сохраняет

    @title:""
    Records collection:"workouts" data:@rows
      Column
        TextField value:$title
        FilledButton onPressed:(){records.add("workouts", mapOf("title", title, "at", now())); title=""}
          Text "Добавить"

    Удаление по условию

    Область удаления выбирает метод: одна запись — remove / purge , произвольный фильтр — *Where , вся коллекция — clear .

    // сбросить всё за сегодня — один вызов, один проход
    OutlinedButton onPressed:(){records.removeWhere("intake", (r){dateKey(r['at'] ?? 0) == dateKey(today())})}
      Text "Сбросить сегодня"

    Корзина и восстановление

    remove мягкое, поэтому удалённое ещё доступно с includeDeleted:true . Предикат purgeWhere — единственный, который ВИДИТ мягко удалённые записи:

    Records collection:"notes" includeDeleted:true data:@trash
      Column
        TextButton onPressed:(){records.purgeWhere("notes", (r){r['deletedAt'] != null})}
          Text "Очистить корзину"

    Восстановление — это обычный update : records.update("notes", id, mapOf("deletedAt", null)) .

    Число из поля ввода

    TextField публикует строку даже при keyboardType:number , а sum / sumBy строки пропускают: записанный текстом объём молча суммируется в ноль. Преобразуйте при записи — mapOf("volume", num(volume), …) — или объявите поле в схеме: Field "volume" type:number приводит значение при каждой записи и чтении, и уже сохранённые строки читаются числами.

    График из записей

    Записи разрежены и датированы, а графику нужен плотный ряд чисел. series(...) проходит все дни диапазона, подставляя 0 там, где записей не было:

    Records collection:"intake" data:@rows
      State @week:{series(rows, addDays(today(), -6), today(),
                          (r){dateKey(r['at'])}, (rs){sumBy(rs, (x){x['ml'] ?? 0})})}
        SizedBox height:150
          $Bars values:$week color:$theme.primary

    Bad practices

    Чистая функция ради побочного эффекта

    // BAD — map/filter это ЧИСТЫЕ функции. Такой вызов порождает по отдельной
    // записи на строку, они гонятся друг с другом, и экран увидит только последнюю.
    onPressed:(){map(filter(rows, (r){dateKey(r['at']) == dateKey(today())}), (r){records.remove("intake", r['id'] ?? "")})}
    // GOOD — один вызов с предикатом: выборка целиком, одна публикация
    onPressed:(){records.removeWhere("intake", (r){dateKey(r['at'] ?? 0) == dateKey(today())})}

    Растущий список в одном ключе Storage

    // BAD — у элементов нет id, редактирование одного переписывает весь список
    Storage @workouts:[]
    // GOOD
    Records collection:"workouts" data:@rows

    Подписка без значения по умолчанию

    // BAD — выводы генератора равны null, пока данные не загрузились
    Text {rows[index]['title']}
    // GOOD
    Text {rows[index]['title'] ?? ""}

    Best practices

    • Не храните производные значения. Итоги, серии и streak считаются из записей выражением; сохранённая копия рано или поздно разойдётся с журналом.
    • Передавайте объект через маршрут , а не через глобальную переменную: id — параметром пути, сам объект — вторым аргументом navigator.show(...) , читать через RouteData .
    • records.clear() без имени коллекции ничего не делает — имя обязательно.
    • clearAll необратим и срабатывает по одному нажатию: ставьте его за подтверждением.

    See also

    DOCS
    dark_mode_baseline
    RU/EN
    menu_baseline