Skip to content

Issue 158: Added TemplateFormulaCell #160

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Feb 12, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 13 additions & 12 deletions packages/xlsx-renderer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,20 +64,21 @@ It is possible to use the command line interface [read more about xlsx-renderer-
| Category | Name | Matching Order | Matching Rule | Description | More info |
|----------|-----:|-------|--------|-------------|:---------|
| - | [BaseCell](./src/cell/BaseCell.ts) | n/o | n/o | All Cell\`s definition classes extend it. | **abstract** |
| Content | [NormalCell](./src/cell/NormalCell.ts) | 1 | not started by `##` or `#!` | This one copy all styles, width, properties and value form template. | **default** |
| Content | [NormalCell](./src/cell/NormalCell.ts) | 1 | not started by `##`, `#!` or `#=` | This one copy all styles, width, properties and value form template. | **default** |
| Content | [VariableCell](./src/cell/VariableCell.ts) | 3 | `## pathToVariable` | Write variable from `ViewModel`. <br/> Paths to object's property or array item are allowed.<br/> When asking about undefined variable it returns empty string. | **Paths examples:** <br/> `simplePath` <br/> `someObject.property` <br/> `array.0.field` <br/> `items.1.path.to.object.prop`|
| Content | [HyperlinkCell](./src/cell/HyperlinkCell.ts) | 5 | `#! HYPERLINK pathToLabel pathToTarget` | Create a hyperlink. | *Paths resolve exactly same as VariableCell* |
| Content | [FormulaCell](./src/cell/FormulaCell.ts) | 4 | Cell.type eq. formulae | It handles correctly formulas inside and outside of loops - when rows were shifted compared to the template. | *It is used automatically when formulae from the template being rendered* <br/> [Example](./tests/integration/data/Renderer010-Formula)|
| Content | [HyperlinkCell](./src/cell/HyperlinkCell.ts) | 6 | `#! HYPERLINK pathToLabel pathToTarget` | Create a hyperlink. | *Paths resolve exactly same as VariableCell* |
| Content | [FormulaCell](./src/cell/FormulaCell.ts) | 5 | Cell.type eq. formulae | It handles correctly formulas inside and outside of loops - when rows were shifted compared to the template. | *It is used automatically when formulae from the template being rendered* <br/> [Example](./tests/integration/data/Renderer010-Formula)|
| Content | [TemplateFormulaCell](./src/cell/TemplateFormulaCell.ts) | 4 | Starts with `#= ` | This one allows you to put a template string into a cell as a formula. To write in a variable use `${patToVariable}`. | **Example:**<br/> `#= ${summaryFormula}(A2:A${tem.__endOutput.r})` gives something like `=MAX(A2:A2910)` <br/> [Example](./tests/integration/data/Renderer017-TemplateFormula)|
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line was added, others lines have only changed Matching Order

| Navigation | [EndRowCell](./src/cell/EndRowCell.ts) | 2 | `#! END_ROW` | Go to the beginning of next row | |
| Worksheet<br/>Navigation<br/>Loop | [FinishCell](./src/cell/FinishCell.ts) | 7 | `#! FINISH conditionPath` | Finish rendering for current worksheet and: <br/> 1) go to next worksheet if `conditionPath===true`<br/> 2) repeat this template worksheet again (`conditionPath === false`) - looping through worksheets <br/> 3) finished whole rendering when this worksheet is the last one. | **Examples:**<br/> `#! FINISHED` or `#! FINISHED itemFromLoop.__iterated` |
| Worksheet | [WsNameCell](./src/cell/WsNameCell.ts) | 13 | `#! WS_NAME pathToVariable` | Set worksheet's name. | **Examples:** <br/> `#! WS_NAME worksheetName` <br/> `#! WS_NAME item.title` <br/> `#! WS_NAME translatedNames.0` |
| Loop | [DumpColsCell](./src/cell/DumpColsCell.ts) | 10 | `#! DUMP_COLS pathToArray` | Useful for writing through multiple columns. It put each value of array to next column. | [Example](./tests/integration/data/Renderer011-DumpCols) |
| Loop | [ForEachCell](./src/cell/ForEachCell.ts) | 6 | #! FOR_EACH item items | Begin the loop named `item`, set the first element of `items` into `item` and go to the beginning of next line.| Connected to: `ContinueCell`, `EndLoopCell`, `DeleteCell`, `FinishedCell`, `SumCell`, `AverageCell`. |
| Loop | [ContinueCell](./src/cell/ContinueCell.ts) | 9 | `#! CONTINUE item` | Iterate to next element of loop named `item` (check `ForEachCell` for more information) and navigate to the beginning of new line. | |
| Loop | [EndLoopCell](./src/cell/EndLoopCell.ts) | 8 | `#! END_LOOP item` | Mark cell when the loop `item` finished. | |
| Aggregation| [SumCell](./src/cell/SumCell.ts) | 11 | `#! SUM item` | Write sum formulae for current column and the `item`'s rows. | [Example](./tests/integration/data/Renderer007-ForEach-Sum) |
| Aggregation | [AverageCell](./src/cell/AverageCell.ts) | 12 | `#! AVERAGE item` | Write average formulae for current column and the `item`'s rows. | [Example](./tests/integration/data/Renderer009-ForEach-Average) |
| View Model | [DeleteCell](./src/cell/DeleteCell.ts) | 14 | `#! DELETE pathToVariable` | Delete variable, useful for nested loops.| [Example](./tests/integration/data/Renderer009-ForEach-Average) |
| Worksheet<br/>Navigation<br/>Loop | [FinishCell](./src/cell/FinishCell.ts) | 8 | `#! FINISH conditionPath` | Finish rendering for current worksheet and: <br/> 1) go to next worksheet if `conditionPath===true`<br/> 2) repeat this template worksheet again (`conditionPath === false`) - looping through worksheets <br/> 3) finished whole rendering when this worksheet is the last one. | **Examples:**<br/> `#! FINISHED` or `#! FINISHED itemFromLoop.__iterated` |
| Worksheet | [WsNameCell](./src/cell/WsNameCell.ts) | 14 | `#! WS_NAME pathToVariable` | Set worksheet's name. | **Examples:** <br/> `#! WS_NAME worksheetName` <br/> `#! WS_NAME item.title` <br/> `#! WS_NAME translatedNames.0` |
| Loop | [DumpColsCell](./src/cell/DumpColsCell.ts) | 11 | `#! DUMP_COLS pathToArray` | Useful for writing through multiple columns. It put each value of array to next column. | [Example](./tests/integration/data/Renderer011-DumpCols) |
| Loop | [ForEachCell](./src/cell/ForEachCell.ts) | 7 | #! FOR_EACH item items | Begin the loop named `item`, set the first element of `items` into `item` and go to the beginning of next line.| Connected to: `ContinueCell`, `EndLoopCell`, `DeleteCell`, `FinishedCell`, `SumCell`, `AverageCell`. |
| Loop | [ContinueCell](./src/cell/ContinueCell.ts) | 10 | `#! CONTINUE item` | Iterate to next element of loop named `item` (check `ForEachCell` for more information) and navigate to the beginning of new line. | |
| Loop | [EndLoopCell](./src/cell/EndLoopCell.ts) | 9 | `#! END_LOOP item` | Mark cell when the loop `item` finished. | |
| Aggregation| [SumCell](./src/cell/SumCell.ts) | 12 | `#! SUM item` | Write sum formulae for current column and the `item`'s rows. | [Example](./tests/integration/data/Renderer007-ForEach-Sum) |
| Aggregation | [AverageCell](./src/cell/AverageCell.ts) | 13 | `#! AVERAGE item` | Write average formulae for current column and the `item`'s rows. | [Example](./tests/integration/data/Renderer009-ForEach-Average) |
| View Model | [DeleteCell](./src/cell/DeleteCell.ts) | 15 | `#! DELETE pathToVariable` | Delete variable, useful for nested loops.| [Example](./tests/integration/data/Renderer009-ForEach-Average) |

</details>

Expand Down
2 changes: 2 additions & 0 deletions packages/xlsx-renderer/src/CellTemplatePool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Cell } from 'exceljs';

import { BaseCell, CellType } from './cell/BaseCell';
import { NormalCell } from './cell/NormalCell';
import { TemplateFormulaCell } from './cell/TemplateFormulaCell';
import { VariableCell } from './cell/VariableCell';
import { FinishCell } from './cell/FinishCell';
import { ForEachCell } from './cell/ForEachCell';
Expand All @@ -21,6 +22,7 @@ export class CellTemplatePool {
NormalCell,
EndRowCell,
VariableCell,
TemplateFormulaCell,
FormulaCell,
HyperlinkCell,
ForEachCell,
Expand Down
2 changes: 1 addition & 1 deletion packages/xlsx-renderer/src/cell/NormalCell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export class NormalCell extends BaseCell {
cell &&
cell.type === ValueType.String &&
typeof cell.value === 'string' &&
!['##', '#!'].includes(cell.value.substring(0, 2))
!['##', '#!', "#="].includes(cell.value.substring(0, 2))
);
}

Expand Down
56 changes: 56 additions & 0 deletions packages/xlsx-renderer/src/cell/TemplateFormulaCell.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { BaseCell } from './BaseCell';
import { Cell, CellFormulaValue, ValueType } from 'exceljs';
import { Scope } from '../Scope';

const variableRegex = /\${[^{]+?}/g;

/**
* @description
* TemplateFormulaCell interpolate string and put it into cell as a formula
* * starts width `#= `
*/
export class TemplateFormulaCell extends BaseCell {
public static match(cell: Cell): boolean {
return (
cell &&
cell.type === ValueType. String &&
(cell.isMerged ? cell.master.address === cell.address : true) &&
typeof cell.value === 'string' &&
cell.value.substring(0, 3) === '#= '
);
}

public apply(scope: Scope): TemplateFormulaCell {
super.apply(scope);

const template = scope
.getCurrentTemplateString()
.substring(3);

const formula = template.replace(variableRegex, (match) => {
const path = match.slice(2, -1).split('.');

// todo refactoring extract, similar like in VariableCell
const value = path.reduce((p, c) => (typeof p === 'object' ? p[c] : p), scope.vm);
if (value === undefined && !scope.isFrozen()) {
// todo do it better (use logger or something like that)
// tslint:disable-next-line:no-console
console.warn(
`WARN: ${path} is undefined for template formula output: ${
JSON.stringify(scope.outputCell)
} when template is:${
JSON.stringify(scope.templateCell)
}`
);
}

return value;
});


scope.setCurrentOutputValue({ formula } as CellFormulaValue);
scope.incrementCol();

return this;
}
}
1 change: 1 addition & 0 deletions packages/xlsx-renderer/src/cell/VariableCell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export class VariableCell extends BaseCell {
.substring(3)
.split('.');

// todo refactoring extract, similar like in TemplateFormulaCell
const value = path.reduce((p, c) => (typeof p === 'object' ? p[c] : p), scope.vm);
if (value === undefined && !scope.isFrozen()) {
// todo do it better (use logger or somethink like that)
Expand Down
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"items": [
{
"name": "Item A",
"weight": 13.5,
"price": 12.1
},
{
"name": "Item B",
"weight": 13.5,
"price": 12
},
{
"name": "Item C",
"weight": 3.5,
"price": 12.7
},
{
"name": "Item D",
"weight": 13,
"price": 2.1
},
{
"name": "Item E",
"weight": 1.5,
"price": 32
}
]
}