Files
alfresco-ng2-components/docs/upgrade-guide/upgrade850-900.md
T
Michal Kinas 5712f2742c [ACS-12451] Add missing ADF upgrade guides (#12138)
* [ACS-12451] Add missing ADF upgrade guides

* [ACS-12451] CR fix

* [ACS-12451] CR fixes
2026-08-11 11:49:30 +02:00

19 KiB

Title
Title
Upgrading from ADF v8.5 to v9.0

Upgrading from ADF v8.5 to v9.0

This guide provides instructions on how to upgrade your v8.5.0 ADF projects to v9.0.0.

Before you begin

Always perform upgrades on a "clean" project state, back up your changes or make a project backup. Because this release bumps the Angular major, budget time to run the Angular 20 ng update migrations on your own app, rebuild, and re-test. Expect to install with --legacy-peer-deps (see below).

Contents

Library updates

Update the package.json file with the latest library versions:

{
    "dependencies": {
        "@alfresco/adf-core": "9.0.0",
        "@alfresco/adf-content-services": "9.0.0",
        "@alfresco/adf-process-services": "9.0.0",
        "@alfresco/adf-process-services-cloud": "9.0.0",
        "@alfresco/adf-insights": "9.0.0",
        "@alfresco/adf-extensions": "9.0.0",
        "@alfresco/js-api": ">=10.0.0",
        "@ngx-translate/core": ">=17.0.0"
    }
}

Bump your Angular platform to 20.x in lockstep (@angular/core/@angular/material/@angular/cdk 20.x, typescript 5.9). Clean node_modules and package-lock.json, then npm install.

Breaking changes

Angular 20 / TypeScript 5.9

ADF 9.0.0 is built against Angular 20 and must be consumed by an Angular 20 app — there is no cross-version support with Angular 19.

Package v8.5.0 v9.0.0
@angular/core, @angular/common, … 19.2.x 20.3.x
@angular/material, @angular/cdk 19.2.x 20.2.x
@angular/material-date-fns-adapter 19.2.x 20.2.x
typescript 5.8.3 5.9.3
ng-packagr 19.2.x 20.3.x
@angular-eslint/* 19.3.0 20.7.0
@typescript-eslint/* 6.x 8.x
zone.js 0.15.0 0.15.0 (unchanged)
rxjs 7.8.2 7.8.2 (unchanged)
nx 22.x 22.x (unchanged)
  • Run the Angular 20 update on your own app (ng update @angular/core@20 @angular/cdk@20 @angular/material@20) and move to TypeScript 5.9. Follow the official Angular update guide.
  • Expect npm install --legacy-peer-deps. A transitive dependency (@mat-datetimepicker/core) still declares an Angular-19 CDK peer range while ADF ships CDK 20, so npm reports a peer conflict without the flag.
  • ESLint: @typescript-eslint/brace-style was removed in @typescript-eslint v8 — drop it from any shared config that inherited it from ADF. Angular 20 also newly recommends @angular-eslint/prefer-inject (constructor injection → inject()); this surfaces as warnings only.
  • CDK PortalInjector removed — Angular CDK 20 removed PortalInjector. ADF replaced its internal usage with Injector.create(); if your own code imported PortalInjector from @angular/cdk/portal, make the same swap.

Knowledge Discovery removed

The Knowledge Discovery / Search-AI / Knowledge Retrieval feature (the HxI-connector agent-based AI query feature originally added in 7.0.0) was removed entirely from both @alfresco/adf-content-services and @alfresco/js-api. There is no replacement — remove all usages.

Removed from @alfresco/adf-content-services (the agent, search-ai and prediction barrels were deleted):

Removed Kind
AgentService Service
SearchAiService Service
SearchAiInputState Interface
PredictionService Service

Removed from @alfresco/js-api:

  • content-rest-api: AgentsApi, SearchAiApi; models Agent, AgentEntry, AgentPaging, AgentPagingList, AiAnswer, AiAnswerEntry, AiAnswerReference, AiAnswerObjectReference, KnowledgeRetrievalConfig, KnowledgeRetrievalConfigEntry, QuestionModel, QuestionRequest, RestrictionQuery.
  • hxi-connector-api (entire secondary API removed): PredictionsApi, Prediction, PredictionEntry, PredictionPaging, PredictionPagingList, and the UpdateType / ReviewStatus types.
  • AlfrescoApi.hxiConnectorClient — the client property (and its config/auth wiring) was removed from AlfrescoApi / AlfrescoApiType.

MaterialModule removed

The deprecated MaterialModule re-export barrel was removed from both @alfresco/adf-core and @alfresco/adf-content-services, and is no longer re-exported by CoreModule / ContentModule.

Import the specific @angular/material/* modules you actually use directly from Angular Material:

// Before
import { MaterialModule } from '@alfresco/adf-core'; // (or from @alfresco/adf-content-services)

// After — import only what you use, from Angular Material
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
// …etc

Apps that were transitively relying on Material modules via ADF's module exports must now import each Material module explicitly.

Search query-builder refactor

BaseQueryBuilderService (the base of SearchQueryBuilderService and SearchHeaderQueryBuilderService) was refactored to parse the user query on demand and to support two query modes. This removes the old "emit updated → subscriber calls execute()" indirection:

  • The updated Subject<SearchRequest> was removed. Code subscribing to queryBuilder.updated must instead react to queryBuilder.execute() results (executed / the returned SearchRequest).
  • The update(queryBody?) method was removed. Call execute() directly.
// Before
this.queryBuilder.updated.subscribe((query) => { /* … */ });
this.queryBuilder.update();

// After
this.queryBuilder.execute(); // executes and emits results directly
  • The userQuery setter no longer trims and parenthesizes. Previously userQuery = 'foo' stored '(foo)'; now it stores the raw value 'foo' and derives the compiled query via the new read-only parsedQuery getter. Read parsedQuery where you previously read the wrapped userQuery.
  • New searchMode: 'regular' | 'formula' (default 'regular'). In regular mode a user term is expanded across the configured fields (search.app:fields, default ["cm:name"]); in formula mode the raw input is used verbatim as an AFTS expression.
  • New read-only wildcardsEnabled — driven by the new search-wildcards-enabled app-config flag (default true). When false, terms match exactly (no trailing *) and the search-text widget's searchPrefix/searchSuffix are not applied.
  • updateSelectedConfiguration(id) gained two optional params — updateSelectedConfiguration(id, resetFilters = true, shouldExecute = true) — non-breaking for existing single-argument callers.
  • SearchTextComponent.enableChangeUpdate now defaults to false (was true).

execute(updateQueryParams = true, queryBody?) keeps the same signature as in 8.5.0.

Extension auth guards are now typed

ExtensionService.authGuards and the setAuthGuards() / getAuthGuards() signatures were narrowed from Record<string, unknown> / Array<unknown> to Record<string, CanActivateFn> / Array<CanActivateFn> (@angular/router) in @alfresco/adf-extensions. Code that registered auth guards with a looser type now gets a compile error — register CanActivateFn guards.

Process instance model — subprocess/linked-process fields

ProcessInstanceCloud replaced its two related-instance arrays with counts:

Before (8.5.0) After (9.0.0)
linkedProcesses?: RelatedProcessInstance[] linkedProcessesCount?: number
subprocesses?: RelatedProcessInstance[] subprocessesCount?: number

Code reading processInstance.linkedProcesses / processInstance.subprocesses must switch to the count fields (the full related-instance collections are no longer carried on the model).

Task cloud — Runtime Bundle task fetch

TaskCloudService.getTaskById dropped its third service argument:

  • Before: getTaskById(appName, taskId, service: 'query' | 'rb' = 'query')
  • After: getTaskById(appName, taskId)

Endpoint selection is now controlled by the new ADF_TASK_RUNTIME_BUNDLE_FALLBACK_ENABLED injection token (InjectionToken<Observable<boolean> | boolean>, from @alfresco/adf-process-services-cloud). When it resolves truthy, getTaskById / FormCloudService.getTask read active tasks from the always-current Runtime Bundle endpoint and transparently fall back to the Query Service on HTTP 404 (e.g. completed/archived tasks). Default (token absent) is unchanged — Query Service only. Any caller passing 'rb'/'query' must drop the argument and provide the token instead. The feature also adds two public exports: the TaskDetailsCloudModelRuntimeBundle interface and the resolveTaskRuntimeBundleFallback$(token) helper.

Multiline text widget base class

MultilineTextWidgetComponentComponent now extends WidgetComponent (previously FormattableTextWidgetComponent), implements OnInit, and renders validation errors via Material mat-error (an errorStateMatcher + translateParameters) instead of the shared ErrorWidgetComponent. Custom widgets that subclassed it and relied on FormattableTextWidgetComponent members must adapt. Many other widgets were migrated to the same mat-error rendering in the same change — the core AmountWidgetComponent, DecimalWidgetComponent, NumberWidgetComponent; the cloud dropdown/date/date-time/display-external-property widgets; the process-services (non-cloud) dropdown/typeahead/functional-group/people widgets; TagActionsComponent; and the StartTaskComponent date field. Custom CSS targeting the old ErrorWidgetComponent markup for any of these widgets may need updating.

Text field default max length

The TEXT field now enforces a default maximum length of 1024 characters when the field defines no explicit maxLength, and oversized paste is blocked:

  • New exported constant DEFAULT_TEXT_MAX_LENGTH = 1024 (@alfresco/adf-core).
  • FORM_FIELD_VALIDATORS now applies MaxLengthFieldValidator to TEXT with a 1024 fallback (and a separate uncapped validator for MULTILINE_TEXT); MaxLengthFieldValidator's constructor gained a 3rd fallbackMaxLength? param.
  • Pasting text that would exceed the resolved max length is now prevented, the field is marked touched, and a FORM.FIELD.VALIDATOR.NO_LONGER_THAN error shows.

TEXT fields that previously relied on unlimited length now cap at 1024 unless the field itself defines a maxLength.

Tree and chip DOM / accessibility changes

Keyboard-accessibility rework changed the DOM of a few components (no @Input/@Output were removed, but markup, CSS selectors and automation ids changed — update tests/CSS that target the old markup):

  • TreeComponent (adf-tree) — the expand/collapse chevron button was removed from the tab order (tabindex="-1", aria-hidden="true") and the label span is no longer interactive (lost role="button" / tabindex="0"). Keyboard users now operate the row: Enter expands/collapses, Space selects/toggles. Rows gained aria-label/aria-selected; selection is announced via LiveAnnouncer (new ADF-TREE.ARIA.SELECTED / DESELECTED keys).
  • DynamicChipListComponent — the chip delete affordance changed from a <mat-icon matChipRemove> to a real <button class="adf-dynamic-chip-list-delete-btn" data-automation-id="adf-dynamic-chip-list-delete-btn-<id>">. The old .adf-dynamic-chip-list-delete-icon element / adf-dynamic-chip-list-delete-{name} id are gone (@Output() removedChip is unchanged; it now emits from the button click). A new public focusDeleteButton(index) method was added, and a new DYNAMIC_CHIP_LIST.DELETE i18n key.

New components and features

  • Session timeout (opt-in, @alfresco/adf-core) — a new subsystem that tracks user idle activity, shows a countdown "Are you still working?" dialog, and logs out on timeout, synchronised across browser tabs via BroadcastChannel. Enable it with the new provideSessionTimeout(options?) provider and/or a sessionTimeout block in app.config.json:
    { "sessionTimeout": { "enabled": true, "idleTimeoutMs": 1800000, "dialogTimeoutMs": 60000 } }
    
    New exports include SessionTimeoutService, SessionTimeoutDialogComponent (adf-session-timeout-dialog), IdleActivityTracker, SessionTimeoutSyncChannel, the SESSION_TIMEOUT_OPTIONS token, the SESSION_TIMEOUT_CONFIG_KEY / DEFAULT_SESSION_TIMEOUT_OPTIONS constants, and the SessionTimeoutOptions interface. Defaults: 30-minute idle timeout, 60-second warning dialog. New SESSION_TIMEOUT.* i18n keys.
  • Clock-drift-tolerant token expiry (opt-in) — a new oauth2.timeSync app-config flag makes OAuth token expiry checks use a server-time-corrected clock (to avoid false logouts on VMs/Citrix with drifted clocks). Backed by a reworked TimeSyncService (getCorrectedNow()) and a new exported TimeSyncDateTimeProvider; OauthConfigModel gained optional timeSync?: boolean and showDebugInformation?: boolean fields, and a new top-level serverTimeUrl app-config key (AppConfigValues.SERVER_TIME_URL) points at the endpoint used to read server time. Off by default — behaviour is unchanged unless oauth2.timeSync is enabled.
  • Type-aware form field value adapter — a new root-provided FormFieldValueAdapterService (register/hasAdapter/adapt, with the exported FormFieldValueAdapter type) in @alfresco/adf-core, the inbound counterpart to 8.5.0's FormFieldValueFormatterService. Both are gated by the existing ADF_TYPED_VALUE_FORMATTING_ENABLED token. A companion ReactivePreselectionService now backs the cloud People/Group widgets' preselection.
  • Repeatable-section row-count event — a new 'onRowCountChanged' form-rules event is emitted (via FormService.formRulesEvent) when a repeatable section adds/removes a row, driven by the new FormModel.onRepeatableSectionRowCountChanged(sectionField). The FormValidationService interface gained an optional formRulesEvent?: Subject<FormRulesEvent>.
  • Dropdown/radio labels in display-text expressions — when typed-value formatting is enabled, ${field.x} expressions now resolve dropdown/radio values to their option label instead of the raw id. New FormFieldTypes.DISPLAY_TEXT_TYPES / FormFieldTypes.isDisplayTextType() helpers.
  • Multiline text auto-grow — the multiline text widget grows unbounded by default; setting the field param autoGrow: false caps its height (scrollable). Driven by field.params.autoGrow (no new @Input).
  • Silent document-list reloadDocumentListService.reloadSilently() / reloadSilently$ reload the list without resetting the current selection (used to keep the context menu open during bulk upload).
  • UrlService blob helpers — new public createObjectUrl(blob) and trustUrl(url) methods (the existing createTrustedUrl(blob) is now composed from them).
  • CustomResourcesService.getRecentFiles gained an optional 4th includeFields: string[] = [] param (forwarded from loadFolderByNodeId for -recent-), so extra fields (e.g. isFavorite) can be requested.
  • Version list now shows the version's modified-by user (modifiedByUser.displayName) and the modified date and time (date: 'medium').

Behavioural changes

Area Change
Auth — OIDC OidcAuthenticationService.reset() now calls oauthService.logOut(true) (terminates the OAuth session) instead of reloading the IDP configuration — supports the session-timeout logout redirect.
Search — node selector ContentNodeSelectorPanelComponent no longer leaves stale ANCESTOR: filters when switching sites/clearing the search; filtering now correctly scopes to the chosen site.
Forms — people widget PeopleWidgetComponent (process-services) now debounces its user search by 300 ms instead of querying on every keystroke.
Forms — spinner The Automate-form spinner overlay is now fully disposed on host destroy, so it no longer persists outside the form.
Forms — task fetch Task claim/unclaim status is evaluated against the always-current Runtime Bundle when the RB-fallback token is enabled (fixes stale block-task claim status).
Uploads FetchHttpClient now converts a Node ReadStream/Buffer to a Blob (with filename) before appending to FormData, fixing broken multipart uploads under the fetch client.
Uploads The form multi-file attachment viewer now updates when a different file is selected; the "upload new version" button re-enables after use; bulk upload no longer collapses the context menu.
Viewer Firefox-headless race fixed — PDF blob/MIME state is assigned atomically and MIME types are normalised (charset params stripped).
Accessibility — tags Tag delete controls are now real keyboard-operable buttons; focus is restored sensibly after a tag is removed, and blank/whitespace tags are rejected.