From 6de904b8876f920f287b63a95934c479acf78307 Mon Sep 17 00:00:00 2001 From: Kuba Sekowski Date: Fri, 20 Feb 2026 13:18:49 +0100 Subject: [PATCH 1/4] Deploying fix to the documentation (#1622) * Fix package-lock file * Docs: remove CodeSandbox embedded demos and add links to working exa,ples in Stackblitz (#1621) --- docs/guide/custom-functions.md | 10 +- docs/guide/integration-with-angular.md | 8 +- docs/guide/integration-with-react.md | 8 +- docs/guide/integration-with-svelte.md | 8 +- docs/guide/integration-with-vue.md | 10 +- package-lock.json | 224 +++++++++++++++++++++++++ 6 files changed, 231 insertions(+), 37 deletions(-) diff --git a/docs/guide/custom-functions.md b/docs/guide/custom-functions.md index d78408e61..10d1db153 100644 --- a/docs/guide/custom-functions.md +++ b/docs/guide/custom-functions.md @@ -358,18 +358,12 @@ it('returns a VALUE error if the range argument contains a string', () => { ## Working demo +Explore the full working example on [Stackblitz](https://stackblitz.com/github/handsontable/hyperformula-demos/tree/3.2.x/custom-functions?v=${$page.buildDateURIEncoded}). + This demo contains the implementation of both the [`GREET`](#add-a-simple-custom-function) and [`DOUBLE_RANGE`](#advanced-custom-function-example) custom functions. - - ## Function options You can set the following options for your function: diff --git a/docs/guide/integration-with-angular.md b/docs/guide/integration-with-angular.md index 1991fe3fb..8f78e2097 100644 --- a/docs/guide/integration-with-angular.md +++ b/docs/guide/integration-with-angular.md @@ -6,10 +6,4 @@ For more details, see the [client-side installation](client-side-installation.md ## Demo - +Explore the full working example on [Stackblitz](https://stackblitz.com/github/handsontable/hyperformula-demos/tree/3.2.x/angular-demo?v=${$page.buildDateURIEncoded}). diff --git a/docs/guide/integration-with-react.md b/docs/guide/integration-with-react.md index 75b4c64e7..d4bc7fe75 100644 --- a/docs/guide/integration-with-react.md +++ b/docs/guide/integration-with-react.md @@ -6,10 +6,4 @@ For more details, see the [client-side installation](client-side-installation.md ## Demo - +Explore the full working example on [Stackblitz](https://stackblitz.com/github/handsontable/hyperformula-demos/tree/3.2.x/react-demo?v=${$page.buildDateURIEncoded}). diff --git a/docs/guide/integration-with-svelte.md b/docs/guide/integration-with-svelte.md index 310fc8823..8b3a5f4b6 100644 --- a/docs/guide/integration-with-svelte.md +++ b/docs/guide/integration-with-svelte.md @@ -6,10 +6,4 @@ For more details, see the [client-side installation](client-side-installation.md ## Demo - +Explore the full working example on [Stackblitz](https://stackblitz.com/github/handsontable/hyperformula-demos/tree/3.2.x/svelte-demo?v=${$page.buildDateURIEncoded}). diff --git a/docs/guide/integration-with-vue.md b/docs/guide/integration-with-vue.md index 5307780c6..eaa104e0e 100644 --- a/docs/guide/integration-with-vue.md +++ b/docs/guide/integration-with-vue.md @@ -31,14 +31,8 @@ This function prevents Vue from converting the HyperFormula instance into a reac ## Demo +Explore the full working example on [Stackblitz](https://stackblitz.com/github/handsontable/hyperformula-demos/tree/3.2.x/vue-3-demo?v=${$page.buildDateURIEncoded}). + ::: tip This demo uses the [Vue 3](https://v3.vuejs.org/) framework. If you are looking for an example using Vue 2, check out the [code on GitHub](https://github.com/handsontable/hyperformula-demos/tree/2.5.x/vue-demo). ::: - - diff --git a/package-lock.json b/package-lock.json index f2d4c6f11..716278366 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10062,6 +10062,34 @@ "esbuild-windows-arm64": "0.14.7" } }, + "node_modules/esbuild-android-arm64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.7.tgz", + "integrity": "sha512-9/Q1NC4JErvsXzJKti0NHt+vzKjZOgPIjX/e6kkuCzgfT/GcO3FVBcGIv4HeJG7oMznE6KyKhvLrFgt7CdU2/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/esbuild-darwin-64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.7.tgz", + "integrity": "sha512-Z9X+3TT/Xj+JiZTVlwHj2P+8GoiSmUnGVz0YZTSt8WTbW3UKw5Pw2ucuJ8VzbD2FPy0jbIKJkko/6CMTQchShQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, "node_modules/esbuild-darwin-arm64": { "version": "0.14.7", "cpu": [ @@ -10074,6 +10102,202 @@ "darwin" ] }, + "node_modules/esbuild-freebsd-64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.7.tgz", + "integrity": "sha512-76zy5jAjPiXX/S3UvRgG85Bb0wy0zv/J2lel3KtHi4V7GUTBfhNUPt0E5bpSXJ6yMT7iThhnA5rOn+IJiUcslQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/esbuild-freebsd-arm64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.7.tgz", + "integrity": "sha512-lSlYNLiqyzd7qCN5CEOmLxn7MhnGHPcu5KuUYOG1i+t5A6q7LgBmfYC9ZHJBoYyow3u4CNu79AWHbvVLpE/VQQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/esbuild-linux-32": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.7.tgz", + "integrity": "sha512-Vk28u409wVOXqTaT6ek0TnfQG4Ty1aWWfiysIaIRERkNLhzLhUf4i+qJBN8mMuGTYOkE40F0Wkbp6m+IidOp2A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.7.tgz", + "integrity": "sha512-+Lvz6x+8OkRk3K2RtZwO+0a92jy9si9cUea5Zoru4yJ/6EQm9ENX5seZE0X9DTwk1dxJbjmLsJsd3IoowyzgVg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-arm": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.7.tgz", + "integrity": "sha512-OzpXEBogbYdcBqE4uKynuSn5YSetCvK03Qv1HcOY1VN6HmReuatjJ21dCH+YPHSpMEF0afVCnNfffvsGEkxGJQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-arm64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.7.tgz", + "integrity": "sha512-kJd5beWSqteSAW086qzCEsH6uwpi7QRIpzYWHzEYwKKu9DiG1TwIBegQJmLpPsLp4v5RAFjea0JAmAtpGtRpqg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-mips64le": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.7.tgz", + "integrity": "sha512-mFWpnDhZJmj/h7pxqn1GGDsKwRfqtV7fx6kTF5pr4PfXe8pIaTERpwcKkoCwZUkWAOmUEjMIUAvFM72A6hMZnA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-ppc64le": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.7.tgz", + "integrity": "sha512-wM7f4M0bsQXfDL4JbbYD0wsr8cC8KaQ3RPWc/fV27KdErPW7YsqshZZSjDV0kbhzwpNNdhLItfbaRT8OE8OaKA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-netbsd-64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.7.tgz", + "integrity": "sha512-J/afS7woKyzGgAL5FlgvMyqgt5wQ597lgsT+xc2yJ9/7BIyezeXutXqfh05vszy2k3kSvhLesugsxIA71WsqBw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ] + }, + "node_modules/esbuild-openbsd-64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.7.tgz", + "integrity": "sha512-7CcxgdlCD+zAPyveKoznbgr3i0Wnh0L8BDGRCjE/5UGkm5P/NQko51tuIDaYof8zbmXjjl0OIt9lSo4W7I8mrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/esbuild-sunos-64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.7.tgz", + "integrity": "sha512-GKCafP2j/KUljVC3nesw1wLFSZktb2FGCmoT1+730zIF5O6hNroo0bSEofm6ZK5mNPnLiSaiLyRB9YFgtkd5Xg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ] + }, + "node_modules/esbuild-windows-32": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.7.tgz", + "integrity": "sha512-5I1GeL/gZoUUdTPA0ws54bpYdtyeA2t6MNISalsHpY269zK8Jia/AXB3ta/KcDHv2SvNwabpImeIPXC/k0YW6A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/esbuild-windows-64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.7.tgz", + "integrity": "sha512-CIGKCFpQOSlYsLMbxt8JjxxvVw9MlF1Rz2ABLVfFyHUF5OeqHD5fPhGrCVNaVrhO8Xrm+yFmtjcZudUGr5/WYQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/esbuild-windows-arm64": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.7.tgz", + "integrity": "sha512-eOs1eSivOqN7cFiRIukEruWhaCf75V0N8P0zP7dh44LIhLl8y6/z++vv9qQVbkBm5/D7M7LfCfCTmt1f1wHOCw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/escalade": { "version": "3.2.0", "dev": true, From 9a510a2acb97c3d3490f9e3b9e961a1c4a98b9ad Mon Sep 17 00:00:00 2001 From: Kuba Sekowski Date: Wed, 15 Apr 2026 18:50:44 +0200 Subject: [PATCH 2/4] Deploy docs: AI integration landing pages (#1656) > [!NOTE] > **Low Risk** > Low risk documentation-only changes: adds new guide pages and adjusts VuePress sidebar navigation with no runtime or API impact. > > **Overview** > Adds three new AI-focused documentation pages: `ai-sdk`, `integration-with-langchain`, and `mcp-server`, describing how to use HyperFormula for deterministic spreadsheet computation in agent workflows. > > Updates the VuePress guide sidebar to surface these pages under **Integrations**, renames the section from *Framework integration* to *Integrations*, and moves the former *Overview* links into a new *About* section. > > Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 54c541b8f4f6b611dfa4bfd882630712b3523db2. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot). Co-authored-by: GreenFlux Co-authored-by: Claude Opus 4.6 --- docs/.vuepress/config.js | 27 ++++++------ docs/guide/ai-sdk.md | 51 +++++++++++++++++++++++ docs/guide/integration-with-langchain.md | 53 ++++++++++++++++++++++++ docs/guide/mcp-server.md | 44 ++++++++++++++++++++ 4 files changed, 163 insertions(+), 12 deletions(-) create mode 100644 docs/guide/ai-sdk.md create mode 100644 docs/guide/integration-with-langchain.md create mode 100644 docs/guide/mcp-server.md diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 46e040b9a..70456f0da 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -175,17 +175,6 @@ module.exports = { ['/guide/demo', 'Demo'], ] }, - { - title: 'Overview', - collapsable: false, - children: [ - ['/guide/quality', 'Quality'], - ['/guide/supported-browsers', 'Supported browsers'], - ['/guide/dependencies', 'Dependencies'], - ['/guide/licensing', 'Licensing'], - ['/guide/support', 'Support'], - ] - }, { title: 'Getting started', collapsable: false, @@ -199,13 +188,16 @@ module.exports = { ] }, { - title: 'Framework integration', + title: 'Integrations', collapsable: false, children: [ ['/guide/integration-with-react', 'Integration with React'], ['/guide/integration-with-vue', 'Integration with Vue'], ['/guide/integration-with-angular', 'Integration with Angular'], ['/guide/integration-with-svelte', 'Integration with Svelte'], + ['/guide/ai-sdk', 'HyperFormula AI SDK'], + ['/guide/integration-with-langchain', 'Integration with LangChain'], + ['/guide/mcp-server', 'HyperFormula MCP Server'], ] }, { @@ -276,6 +268,17 @@ module.exports = { ['/guide/migration-from-2.x-to-3.0', 'Migrating from 2.x to 3.0'], ] }, + { + title: 'About', + collapsable: false, + children: [ + ['/guide/quality', 'Quality'], + ['/guide/supported-browsers', 'Supported browsers'], + ['/guide/dependencies', 'Dependencies'], + ['/guide/licensing', 'Licensing'], + ['/guide/support', 'Support'], + ] + }, { title: 'Miscellaneous', collapsable: false, diff --git a/docs/guide/ai-sdk.md b/docs/guide/ai-sdk.md new file mode 100644 index 000000000..aeb93e6ca --- /dev/null +++ b/docs/guide/ai-sdk.md @@ -0,0 +1,51 @@ +# HyperFormula AI SDK + +Let LLMs safely read/write spreadsheets and compute formulas via a deterministic engine. + +## What it does + +- **Evaluate formulas on the fly** —call `calculateFormula()` to evaluate any Excel-compatible formula without placing it in a cell. +- **Read and write cells and ranges** —get or set individual cells and multi-cell ranges so an LLM can inspect, populate, or modify sheet data programmatically. +- **Trace dependencies** —call `getCellDependents()` and `getCellPrecedents()` to understand which cells feed into a formula and what downstream values would change. + +## Quickstart + +```js +import HyperFormula from 'hyperformula'; +import { createSpreadsheetTools } from 'hyperformula/ai'; + +// 1. Create a HyperFormula instance with initial data +const hf = HyperFormula.buildFromArray([ + ['Revenue', 100], + ['Cost', 60], + ['Profit', '=B1-B2'], +]); + +// 2. Create tools your LLM agent can call +const tools = createSpreadsheetTools(hf); + +// 3. Agent interaction examples +tools.evaluate({ formula: '=IRR({-1000,300,400,500,200})' }); +// → 0.1189 — deterministic, no LLM math + +tools.setCellContents({ sheet: 0, col: 1, row: 0, value: 200 }); +tools.getRange({ sheet: 0, startCol: 0, startRow: 0, endCol: 1, endRow: 2 }); +// → [['Revenue', 200], ['Cost', 60], ['Profit', 140]] + +// Agent: "What drives the profit number?" +tools.getDependents({ sheet: 0, col: 1, row: 0 }); +// → [{ sheet: 0, col: 1, row: 2 }] — Revenue flows into Profit +``` + +## Use cases + +- **Explain a sheet** —ask an agent to summarize what a spreadsheet does, which cells are inputs, and how outputs are derived. +- **Generate a what-if scenario** —let the model tweak assumptions (price, volume, rate) and observe how results change in real time. +- **Validate and clean data** —have the agent scan ranges for errors, missing values, or inconsistencies and fix them with formulas or direct edits. +- **Create formulas from natural language** —describe a calculation in plain English and let the model write and verify the correct Excel formula. + +## Beta access + +::: tip +[Sign up for beta access](https://2fmjvg.share-eu1.hsforms.com/2e6drCkuLTn-1RuiYB91eJA) +::: diff --git a/docs/guide/integration-with-langchain.md b/docs/guide/integration-with-langchain.md new file mode 100644 index 000000000..cbb695dcd --- /dev/null +++ b/docs/guide/integration-with-langchain.md @@ -0,0 +1,53 @@ +# Integration with LangChain/LangGraph + +A LangChain/LangGraph tool that gives AI agents deterministic, Excel-compatible formula evaluation instead of relying on LLM-generated math. + +## What it does + +**Without HyperFormula:** + +```python +result = llm.invoke( + "Calculate the IRR for these cash flows: [-1000, 300, 400, 500, 200]" +) +# "The IRR is approximately 12.4%" ← non-deterministic, unverifiable +``` + +**With HyperFormula tool:** + +```python +from langchain_core.tools import tool +from hyperformula import HyperFormula + +hf = HyperFormula.build_from_array([[-1000, 300, 400, 500, 200]]) + +@tool +def evaluate_formula(formula: str) -> str: + """Evaluate an Excel-compatible formula using HyperFormula.""" + return hf.calculate_formula(formula, sheet_id=0) + +agent = create_react_agent(llm, [evaluate_formula]) + +# Agent calls: evaluate_formula("=IRR(A1:E1)") +# → 0.1189 ← deterministic, auditable +``` + +## How it works + +1. **Agent populates a HyperFormula sheet** —writes data and formulas (`=SUM`, `=IF`, `=VLOOKUP`, etc.) into cells. +2. **HyperFormula evaluates deterministically** —resolves the full dependency graph using 400+ built-in functions. No LLM in the loop for math. +3. **Agent continues with verified data** —computed values flow back into the chain for reasoning, reporting, or downstream actions. + +## Use cases + +- Financial modeling (NPV, IRR, amortization) +- Data transformation and aggregation (SUMIF, VLOOKUP) +- Dynamic pricing with formula-defined logic +- What-if scenarios and forecasting +- Report generation with verified KPIs + +## Beta access + +::: tip +[Sign up for beta access](https://2fmjvg.share-eu1.hsforms.com/2e6drCkuLTn-1RuiYB91eJA) +::: diff --git a/docs/guide/mcp-server.md b/docs/guide/mcp-server.md new file mode 100644 index 000000000..618323f22 --- /dev/null +++ b/docs/guide/mcp-server.md @@ -0,0 +1,44 @@ +# HyperFormula MCP Server + +An MCP (Model Context Protocol) server that exposes HyperFormula as a tool for any MCP-compatible AI client, giving LLMs deterministic spreadsheet computation. + +## What it does + +- **Evaluate formulas** —any MCP client can call HyperFormula to evaluate Excel-compatible formulas and get exact results. +- **Read and write cells** —get or set individual cell values and ranges through standard MCP tool calls. +- **Inspect dependencies** —trace which cells a formula depends on and understand the calculation graph. + +**Without HyperFormula:** + +``` +User: What's the NPV at 8% for these cash flows? +Agent: "Approximately $142.50" ← non-deterministic, unverifiable +``` + +**With HyperFormula MCP server:** + +``` +User: What's the NPV at 8% for these cash flows? +Agent → tool call: evaluate("=NPV(0.08, B1:B5)") +Agent: "$138.43" ← deterministic, auditable +``` + +## How it works + +1. **Start the MCP server** —runs HyperFormula as a local MCP server that any compatible client (Claude Desktop, Cursor, VS Code, etc.) can connect to. +2. **Client sends tool calls** —the AI client calls tools like `evaluate`, `getCellValue`, and `setCellContents` via the MCP protocol. +3. **HyperFormula evaluates deterministically** —resolves formulas using 400+ built-in functions with full dependency tracking. No LLM in the loop for math. +4. **Results flow back to the client** —computed values return through MCP, grounding the AI's response in verified numbers. + +## Use cases + +- Spreadsheet Q&A in Claude Desktop or other MCP clients +- Formula evaluation in IDE-based AI assistants +- Financial calculations in chat-based agent workflows +- Data validation and transformation via natural language + +## Beta access + +::: tip +[Sign up for beta access](https://2fmjvg.share-eu1.hsforms.com/2e6drCkuLTn-1RuiYB91eJA) +::: From 3cff8a37a170cfb7c25020de0d74cb237be6f01e Mon Sep 17 00:00:00 2001 From: "krzysztof.zielinski" Date: Tue, 9 Jun 2026 11:00:04 +0200 Subject: [PATCH 3/4] HF-238 - Review \"Integration with Angular\" guide and demo --- docs/guide/integration-with-angular.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/docs/guide/integration-with-angular.md b/docs/guide/integration-with-angular.md index 491908f84..bd2829511 100644 --- a/docs/guide/integration-with-angular.md +++ b/docs/guide/integration-with-angular.md @@ -45,17 +45,20 @@ export class SpreadsheetService { } ``` -Consume the service from a component and bind `values$ | async` in the template. Declare the component in your `AppModule` alongside `CommonModule`: +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`: ```typescript // spreadsheet.component.ts import { Component } from '@angular/core'; +import { CommonModule } from '@angular/common'; import { Observable } from 'rxjs'; import { SpreadsheetService } from './spreadsheet.service'; import { type CellValue } from 'hyperformula'; @Component({ selector: 'app-spreadsheet', + standalone: true, + imports: [CommonModule], templateUrl: './spreadsheet.component.html', }) export class SpreadsheetComponent { @@ -90,6 +93,21 @@ export class SpreadsheetComponent { ## Notes +### Older, `NgModule`-based apps + +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, then declare it in your module and import `CommonModule` there instead: + +```typescript +// app.module.ts +@NgModule({ + declarations: [SpreadsheetComponent], + imports: [BrowserModule, CommonModule], +}) +export class AppModule {} +``` + +The service and template above are unchanged — only the way the component is wired up differs. + ### Provider scope `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. @@ -125,3 +143,5 @@ The service above is already SSR-safe — HyperFormula has no browser-only API d ## Demo For a more advanced example, check out the Angular demo on Stackblitz. + +The demo is written with modern Angular standards — standalone components, signals, the new control flow (`@if` / `@for`) and zoneless change detection — so it may differ stylistically from the `BehaviorSubject` + `async` pipe approach shown above, which is kept deliberately version-agnostic. From 7a36f99b13110b9c7f0f5cae490783abde311dec Mon Sep 17 00:00:00 2001 From: "krzysztof.zielinski" Date: Tue, 9 Jun 2026 14:13:05 +0200 Subject: [PATCH 4/4] HF-238 - Review \"Integration with Angular\" guide and demo --- docs/guide/integration-with-angular.md | 120 +++++++++++++++++++++++-- 1 file changed, 111 insertions(+), 9 deletions(-) diff --git a/docs/guide/integration-with-angular.md b/docs/guide/integration-with-angular.md index bd2829511..09b62cacb 100644 --- a/docs/guide/integration-with-angular.md +++ b/docs/guide/integration-with-angular.md @@ -4,9 +4,111 @@ The HyperFormula API is identical in an Angular app and in plain JavaScript. Thi Install with `npm install hyperformula`. For other options, see the [client-side installation](client-side-installation.md) section. -## Basic usage +## Basic usage (modern Angular) -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. +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. + +```typescript +// spreadsheet.service.ts +import { Injectable, signal } from '@angular/core'; +import { HyperFormula, type CellValue } from 'hyperformula'; + +@Injectable({ providedIn: 'root' }) +export class SpreadsheetService { + private readonly hf: HyperFormula; + + private readonly _values = signal([]); + readonly values = this._values.asReadonly(); + + constructor() { + this.hf = HyperFormula.buildFromArray( + [ + [1, 4, '=A1+B1'], + // your data rows go here + ], + { + licenseKey: 'gpl-v3', + // more configuration options go here + } + ); + this._values.set(this.hf.getSheetValues(0)); + } + + calculate() { + this._values.set(this.hf.getSheetValues(0)); + } + + reset() { + this._values.set([]); + } +} +``` + +Inject the service with `inject()`, expose its signal, and use `OnPush` change detection: + +```typescript +// spreadsheet.component.ts +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; +import { SpreadsheetService } from './spreadsheet.service'; + +@Component({ + selector: 'app-spreadsheet', + templateUrl: './spreadsheet.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class SpreadsheetComponent { + private readonly spreadsheetService = inject(SpreadsheetService); + + readonly values = this.spreadsheetService.values; + + runCalculations() { + this.spreadsheetService.calculate(); + } + + reset() { + this.spreadsheetService.reset(); + } +} +``` + +Read the signal in the template with the new control flow: + +```html + + + +@if (values().length) { + + @for (row of values(); track $index) { + + @for (cell of row; track $index) { + + } + + } +
{{ cell }}
+} +``` + +Bootstrap the app with zoneless change detection: + +```typescript +// main.ts +import { provideZonelessChangeDetection } from '@angular/core'; +import { bootstrapApplication } from '@angular/platform-browser'; +import { AppComponent } from './app/app.component'; + +bootstrapApplication(AppComponent, { + providers: [provideZonelessChangeDetection()], +}).catch((err) => console.error(err)); +``` + +> Signals require Angular 16+, the new control flow Angular 17+, and `provideZonelessChangeDetection` Angular 20+. For earlier versions, use the `BehaviorSubject` approach below. + + +## Basic usage (older Angular versions) + +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. ```typescript // spreadsheet.service.ts @@ -24,7 +126,7 @@ export class SpreadsheetService { constructor() { this.hf = HyperFormula.buildFromArray( [ - [1, 2, '=A1+B1'], + [1, 4, '=A1+B1'], // your data rows go here ], { @@ -91,11 +193,9 @@ export class SpreadsheetComponent { ``` -## Notes - -### Older, `NgModule`-based apps +### `NgModule`-based apps (Angular 13 and older) -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, then declare it in your module and import `CommonModule` there instead: +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: ```typescript // app.module.ts @@ -108,8 +208,12 @@ export class AppModule {} The service and template above are unchanged — only the way the component is wired up differs. +## Notes + ### Provider scope +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`. + `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. ### Cleanup @@ -143,5 +247,3 @@ The service above is already SSR-safe — HyperFormula has no browser-only API d ## Demo For a more advanced example, check out the Angular demo on Stackblitz. - -The demo is written with modern Angular standards — standalone components, signals, the new control flow (`@if` / `@for`) and zoneless change detection — so it may differ stylistically from the `BehaviorSubject` + `async` pipe approach shown above, which is kept deliberately version-agnostic.