diff --git a/DocxTemplater.Test/DocxTemplater.Test.csproj b/DocxTemplater.Test/DocxTemplater.Test.csproj index 1f24d35..a9cff9e 100644 --- a/DocxTemplater.Test/DocxTemplater.Test.csproj +++ b/DocxTemplater.Test/DocxTemplater.Test.csproj @@ -1,6 +1,7 @@  - enable + 12.0 + enable disable false true diff --git a/DocxTemplater.Test/PatternMatcherTest.cs b/DocxTemplater.Test/PatternMatcherTest.cs index f7e269d..673d9e4 100644 --- a/DocxTemplater.Test/PatternMatcherTest.cs +++ b/DocxTemplater.Test/PatternMatcherTest.cs @@ -57,6 +57,9 @@ static IEnumerable TestPatternMatch_Cases() yield return new TestCaseData("{{var}:format(a,b)}").Returns(new[] { PatternType.Variable }).SetName("Multiple Arguments"); yield return new TestCaseData("{{/}}").Returns(new[] { PatternType.ConditionEnd }); yield return new TestCaseData("{ { / } }").Returns(new[] { PatternType.ConditionEnd }); + yield return new TestCaseData("{?{ds.QrBills.idx == 2}}").Returns(new[] { PatternType.Condition }); + yield return new TestCaseData("{?{ds.QrBills._Idx == 2}}").Returns(new[] { PatternType.Condition }).SetName("underscore in variable name"); + yield return new TestCaseData("{?{ds.QrBills._Idx % 2 == 0}}").Returns(new[] { PatternType.Condition }).SetName("modulo in condition"); yield return new TestCaseData( "NumericValue is greater than 0 - {{ds.Items.InnerCollection.InnerValue}:toupper()}{{else}}" + "I'm here if if this is not the case{{/}}{{/ds.Items.InnerCollection}}{{/Items}}") diff --git a/DocxTemplater.Test/SpecialCollectionVariableTest.cs b/DocxTemplater.Test/SpecialCollectionVariableTest.cs new file mode 100644 index 0000000..0bfded1 --- /dev/null +++ b/DocxTemplater.Test/SpecialCollectionVariableTest.cs @@ -0,0 +1,68 @@ +using DocumentFormat.OpenXml.Packaging; +using DocumentFormat.OpenXml.Wordprocessing; +using DocumentFormat.OpenXml; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DocxTemplater.Test +{ + internal class SpecialCollectionVariableTest + { + [Test] + public void TestIndexVariableInLoop() + { + var model = new[] {"Item1", "Item2", "Item3", "Item4"}; + var template = "Items:{{#Items}}{{Items._Idx}}{{.}} {{/Items}}"; + + using var memStream = new MemoryStream(); + using var wpDocument = WordprocessingDocument.Create(memStream, WordprocessingDocumentType.Document); + MainDocumentPart mainPart = wpDocument.AddMainDocumentPart(); + mainPart.Document = new Document(new Body(new Paragraph(new Run(new Text(template))))); + wpDocument.Save(); + memStream.Position = 0; + var docTemplate = new DocxTemplate(memStream); + docTemplate.BindModel("Items", model); + var result = docTemplate.Process(); + result.Position = 0; + // compare body + result.Position = 0; + var document = WordprocessingDocument.Open(result, false); + var body = document.MainDocumentPart.Document.Body; + Assert.That(body.InnerXml, Is.EqualTo("" + + "" + + "Items:1Item1" + + " 2Item2" + + " 3Item3" + + " 4Item4" + + " ")); + } + + [Test] + public void TestConditionWithIndexVariableInLoop() + { + var model = new[] { "Item1", "Item2", "Item3", "Item4" }; + var template = "Items:{{#Items}}{?{Items._Idx % 2 == 0}}{{.}}{{/}}{{/Items}}"; + + using var memStream = new MemoryStream(); + using var wpDocument = WordprocessingDocument.Create(memStream, WordprocessingDocumentType.Document); + MainDocumentPart mainPart = wpDocument.AddMainDocumentPart(); + mainPart.Document = new Document(new Body(new Paragraph(new Run(new Text(template))))); + wpDocument.Save(); + memStream.Position = 0; + var docTemplate = new DocxTemplate(memStream); + docTemplate.BindModel("Items", model); + var result = docTemplate.Process(); + result.Position = 0; + // compare body + result.Position = 0; + var document = WordprocessingDocument.Open(result, false); + var body = document.MainDocumentPart.Document.Body; + Assert.That(body.InnerXml, Is.EqualTo("Items:" + + "Item2" + + "Item4")); + } + } +} diff --git a/DocxTemplater/PatterMatcher.cs b/DocxTemplater/PatterMatcher.cs index ba38c21..735a881 100644 --- a/DocxTemplater/PatterMatcher.cs +++ b/DocxTemplater/PatterMatcher.cs @@ -25,7 +25,7 @@ internal static class PatternMatcher (?:\s*s\s*:) | (?(?:else)|:) | (?(condMarker) # if condition marker is set, we expect a condition - (?[a-zA-Z0-9+\-*\/><=\s\.\!&\|]+)? #condition name (without brackets) + (?[a-zA-Z0-9+\-*\/><=\s\.\!&\|_%]+)? #condition expression (without brackets) | (?: (?[\/\#])?(?[a-zA-Z0-9\._]+)? #variable name diff --git a/README.md b/README.md index 7e729fc..b4a19e7 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ The syntax is case insensitive. | `{{SomeDate}:F('MM/dd/yyyy')}` | Date variable with formatting - short syntax | | `{{SomeBytes}:img()}` | Image Formatter for image data | | `{{SomeHtmlString}:html()}` | Inserts HTML string into the word document | +| `{{#Items}}{?{Items._Idx % 2 == 0}}{{.}}{{/}}{{/Items}}` | Renders every second item in a list | --- ### Collections @@ -113,6 +114,11 @@ Will render a table row for each item in the collection: |-------|-----------| | John | Developer | | Alice | CEO | + +#### Accessing the Index of the Current Item + +To access the index of the current item, use the special variable `Items._Idx` In this example, the collection is called "Items". + --- ### Separator