Skip to content

Commit

Permalink
Add support for "evaluateIdentifier" in angular parser
Browse files Browse the repository at this point in the history
  • Loading branch information
edi9999 committed Jul 22, 2024
1 parent 68e17c3 commit 3b3e724
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 0 deletions.
49 changes: 49 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,52 @@
### 3.49.0

Add possibility, when using the angular parser, to use "magic" keys to return some specific values. (This feature cannot be implemented if you use the `"docxtemplater/expressions-ie11.js"` package).

In your template, if you write :

```docx
{#loop}
{__val}
{/}
```

This will retrieve the "val" value from the scope that is above the current scope (it retrieves the value of "val" in the scope outside of the loop).

```js
const expressionParser = require("docxtemplater/expressions.js");
const doc = new Docxtemplater(zip, {
parser: expressionParser.configure({
evaluateIdentifier(tag, scope, scopeList, context) {
const matchesParent = /^(_{2,})(.*)/g;
if (matchesParent.test(tag)) {
const parentCount = tag.replace(matchesParent, "$1").length - 1;
tag = tag.replace(matchesParent, "$2");
if (parentCount >= 1) {
for (let i = scopeList.length - 1 - parentCount; i >= 0; i--) {
const s = scopeList[i];
if (s[tag] != null) {
const property = s[tag];
return typeof property === "function"
? property.bind(s)
: property;
}
}
}
}
},
}),
});

doc.render({
loop: [
{
val: "This value",
},
],
val: "Other value", // <= This value will be retrieved
});
```

### 3.48.0

Allow to configure the behavior of the "change delimiter syntax".
Expand Down
7 changes: 7 additions & 0 deletions es6/docxtemplater.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,10 @@ ieAngularParser.configure({
isIdentifierStart: validStartChars,
isIdentifierContinue: validContinuationChars,
});

angularParser.configure({
evaluateIdentifier(tag: string, scope: any, scopeList: any[], context: any) {
let res = context.num + context.num;
return res;
},
});
6 changes: 6 additions & 0 deletions es6/expressions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ interface ParserOptions {
literals?: { [x: string]: any };
isIdentifierStart?: (char: string) => boolean;
isIdentifierContinue?: (char: string) => boolean;
evaluateIdentifier?: (
tag: string,
scope: any,
scopeList: any[],
context: DXT.ParserContext
) => any;
}

type Parser = {
Expand Down
22 changes: 22 additions & 0 deletions es6/expressions.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,17 @@ function configuredParser(config = {}) {
{},
{
get(target, name) {
if (config.evaluateIdentifier) {
const fnResult = config.evaluateIdentifier(
name,
scope,
scopeList,
context
);
if (fnResult != null) {
return fnResult;
}
}
if (name === "$index") {
return getIndex(scope, context);
}
Expand All @@ -173,6 +184,17 @@ function configuredParser(config = {}) {
return null;
},
has(target, name) {
if (config.evaluateIdentifier) {
const fnResult = config.evaluateIdentifier(
name,
scope,
scopeList,
context
);
if (fnResult != null) {
return true;
}
}
if (name === "$index") {
return true;
}
Expand Down
48 changes: 48 additions & 0 deletions es6/tests/e2e/fixtures.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const angularParserIE11 = require("../../expressions-ie11.js");
const Errors = require("../../errors.js");
const { wrapMultiError } = require("../utils.js");
const nbsp = String.fromCharCode(160);
const { expect } = require("../utils.js");

expressionParser.filters.upper = function (str) {
if (!str) {
Expand Down Expand Up @@ -1807,6 +1808,53 @@ http://errors.angularjs.org/"NG_VERSION_FULL"/$parse/lexerr?p0=Unexpected%20next
},
result: '<w:t xml:space="preserve">FOO</w:t>',
},
{
it: "should be possible to use parent scope together with expressionParser",
content: "<w:t>{#loop}{__b|twice}{b|twice}{/loop}</w:t>",
// $b means in this context "b" but from the rootscope
scope: {
loop: [
{
b: 2,
},
{
b: 3,
},
],
b: 1,
},
...noInternals,
options: {
linebreaks: true,
parser: expressionParser.configure({
evaluateIdentifier(tag, scope, scopeList, context) {
const matchesParent = /^(_{2,})(.*)/g;
expect(context.num).to.be.a("number");
if (matchesParent.test(tag)) {
const parentCount = tag.replace(matchesParent, "$1").length - 1;
tag = tag.replace(matchesParent, "$2");
if (parentCount >= 1) {
for (let i = scopeList.length - 1 - parentCount; i >= 0; i--) {
const s = scopeList[i];
if (s[tag] != null) {
const property = s[tag];
return typeof property === "function"
? property.bind(s)
: property;
}
}
}
}
},
filters: {
twice(input) {
return 2 * input;
},
},
}),
},
result: '<w:t xml:space="preserve">2426</w:t>',
},
{
it: "should be possible to add filter for one instance of the ie11 parser",
content: "<w:t>{b|foo}</w:t>",
Expand Down

0 comments on commit 3b3e724

Please sign in to comment.