Skip to content

Commit ec35751

Browse files
committed
Merge develop into docs/astro-migration
Brings in one content-only commit that landed on develop: - d4195b5 HF-238 - Review "Integration with Angular" guide and demo (#1691) It touches `docs/guide/integration-with-angular.md`, which this PR migrated to `docs/src/content/docs/guide/integration-with-angular.md`. Git's rename detection auto-applied the develop-side edits to the new location — no conflicts. Pushes the merge so GitHub's `mergeable: CONFLICTING` flag recomputes.
2 parents 0509e26 + d4195b5 commit ec35751

1 file changed

Lines changed: 126 additions & 4 deletions

File tree

docs/src/content/docs/guide/integration-with-angular.md

Lines changed: 126 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,111 @@ The HyperFormula API is identical in an Angular app and in plain JavaScript. Thi
77

88
Install with `npm install hyperformula`. For other options, see the [client-side installation](/docs/guide/client-side-installation) section.
99

10-
## Basic usage
10+
## Basic usage (modern Angular)
1111

12-
Wrap the engine in an `@Injectable` service backed by a `BehaviorSubject`. Components subscribe to the observable with the `async` pipe, which handles subscription cleanup automatically.
12+
For modern Angular (v20+) we recommend a service that exposes a **signal**, **standalone** components, the new control flow (`@if` / `@for`) and **zoneless** change detection. Wrap the engine in an `@Injectable` service and expose its values as a read-only signal; the template reads the signal directly and Angular refreshes the view whenever it changes.
13+
14+
```typescript
15+
// spreadsheet.service.ts
16+
import { Injectable, signal } from '@angular/core';
17+
import { HyperFormula, type CellValue } from 'hyperformula';
18+
19+
@Injectable({ providedIn: 'root' })
20+
export class SpreadsheetService {
21+
private readonly hf: HyperFormula;
22+
23+
private readonly _values = signal<CellValue[][]>([]);
24+
readonly values = this._values.asReadonly();
25+
26+
constructor() {
27+
this.hf = HyperFormula.buildFromArray(
28+
[
29+
[1, 4, '=A1+B1'],
30+
// your data rows go here
31+
],
32+
{
33+
licenseKey: 'gpl-v3',
34+
// more configuration options go here
35+
}
36+
);
37+
this._values.set(this.hf.getSheetValues(0));
38+
}
39+
40+
calculate() {
41+
this._values.set(this.hf.getSheetValues(0));
42+
}
43+
44+
reset() {
45+
this._values.set([]);
46+
}
47+
}
48+
```
49+
50+
Inject the service with `inject()`, expose its signal, and use `OnPush` change detection:
51+
52+
```typescript
53+
// spreadsheet.component.ts
54+
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
55+
import { SpreadsheetService } from './spreadsheet.service';
56+
57+
@Component({
58+
selector: 'app-spreadsheet',
59+
templateUrl: './spreadsheet.component.html',
60+
changeDetection: ChangeDetectionStrategy.OnPush,
61+
})
62+
export class SpreadsheetComponent {
63+
private readonly spreadsheetService = inject(SpreadsheetService);
64+
65+
readonly values = this.spreadsheetService.values;
66+
67+
runCalculations() {
68+
this.spreadsheetService.calculate();
69+
}
70+
71+
reset() {
72+
this.spreadsheetService.reset();
73+
}
74+
}
75+
```
76+
77+
Read the signal in the template with the new control flow:
78+
79+
```html
80+
<!-- spreadsheet.component.html -->
81+
<button (click)="runCalculations()">Run calculations</button>
82+
<button (click)="reset()">Reset</button>
83+
@if (values().length) {
84+
<table>
85+
@for (row of values(); track $index) {
86+
<tr>
87+
@for (cell of row; track $index) {
88+
<td>{{ cell }}</td>
89+
}
90+
</tr>
91+
}
92+
</table>
93+
}
94+
```
95+
96+
Bootstrap the app with zoneless change detection:
97+
98+
```typescript
99+
// main.ts
100+
import { provideZonelessChangeDetection } from '@angular/core';
101+
import { bootstrapApplication } from '@angular/platform-browser';
102+
import { AppComponent } from './app/app.component';
103+
104+
bootstrapApplication(AppComponent, {
105+
providers: [provideZonelessChangeDetection()],
106+
}).catch((err) => console.error(err));
107+
```
108+
109+
> Signals require Angular 16+, the new control flow Angular 17+, and `provideZonelessChangeDetection` Angular 20+. For earlier versions, use the `BehaviorSubject` approach below.
110+
111+
112+
## Basic usage (older Angular versions)
113+
114+
For broader compatibility — including Angular versions without signals or zoneless change detection — wrap the engine in an `@Injectable` service backed by a `BehaviorSubject`. Components subscribe to the observable with the `async` pipe, which handles subscription cleanup automatically.
13115

14116
```typescript
15117
// spreadsheet.service.ts
@@ -27,7 +129,7 @@ export class SpreadsheetService {
27129
constructor() {
28130
this.hf = HyperFormula.buildFromArray(
29131
[
30-
[1, 2, '=A1+B1'],
132+
[1, 4, '=A1+B1'],
31133
// your data rows go here
32134
],
33135
{
@@ -48,17 +150,20 @@ export class SpreadsheetService {
48150
}
49151
```
50152

51-
Consume the service from a component and bind `values$ | async` in the template. Declare the component in your `AppModule` alongside `CommonModule`:
153+
Consume the service from a component and bind `values$ | async` in the template. The component below is **standalone** (the default since Angular 17) and imports `CommonModule` directly, so it works without an `NgModule`. The structural directives `*ngIf` / `*ngFor` and the `async` pipe all come from `CommonModule`:
52154

53155
```typescript
54156
// spreadsheet.component.ts
55157
import { Component } from '@angular/core';
158+
import { CommonModule } from '@angular/common';
56159
import { Observable } from 'rxjs';
57160
import { SpreadsheetService } from './spreadsheet.service';
58161
import { type CellValue } from 'hyperformula';
59162

60163
@Component({
61164
selector: 'app-spreadsheet',
165+
standalone: true,
166+
imports: [CommonModule],
62167
templateUrl: './spreadsheet.component.html',
63168
})
64169
export class SpreadsheetComponent {
@@ -91,10 +196,27 @@ export class SpreadsheetComponent {
91196
</ng-container>
92197
```
93198

199+
### `NgModule`-based apps (Angular 13 and older)
200+
201+
Standalone components require Angular 14 or newer. If your project still uses `NgModule`s (or targets Angular 13 or older), drop the `standalone: true` and `imports` fields from the component above, then declare it in your module and import `CommonModule` there instead:
202+
203+
```typescript
204+
// app.module.ts
205+
@NgModule({
206+
declarations: [SpreadsheetComponent],
207+
imports: [BrowserModule, CommonModule],
208+
})
209+
export class AppModule {}
210+
```
211+
212+
The service and template above are unchanged — only the way the component is wired up differs.
213+
94214
## Notes
95215

96216
### Provider scope
97217

218+
The notes below apply to both the modern and older approaches — provider scope and cleanup depend on how the service is registered, not on whether it exposes a signal or a `BehaviorSubject`.
219+
98220
`providedIn: 'root'` makes the service an application-wide singleton — suitable when a single HyperFormula instance is shared across the app. For per-feature or per-component instances (for example, several independent reports on one screen), provide the service at the component level via `providers: [SpreadsheetService]`; the service is then created and destroyed alongside the component.
99221

100222
### Cleanup

0 commit comments

Comments
 (0)