Skip to content

Commit

Permalink
Issue 152: Added string template cell (#161)
Browse files Browse the repository at this point in the history
* Added `TemplateStringCell` test

* Added `TemplateStringCell` implementation

* Describe `TemplateStringCell` in README.md

* Update xlsx-renderer readme
  • Loading branch information
Siemienik authored Feb 12, 2021
1 parent caf8796 commit be07a88
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 15 deletions.
31 changes: 17 additions & 14 deletions packages/xlsx-renderer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,19 +63,20 @@ It is possible to use the command line interface [read more about xlsx-renderer-
| - | [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 | [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) | 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 `${pathToVariable}`. | **Example:**<br/> `#= ${summaryFormula}(A2:A${item.__endOutput.r})` gives something like `=MAX(A2:A2910)` <br/> [Example](./tests/integration/data/Renderer017-TemplateFormula)|
| Content | [TemplateStringCell](./src/cell/TemplateStringCell.ts) | 4 | Starts with <code>#` </code> | Template string allows you to create advanced text, for example concat two variables or put them into a sentence. To write in a variable use `${pathToVariable}`. | **Example:**<br/> <code>#` Hello ${name}! How are you?</code> gives for instance `Hello World! How are you?` <br/> [Example](./tests/integration/data/Renderer018-TemplateString)|
| Content | [HyperlinkCell](./src/cell/HyperlinkCell.ts) | 7 | `#! HYPERLINK pathToLabel pathToTarget` | Create a hyperlink. | *Paths resolve exactly same as VariableCell* |
| Content | [FormulaCell](./src/cell/FormulaCell.ts) | 6 | 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) | 5 | Starts with `#= ` | This one allows you to put a template string (custom formula) into a cell as a formula. To write in a variable use `${pathToVariable}`. | **Example:**<br/> `#= ${summaryFormula}(A2:A${item.__endOutput.r})` gives something like `=MAX(A2:A2910)` <br/> [Example](./tests/integration/data/Renderer017-TemplateFormula)|
| 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) | 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) |
| Worksheet<br/>Navigation<br/>Loop | [FinishCell](./src/cell/FinishCell.ts) | 9 | `#! 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) | 15 | `#! 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) | 12 | `#! 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) | 8 | #! 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) | 11 | `#! 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) | 10 | `#! END_LOOP item` | Mark cell when the loop `item` finished. | |
| Aggregation| [SumCell](./src/cell/SumCell.ts) | 13 | `#! 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) | 14 | `#! 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) | 16 | `#! DELETE pathToVariable` | Delete variable, useful for nested loops.| [Example](./tests/integration/data/Renderer009-ForEach-Average) |

## Examples

Expand Down Expand Up @@ -108,6 +109,8 @@ _These examples might be runned by using the command line tool, [read more](../x
| 15 | [ForEach-merged-two-tables](./tests/integration/data/Renderer015-ForEach-merged-two-tables) | Checks merged cells behaviour |
| 16 | [ForEach-merged-pyramid](./tests/integration/data/Renderer016-ForEach-merged-pyramid) | Checks merged cells behaviour |
| 17 | [TemplateFormulaCell](./tests/integration/data/Renderer017-TemplateFormulaCell) | Dynamic formula creation |
| 18 | [TemplateStringCell](./tests/integration/data/Renderer018-TemplateStringCell) | Dynamic content creation following by custom template string (`Hello ${name}`). |


## Support

Expand All @@ -123,9 +126,9 @@ We are ready to provide paid support, in order that please contact me: [hi@siemi

If Node v8 & v9 needed, please contact us [support@siemienik.pl](mailto://support@siemienik.pl).

## Browser Support
### Browser Support

XLSX Renderer may run on browser side, [read more how to do it](https://github.com/Siemienik/XToolSet/issues/93#issuecomment-732309383).
XLSX Renderer may run on browser side, [read more how to do it](https://github.com/Siemienik/XToolSet/issues/93).

---

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 @@ -3,6 +3,7 @@ import { Cell } from 'exceljs';
import { BaseCell, CellType } from './cell/BaseCell';
import { NormalCell } from './cell/NormalCell';
import { TemplateFormulaCell } from './cell/TemplateFormulaCell';
import { TemplateStringCell } from './cell/TemplateStringCell';
import { VariableCell } from './cell/VariableCell';
import { FinishCell } from './cell/FinishCell';
import { ForEachCell } from './cell/ForEachCell';
Expand All @@ -22,6 +23,7 @@ export class CellTemplatePool {
NormalCell,
EndRowCell,
VariableCell,
TemplateStringCell,
TemplateFormulaCell,
FormulaCell,
HyperlinkCell,
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)) // todo documentation: describe prefixes in a documentation
);
}

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

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

/**
* @description
* TemplateStringCellinterpolate string and put it into cell as a string value
* * starts width <pre>#` </pre>
*/
export class TemplateStringCell 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): TemplateStringCell {
super.apply(scope);

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

const result = 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 string output: ${
JSON.stringify(scope.outputCell)
} when template is:${
JSON.stringify(scope.templateCell)
}`
);
}

return value;
});

// todo refactoring: this is only one line which the logic is different as in TemplateFormulaCell, consider about refactoring
scope.setCurrentOutputValue(result);
scope.incrementCol();

return this;
}
}
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"author": {
"firstName": "Paweł",
"lastName": "Siemienik",
"hobby": "OSS development"
}
}

0 comments on commit be07a88

Please sign in to comment.