Shared models
C# properties generally use PascalCase; Dart uses lowerCamelCase. Replace input collections when changing data.
| Model | Contract |
|---|---|
| NgSelectionRules | Validates ButtonGroup options and selected IDs; Toggle returns a new selection in item order. See ButtonGroup. |
| NgMenuItem | ID, text, disabled, visible, separator and read-only children; max 3 levels / 100 nodes. See Menu. |
| NgStep | Label, Disabled, Optional and nullable Valid; see Stepper and Wizard. |
| NgPromptResponse | Immutable Id, Prompt, Text; IDs increase within a session. |
| NgPromptFeedback | Response and Helpful: true, false or null to clear a vote. |
| NgPromptSession | Request state and the latest 20 responses; cancellation rejects late results. See AI Prompt. |
| NgOption | Unique key, label, disabled flag. C#: new NgOption(value,label,disabled); Dart: NgOption(value,label,disabled:true). |
| NgTab | Label and RenderFragment (Blazor) or Widget (Flutter) content. |
| NgDateRangePreset | Id/id, Label/label, ordered Start/start and End/end dates, optional Disabled/disabled. Unique IDs per picker. Bounds and disabled-date rules apply to endpoints. |
| NgDateRange | Nullable start/end: DateOnly in Blazor, DateTime in Flutter. Form rules determine whether a partial range can be submitted. |
| NgDataColumn<T> | Key, Title, typed Value accessor, Format and Template/cellBuilder. Sort underlying values, not formatted text. |
| NgTreeNode<T> | Unique Id, Item, Children, Disabled; no cycles. |
| NgBreadcrumbItem | ID and label; the final item is the current location. |
| NgGaugeRange | From, To, Color, Label (lowercase in Dart); finite ordered bounds inside the gauge, no overlap, nonblank label and #RRGGBB color. Gaps are allowed; shared endpoints belong to the next range, Max is inclusive. |
| NgChartSeries | Name and nullable numbers matching category count; null breaks a line. |
| NgChartSelection | Zero-based series/point indices, names, numeric value and display label. |
| NgScheduleView | Week/Day in C#, week/day in Dart. Controlled agenda mode; default Week. |
| NgScheduleEvent | Unique Id, nonempty Title, Start and End with End > Start. |
| NgSheet | Immutable single-sheet dimensions and address-to-raw-text dictionary. |
| NgCellValue | Text, nullable Number and nullable Error code. |
| NgPivotRecord | Unique nonblank ID, string dimensions and one nullable numeric measure. |
| NgPivotSelection | Raw row/column keys and original records in a read-only callback collection; null identifies a total, empty string a blank dimension. |
NgSheet
Addresses A1–Z100 are case-insensitive. A sheet has 1–100 rows and 1–26 columns. Each cell contains up to 1,000 characters; an absent in-bounds address is empty.
var sheet = new NgSheet(8, 5, new Dictionary<string,string> {
["A1"] = "10", ["B1"] = "=A1*2"
});
var next = sheet.Set("A1", "25");
var raw = next.Raw("B1"); // =A1*2
var result = next.Evaluate("B1"); // Number = 50
var pasted = next.Paste("A2", "3\t4\n=SUM(A2:B2)");final sheet = NgSheet(rows: 8, columns: 5,
cells: {'A1':'10', 'B1':'=A1*2'});
final next = sheet.set('A1','25');
final raw = next.raw('B1'); // =A1*2
final result = next.evaluate('B1'); // number = 50
final pasted = next.paste('A2','3\t4\n=SUM(A2:B2)');Set and Paste return a new sheet. Rectangular TSV paste preserves formula references literally: =A1 remains =A1. Out-of-bounds or oversized cells reject the whole operation. This is text paste, not XLSX/CSV import.
Formula language
Formulas start with =. Supported syntax: dot-decimal/exponent numbers, + - * /, unary signs, parentheses, cell references, ranges in function arguments and the functions below. Precedence is parentheses/unary, multiplication/division, addition/subtraction. Names and addresses are case-insensitive; arguments use commas.
| Formula | Meaning |
|---|---|
=2+3*4 |
14 |
=(2+3)*4 |
20 |
=A1*B1 |
Multiply numeric cells |
=SUM(A1:A5) |
Sum numbers, ignoring text/empty cells |
=AVERAGE(A1:A5) |
Mean of numbers; no numbers produces #DIV/0! |
=MIN(A1:A5) / =MAX(A1:A5) |
Extremum; an empty range returns 0 |
=COUNT(A1:A5) |
Count numeric cells |
=SUM(A1:B2,10) |
Sum a rectangle and 10 |
An empty direct reference evaluates to 0 in arithmetic; text produces #VALUE!. Range arguments skip text/empty cells. Scalar arguments evaluate expressions: COUNT(A1) counts computed zero for empty A1, while COUNT(A1:A1) skips it. This is a limited language, not full Excel semantics. No string/logical formulas, IF, dates, sheet/absolute references, named ranges or external code.
| Error | Cause |
|---|---|
| #FORMULA! | Invalid syntax/operators/parentheses/separators |
| #NAME? | Unsupported function |
| #REF! | Invalid or out-of-bounds address |
| #VALUE! | Text used as a number |
| #DIV/0! | Division by zero or average of no numbers |
| #CYCLE! | Direct/indirect circular reference |
| #NUM! | Nonfinite result/overflow |
| #LIMIT! | More than 64 expression levels or 100 dependent cells along a path |
Errors propagate. Dependencies are cached within an evaluation. Numbers use double floating point; exact decimal financial rules need a separate business layer. Raw text is stored separately from G10/10-significant-digit display.
Pivot totals
Sum/Average/Min/Max ignore null; no numbers displays —. Count includes records with null values. Totals aggregate original values: groups 10/20 and 90 produce Average 40, not (15+90)/2. NaN/Infinity and duplicate IDs are rejected. Missing dimensions equal an empty string. Selecting a total returns matching source records, or all records for the grand total. Aggregate overflow yields null displayed as —; normalize input if this must be distinguished from missing data.
NgDocument
Immutable Blocks contain Text, Kind, Bold, Italic, Underline and Align. Kinds: paragraph, heading1, heading2, bullet, numbered, quote. Alignment: left, center, right; C# enum names use PascalCase.
Replace(index,block), InsertAfter(index), Remove(index) and Move(index,target) return a new document, using zero-based indices. Removing the last block leaves one empty paragraph. Invalid indices/limits leave the original unchanged. Update a block with C# block with { Text = "New text" } or Dart block.copyWith(text: 'New text').
ToJson/fromJson interchange:
{
"version": 1,
"blocks": [
{
"text": "Title",
"kind": "heading1",
"bold": false,
"italic": false,
"underline": false,
"align": "left"
}
]
}Version and blocks are required; unknown fields are rejected. Missing optional block fields use empty-paragraph defaults; null text/type/flags are invalid. Use exact camelCase enum names. Limits: 100 blocks, 10,000 UTF-16 units per block, 100,000 total, 1,000,000 input JSON units. CRLF/CR normalize to LF. HTML remains text and ToHtml escapes it. See Editor for draft/history rules.
NgDiagramModel
Immutable Nodes/Edges describe ID/text/geometry/shapes and ID/from/to/text/directed connections. Coordinates identify the top-left corner and are independent of zoom.
{
"version": 1,
"width": 1000,
"height": 600,
"nodes": [
{
"id": "a",
"text": "Start",
"x": 40,
"y": 100,
"width": 160,
"height": 80,
"shape": "ellipse"
},
{
"id": "b",
"text": "Step",
"x": 360,
"y": 100,
"width": 160,
"height": 80,
"shape": "rectangle"
}
],
"edges": [
{
"id": "next",
"from": "a",
"to": "b",
"text": "Next",
"directed": true
}
]
}Required JSON fields: version/width/height/nodes/edges; nodes require id/text/x/y; edges require id/from/to. Omitted optional fields use constructor defaults. Unknown fields, invalid types/enums/IDs/geometry and JSON over 1,000,000 UTF-16 units are rejected. Empty arrays are allowed. Use the exported camelCase schema.
UpsertNode/UpsertEdge/RemoveNode/RemoveEdge/Move return a validated new graph. Canvas: 200–5000 per dimension; nodes: 60–400 × 40–240, inside the canvas. At most 100 nodes/200 edges, IDs at most 64 ASCII characters, labels at most 100 UTF-16 units. Move clamps coordinates; UpsertNode rejects out-of-bounds geometry. RemoveNode removes incident edges. See Diagram for interactions and limitations.
Geographic models
NgMapPoint stores latitude/longitude; NgMapView adds zoom. NgMapMarker contains ID/text/position. NgMapPath contains ID/text/copied points and a closed-polygon flag. NgMapModel copies collections and validates shared IDs and bounds. See Map for limits, signatures, clustering and update examples.
ChipList application records
NgChipData<T> maps your records to a read-only list of NgOption values. Required selectors provide the stable ID and accessible text; optional selectors provide disabled state, removal permission and NgChipAppearance. Pass these mappings explicitly to ChipList. Resolve the original record with GetItem(id) (Blazor) or getItem(id) (Flutter) in event handlers or templates.
This is a snapshot of mapped fields, not a deep copy of application records. Rebuild after edits or collection changes and update selected IDs together with removed records. Unknown lookup IDs throw; malformed mappings fail during construction. See ChipList for complete, compiled examples on both platforms.
Button appearance overrides (0.26)
NgButtonAppearance has nullable Size/size, Rounded/rounded, FillMode/fillMode and ThemeColor/themeColor fields. Return it from ButtonGroup's ItemAppearance/itemAppearance callback. Null fields inherit the corresponding group setting; it does not change the item's ID, command/toggle kind, visibility or selected value. See the ButtonGroup manual for supported tokens and a compiled mixed-command example.
Navigation presentation (0.27)
NgMenuItem adds optional immutable Visible/visible (default true) and Separator/separator (default false) alongside ID, text, disabled and children. Separators need unique IDs and labels but cannot contain children or invoke actions. Hidden ancestors make their descendants unreachable. Validation includes hidden items.
Drawer continues to use NgOption. IsVisible/isVisible and IsSeparator/isSeparator are pure predicates over those options. Their labels remain accessible names when item templates replace the displayed content. See the Menu and Drawer manuals for complete examples and lifecycle rules.
Stepper/Wizard contexts (0.28)
NgStepContext contains the immutable step model, zero-based Index/index and Current/current flag. Indicator and label templates are display-only; model labels and validity metadata remain accessible names.
NgWizardChange describes Current/current, Target/target and IsFinish/isFinish. BeforeChange returns an asynchronous boolean; false rejects the request before validation and state callbacks.
NgWizardActions provides Value/value, Busy/busy, Finished/finished, IsLast/isLast and CanBack/CanNext/CanFinish (lower camelCase in Dart). BackAsync/NextAsync/FinishAsync in Blazor and back/next/finish in Flutter return tasks/futures using the same guarded transition pipeline. Use IsLast to select a forward action and Can… to determine whether it is enabled. Do not cache these render contexts. See the Wizard manual for a complete custom action bar.
Selection input filtering (0.30)
NgFilterRules supplies the shared local filtering contract in both libraries. Validate/validate(mode, minLength) accepts contains, startsWith or endsWith and a nonnegative minimum query length. Matches/matches(label, query, mode, minLength) applies that validated configuration without trimming the query. Matching is case-insensitive and follows platform string rules, not locale-specific collation.
ValidateItems/validateItems requires unique nonblank NgOption.Value/value IDs and nonblank labels for ComboBox and MultiSelect. Values are IDs, not display labels; renaming a record must retain its ID. Disabled selected MultiSelect records are locked during user removal/clear. The parent may replace controlled selections explicitly and is responsible for reconciling removed records and externally supplied values that exceed a configured user-selection limit.
Spreadsheet change and protection helpers (0.39)
NgSheet.Paste(origin, text, canEdit) in C# / paste(origin, text, canEdit: ...) in Dart validates every supplied destination before returning a new sheet. The optional predicate receives uppercase addresses. A protected destination rejects the whole operation, even if the supplied value is unchanged. ChangedCells(other) / changedCells(other) returns changed raw-value addresses in row order and requires equal dimensions. Formula result changes alone do not count as raw edits.
Local Grid grouping models
Use GroupBy/groupBy for a single string selector or GroupSelectors/groupSelectors for ordered nested selectors (maximum eight levels). They are mutually exclusive. NgDataOperations.GroupTree (Blazor) and ngGroupTree (Flutter) expose the same first-appearance grouping algorithm. Each NgGridGroup<T> contains its full Path/path, encoded Id/id, Key/key, zero-based Depth/depth, filtered Items/items and Children/children.
NgDataOperations.GroupId(new[]{"Chair","Available"}) and ngGroupId(['Chair','Available']) both produce 5:Chair9:Available. Store these opaque full-path IDs in the controlled collapsed set. Empty keys and punctuation are supported. IDs remain stable across search, sorting and paging. Change the source version when changing grouping rules, and let the application decide whether to reset its collapsed set.
NgGroupAggregate<T> takes a title, a nullable numeric selector, an operation (sum, average, min, max, count) and an optional formatter. Blazor uses positional record arguments; Flutter uses named operation and format. Each aggregate is computed over original filtered records at its group level, not over page rows or child averages. Null and nonfinite values are ignored. Count means the number of finite numeric values; group header counts mean records. This uses double precision and is not a decimal accounting engine. See the Grid manual for state and paging contracts.
Remote grouped records
NgGroupedDataRequest adds ordered GroupKeys/groupKeys to page, page size, search and sorts. NgGroupedItem<T> pairs one record with its full path. NgRemoteGroup contains a group path, full filtered count and NgGroupSummary values (title and nullable double). NgGroupedDataResult<T> returns page items, the full filtered total and exactly the metadata needed for that page. Validated display groups expose Count/count separately from Items/items: in remote mode Items contains only downloaded records, while Count and Summaries are supplied by the server. See Remote grouped data for complete JSON and behavior contracts.
Grid editing
NgGridEditing<T>, NgGridEditField and NgGridEditOperation define drafts and application-owned asynchronous persistence. See Grid editing for validation, cancellation, concurrency and source refresh.
Grid export
NgGridExport<T>, NgGridExportScope, NgGridExportRequest, NgGridExportFile and NgGridCsv define bounded CSV/TSV generation and delivery. See Grid export for scope, remote completeness, encoding, limits and cancellation.