Skip to content

v4.1.0

Choose a tag to compare

@lehni lehni released this 13 Oct 20:25
· 496 commits to main since this release

The first release of Dito.js 4, which includes the withdrawn 4.0.0, so these notes list all changes since 3.5.0. The admin is rebuilt around its data: forms, dialogs and panels keep their edits, dirty state and errors also for fields that aren't rendered, nested forms edit drafts, and callbacks receive typed contexts instead of components. See Upgrading to 4.x for what apps need to change.

Behaviour changes

  • admin: The value hooks are renamed: format() → formatValue(), parse() → parseValue(), compute() → computeValue() and process() → serializeValue(). The old names throw, except format() on date and color types, where format is the format setting: rename these by hand, see Value hooks.
  • admin: Schemas can't define methods, computed, watch and data, and handlers like onClick() or itemLabel() are called with their context only, without the component as this: use plain functions, onChange() and module state, see Methods, computed, watch and data.
  • admin: Schema options, see Schema options; the old options throw:
    • formats of the date types is format, and the date strings of loaded data become Date objects.
    • Trees open their items with collapsed: false instead of open: true. collapsed is true by default and can be a callback.
    • relate takes the key to relate options by, e.g. relate: 'code', instead of relateBy, and groupBy is options.group.
    • format: 'name' of the color type is removed: use a hex format, e.g. 'hex'.
    • nested only works on sections and panels.
    • Options that were never read are removed from the types, e.g. defaultSort and defaultScope of sources and idKey of forms.
  • admin: Relative paths in options.dataPath and in the dataPath of the computed, data and hidden types are relative to the item instead of the field, so they need one ../ less: '../authors' becomes 'authors'. The match of validate() resolves the same way, and .. above the root throws.
  • admin: Events, see Event handlers:
    • Declare handlers as on… properties, e.g. onClick(). events: {} is deprecated until 6.0, and an on… handler of the same event wins over it.
    • An onClick() on the submit or cancel buttons of forms and dialogs replaces their default handler: call context.submit(), context.closeForm() or context.closeDialog() from it.
    • Forms fire onSuccess() after saving, with context.isCreating, and nested forms onApply() when they apply their draft, instead of create and submit, which throw.
    • onToggle() with context.collapsed replaces onOpen() with context.open.
    • input fires for each edit in all types and bubbles like change. Returning false from a handler of either stops it from bubbling.
    • Date, datetime and time fields fire change once the edit is done.
    • Errors thrown by handlers reach onError() as they are, instead of wrapped in an AggregateError: read error.message.
  • admin: Contexts, see Contexts:
    • Contexts don't expose components anymore, e.g. component or formComponent: what callbacks used them for are context members, listed under New.
    • Renamed members: query → routeQuery, index → valueIndex, processedItem → serializedItem and processedRootItem → serializedRootItem. sourceComponent.query is context.sourceQuery.
    • dialogComponent.resolve(value) is context.closeDialog(value), formComponent.setData(data) and schemaComponent.resetData() are context.resetItem(data) and context.resetItem(), and the validation members of components are context.validate(), isValid() and clearErrors(). dialogComponent.reject(), applyCleanChanges(), filterData(), navigateToComponent() and navigateToRouteComponent() are removed.
    • context.navigate() resolves to whether the navigation happened, and getResourceUrl() without arguments throws when there is no resource.
    • Each context describes its own item, with itemDataPath, itemIndex and parentItem: in the events of forms and sections and in list buttons, 3.5 gave those of the item one level up. context.value is undefined there.
    • In panels with their own data, item is the panel's data and rootItem the page's. In dialogs, rootItem is the dialog's data.
    • onSuccess() and onError() of submit buttons get the same context as their onClick().
  • admin: Code outside of callbacks reaches the admin's services through admin.context, and its settings replace window.dito, see Admin services. closeNotifications(), rootComponent, registerLoading(), meta.api and meta.schema of routes, and api.base are removed: dito.base is the base of the routes.
  • admin: Custom components don't get DitoMixin and TypeMixin anymore: they receive schema, context and modelValue, and emit update:modelValue and change, see Custom components. The admin's own components are internal, and their actions, e.g. request() and navigate(), are context members.
  • admin: Types are defined with defineType(descriptor) instead of admin.register(), with renamed options, and processSchema(), processValue() and getTypeValidations() are setupSchema(), serializeValue() and getValidations(), see Custom types.
  • admin: TypeScript types, see TypeScript types:
    • SchemaOpen is SchemaToggle and OpenEventHandler is ToggleEventHandler. HTTPVerb (use HttpMethod), the mixin types and the types of component instances are removed.
    • Many types are renamed and keep working as deprecated aliases, e.g. DitoContext → SchemaContext, DitoGlobal → GlobalConfig and Form → FormSchema.
    • The types follow what the admin reads, and options that were missing are typed.
  • server: TypeScript types: HTTPMethod is HttpMethod, as a deprecated alias. ApiConfig has no dateFormat and resources: set resources on the admin's api. Custom keywords receive instancePath instead of dataPath. The types follow what the server reads, e.g. Authorize takes settings by action name, and options that were missing are typed.
  • admin: .dito-form-nested and DitoFormNested are .dito-form--embedded and DitoFormEmbedded, and the deprecated $pulldown-* SCSS variables are removed.
  • ui: $ease-put-quint is $ease-out-quint, .dito-trigger-disabled is .dito-trigger--disabled, and .dito-popup-{placement} is .dito-popup--{placement}.
  • ui: DitoDatePicker, DitoTimePicker and DitoDateTimePicker emit change once the edit is done, and update:modelValue for each value. The disabledDate, disabledHour, disabledMinute and disabledSecond props are removed, see UI.
  • ui: DitoButton with a verb and without text is labelled by its verb, and getVerbButtonAttributes() doesn't take hasText anymore.
  • build: @ditojs/admin and @ditojs/ui are only published as ES modules, and all packages have exports maps with import only: require() and imports of internal files, e.g. @ditojs/server/src/…, fail. defineViteConfig() doesn't read rollupOutput anymore, see Packages and builds.
  • utils: stripTags() is removed: use stripHtml(). setDataPathEntries() is setEntriesAtDataPath(), and escapeRegexp() is escapeRegExp().
  • utils, server: ** in the middle of data paths, also in the assets of models, matches zero or more levels, like in .gitignore: a/**/c matches a/c too.
  • server: Services and storages, see Services and storages:
    • Service.setup(config) is configure(config), and the application starts and stops its services itself: an overridden setup() isn't called anymore.
    • Storages are created with constructor(app, name, config) and named by their key in config.storages. app.addStorage(config, name) needs the name, and the async initialize() replaces setup().
  • server: Queries, see Queries:
    • upsert(data) saves models by the ids in the data, patching those that exist and inserting the others, instead of patching what the query matches. Its update and fetch options are removed.
    • Collection PUT and PATCH requests reject models without ids with a 400 error, and the array forms of patchAndFetch() and updateAndFetch() throw for them: create them with POST or insertAndFetch().
    • These methods and array inserts outside of PostgreSQL run in a transaction, so a failure rolls back the models saved before it.
    • Outside of PostgreSQL, arrays passed to insert() are inserted without their relations, as in PostgreSQL: use insertDitoGraph() or insertGraph() for graphs.
    • Text filters without an operator search like contains, instead of not filtering.
  • server: Validation, see Validation:
    • discriminator keywords are applied: each variant of the oneOf needs a fixed type in its properties.
    • Objects that combine parts through allOf, anyOf or oneOf without their own properties, and $refs to definitions like '#Address', reject unknown properties. Set unevaluatedProperties: true to allow them.
    • type: 'color' only accepts hex colors: use type: 'string' or type: 'object' for others.
  • server: Deprecations are removed, e.g. the scope prefixes - and # (use ! and @), and TimeStampedMixin, getDitoObject(), initialized and others are renamed, see Removed deprecations and Renames.
  • server: The admin's HMR is served through the app's server: remove server.hmr.port from the admin's Vite config.
  • admin: Dirty state and saving, see Dirty state and saving:
    • Forms are dirty once their data is edited, until it's saved, applied or loaded again: reverting an edit leaves them dirty.
    • Only writes of values that a field shows and that are saved are edits, not those of exclude fields or of sources with their own resource. Writes through item, parentItem, rootItem and value are edits; code that changes values in place calls context.markDirty().
    • Views, dialogs and panels without a button of type submit are never dirty.
    • Save responses and context.resetItem(data) merge into the data in place, so open nested forms keep their edits.
    • Leaving a form while it saves waits for the save, and submitting waits for uploads and loads without the 30 second limit of 3.5.
    • Saving the forms of tree items' children doesn't reload the tree.
  • admin: Nested forms without mutate: true edit a draft of their item, which OK merges into the parent and Cancel discards, see Nested forms. Open forms follow their item when it moves, and close when it's removed.
  • admin: Errors stay with their values when sections collapse, tabs switch and items move, and any change of a value clears them. Errors of values without a field of their own show on the field around them, and rows and tree items with errors inside are marked, see Errors.
  • admin: Cells and item labels show values as their types display them, e.g. dates, and selects keep values that their options lack, showing them as unavailable. Tabs render once they're selected.
  • admin: Object defaults of api.defaults only fill in what schemas leave out, and the create menu of lists offers forms by creatable only, not by their if or visible.
  • admin: Routes, requests and the clipboard, see Routes, requests and the clipboard:
    • The URLs of forms of lists in nested sections and inlined objects contain the container's name, e.g. /books/1/contents/notes/2, and tree items with a resource are addressed by id.
    • request({ cache: 'local' }) caches while the route is shown, and dialogs have their own local cache.
    • Pasting writes the copied values into the item, which keeps its id and position, and copied related items get new temporary ids.

New

  • admin: Context members for what callbacks used components for, e.g. isDirty, isCreating, validate(match), isValid(match), clearErrors(match), markDirty(), resetItem(data), submit(), closeForm(), closeDialog(value), showDialog() and getSchemaValue(), see New features. match takes data paths with * and **, regular expressions or functions.
  • admin: Values:
    • deserializeValue() converts stored values to edited ones, and serializeValue() returning undefined leaves out the key. Types can define both.
    • serializedItem and serializedRootItem are available in all callbacks.
  • admin: Events:
    • Handlers of bubbled events receive currentDataPath, the data path of the component whose handler runs.
    • Forms fire onCancel(), and a value returned from their onSuccess() or onError() suppresses the notification.
    • Trees fire onToggle() with context.collapsed.
  • admin: itemLabel and itemLabelText hold the label of the form's item in all callbacks inside a form, and option labels receive context.isSelected.
  • admin: Type descriptors can state rendersLabel, submitsData and hasEmbeddedForms, display values with getDisplayValue(), load options with getOptionsDataSchema(), and take an array as type.
  • admin: Custom components are typed with CustomComponentProps and CustomComponentEmits, can emit focus and blur, and receive errorsElementId.
  • admin: multiple of uploads, lines of textareas and markups, and placeholder of multiselects take callbacks, lists with wrapPrimitives support routed forms, and panels with nested: true hold their values under their name.
  • admin: The types AdminContext, AdminUser and TypeDescriptor, and the SCSS variable $error-bar-width.
  • server: ApiConfig has formats and timeout, and ApiResource has method, id and query.
  • utils: resolveDataPath(dataPath, path) resolves path relative to dataPath.

Fixes

  • admin, nested forms:
    • Applying a nested form no longer drops or serializes values twice, and cancelling it leaves no temporary ids in the parent.
    • Removing an item before an open nested form's item no longer switches the form and drops its edits.
    • Copying no longer carries other items' ids into pasted items.
  • admin, routes and resources:
    • Lists in forms shared by several views, lists with their own path, lists in nested sections, and lists with names like sourceURLs open the right forms.
    • Lists linked to views with several components get correct edit links and can create items.
    • Relative resources resolve against the nearest form or list with a resource, e.g. pages/1 instead of pages/pages/1 in trees.
  • admin, events:
    • The on… handlers of sections fire, and click events of buttons bubble through all schemas above.
    • onError() of buttons that calls context.notify() replaces the default notification.
  • admin, fields:
    • markup edits that don't blur the editor, e.g. links, are written right away, and the focus returns to the editor after the link dialog.
    • Saving with an error in a collapsed section or inlined form opens it and focuses the field.
    • rules: { name: false } removes the rule.
    • The if of components of panels with their own data only runs with that data.
  • admin, other:
    • spinner: null hides the header's spinner, and admin.el holds the mounted element.
    • showDialog() works without buttons or components.
    • creatable callbacks that return { label, query } work for lists, and the create button of sources with a single disabled form is disabled.
    • Lists with columns as an array label their items by the first column.
    • Tree items with null children sort as empty lists.
  • utils:
    • isEmpty() checks the size of Map and Set and never treats dates as empty.
    • setValueAtDataPath() errors print the path with /.
    • isCreditCard() no longer accepts numbers that only start like a card number.
    • debounceAsync() keeps working after cancel() and with immediate.
    • getEntriesAtDataPath() no longer adds a trailing slash for trailing wildcards, which wrote assets to the wrong key.
  • server, controllers:
    • authorize: false on an action or controller denies access, where it was skipped.
    • The authorize of a UsersController doesn't apply to login, logout and session, and logins of users without a password hash fail with 401 instead of 500.
    • allow: ['*'] on collection and member routes all actions.
    • { from: 'member' } parameters are left out of parameter validation.
    • Controllers that override relations keep the relation and member actions of their base.
    • CollectionController is exported.
  • server, models:
    • UserMixin hashes passwords of a custom passwordProperty, and calls the setup() of the model it extends.
    • Foreign keys that relations add to related models no longer go missing when the related model is registered first.
    • upsertAndFetch() and the array forms of patchAndFetch() and updateAndFetch() return all saved rows regardless of the default scope.
    • hooks accept arrays of handlers.
    • A JSON Schema required array on a property no longer makes the property itself required.
    • getPropertyOrRelationAtDataPath() leaves array wildcards out of relation expressions (#214).
  • server, other:
    • db:create_migration creates valid columns for type: 'asset', model types, type: 'color' and $ref properties, uses increments() only for integer keys, and names join table columns like the relations do.
    • db:seed exits with an error code when a seed fails.
    • The assets and logger options of one application no longer change the defaults of later ones.
    • Files uploaded to S3 storages without url get the S3 url.

Internal

  • The admin is written in TypeScript where it was rebuilt, and its components use setup() and composables instead of mixins.