--- 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](#library-updates) - [Breaking changes](#breaking-changes) - [Angular 20 / TypeScript 5.9](#angular-20--typescript-59) - [Knowledge Discovery removed](#knowledge-discovery-removed) - [`MaterialModule` removed](#materialmodule-removed) - [Search query-builder refactor](#search-query-builder-refactor) - [Extension auth guards are now typed](#extension-auth-guards-are-now-typed) - [Process instance model — subprocess/linked-process fields](#process-instance-model--subprocesslinked-process-fields) - [Task cloud — Runtime Bundle task fetch](#task-cloud--runtime-bundle-task-fetch) - [Multiline text widget base class](#multiline-text-widget-base-class) - [Text field default max length](#text-field-default-max-length) - [Tree and chip DOM / accessibility changes](#tree-and-chip-dom--accessibility-changes) - [New components and features](#new-components-and-features) - [Behavioural changes](#behavioural-changes) ## Library updates Update the `package.json` file with the latest library versions: ```json { "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](https://angular.dev/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: ```ts // 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` 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. ```ts // 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` / `Array` to **`Record`** / **`Array`** (`@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 | 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 `` to a real `