Skip to content

Commit 3205162

Browse files
committed
add support to list inside lists
1 parent 2ea3b51 commit 3205162

9 files changed

Lines changed: 328 additions & 40 deletions

File tree

README.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,94 @@ MiniWord.SaveAsByTemplate(path, templatePath, value);
178178

179179
![image](C:\Users\Wei\Downloads\190843663-c00baf16-21f2-4579-9d08-996a2c8c549b.png)
180180

181+
### List inside list
182+
183+
Tag value is `IEnumerable<MiniWordForeach>` type. Adding `{{foreach` and `endforeach}}` tags to template is required.
184+
185+
##### Example
186+
187+
```csharp
188+
var value = new Dictionary<string, object>()
189+
{
190+
["TripHs"] = new List<Dictionary<string, object>>
191+
{
192+
new Dictionary<string, object>
193+
{
194+
{ "sDate", DateTime.Parse("2022-09-08 08:30:00") },
195+
{ "eDate", DateTime.Parse("2022-09-08 15:00:00") },
196+
{ "How", "Discussion requirement part1" },
197+
{
198+
"Details", new List<MiniWordForeach>()
199+
{
200+
new MiniWordForeach()
201+
{
202+
Value = new Dictionary<string, object>()
203+
{
204+
{"Text", "Air"},
205+
{"Value", "Airplane"}
206+
},
207+
Separator = " | "
208+
},
209+
new MiniWordForeach()
210+
{
211+
Value = new Dictionary<string, object>()
212+
{
213+
{"Text", "Parking"},
214+
{"Value", "Car"}
215+
},
216+
Separator = " / "
217+
}
218+
}
219+
}
220+
}
221+
}
222+
};
223+
MiniWord.SaveAsByTemplate(path, templatePath, value);
224+
```
225+
226+
##### Template
227+
228+
![before_foreach](https://user-images.githubusercontent.com/38832863/220123955-063c9345-3998-4fd7-982c-8d1e3b48bbf8.PNG)
229+
230+
##### Result
231+
232+
![after_foreach](https://user-images.githubusercontent.com/38832863/220123960-913a7140-2fa2-415e-bb3e-456e04167382.PNG)
233+
234+
### If statement inside template
235+
236+
Adding `@if` and `@endif` tags to template is required.
237+
238+
##### Example
239+
240+
```csharp
241+
var value = new Dictionary<string, object>()
242+
{
243+
["Name"] = new List<MiniWordHyperLink>(){
244+
new MiniWordHyperLink(){
245+
Url = "https://google.com",
246+
Text = "測試連結22!!"
247+
},
248+
new MiniWordHyperLink(){
249+
Url = "https://google1.com",
250+
Text = "測試連結11!!"
251+
}
252+
},
253+
["Company_Name"] = "MiniSofteware",
254+
["CreateDate"] = new DateTime(2021, 01, 01),
255+
["VIP"] = true,
256+
["Points"] = 123,
257+
["APP"] = "Demo APP",
258+
};
259+
MiniWord.SaveAsByTemplate(path, templatePath, value);
260+
```
261+
262+
##### Template
263+
264+
![before_if](https://user-images.githubusercontent.com/38832863/220125429-7dd6ce94-35c6-478e-8903-064f9cf9361a.PNG)
265+
266+
##### Result
267+
268+
![after_if](https://user-images.githubusercontent.com/38832863/220125435-72ea24b4-2412-45de-961a-ad4b2134417b.PNG)
181269

182270

183271
## Other

release-note/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
---
2424

2525

26+
### 0.6.2
27+
- [New] Add support to List inside List via `IEnumerable<MiniWordForeach>` and `{{foreach`/`endforeach}}` tags (via @eynarhaji)
28+
- [New] Add support to @if statements inside templates (via @eynarhaji)
2629

2730
### 0.6.1
2831
- [Bug] Fixed system does not support `IEnumerable<MiniWordHyperLink>` (#39 via @isdaniel)
-13.8 KB
Binary file not shown.
50.1 KB
Binary file not shown.

samples/docx/TestIfStatement.docx

13 KB
Binary file not shown.

src/MiniWord/MiniWord.Implment.cs

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ private static void Generate(this OpenXmlElement xmlElement, WordprocessingDocum
4545
// avoid {{tag}} like <t>{</t><t>{</t>
4646
//AvoidSplitTagText(xmlElement);
4747
// avoid {{tag}} like <t>aa{</t><t>{</t> test in...
48-
AvoidSplitTagText(xmlElement, GetReplaceKeys(tags));
48+
AvoidSplitTagText(xmlElement);
4949

5050
//Tables
5151
var tables = xmlElement.Descendants<Table>().ToArray();
@@ -56,16 +56,15 @@ private static void Generate(this OpenXmlElement xmlElement, WordprocessingDocum
5656

5757
foreach (var tr in trs)
5858
{
59-
6059
var matchs = (Regex.Matches(tr.InnerText, "(?<={{).*?\\..*?(?=}})")
6160
.Cast<Match>().GroupBy(x => x.Value).Select(varGroup => varGroup.First().Value)).ToArray();
6261
if (matchs.Length > 0)
6362
{
6463
var listKeys = matchs.Select(s => s.Split('.')[0]).Distinct().ToArray();
6564
// TODO:
66-
// not support > 1 list in same tr
67-
if (listKeys.Length > 1)
68-
throw new NotSupportedException("MiniWord doesn't support more than 1 list in same row");
65+
// not support > 2 list in same tr
66+
if (listKeys.Length > 2)
67+
throw new NotSupportedException("MiniWord doesn't support more than 2 list in same row");
6968
var listKey = listKeys[0];
7069
if (tags.ContainsKey(listKey) && tags[listKey] is IEnumerable)
7170
{
@@ -106,6 +105,7 @@ private static void AvoidSplitTagText(OpenXmlElement xmlElement)
106105
var pool = new List<Text>();
107106
var sb = new StringBuilder();
108107
var needAppend = false;
108+
var foreachIncluded = false;
109109
foreach (var text in texts)
110110
{
111111
var clear = false;
@@ -122,10 +122,28 @@ private static void AvoidSplitTagText(OpenXmlElement xmlElement)
122122
// TODO: check tag exist
123123
// TODO: record tag text if without tag then system need to clear them
124124
// TODO: every {{tag}} one <t>for them</t> and add text before first text and copy first one and remove {{, tagname, }}
125-
125+
126+
if(s.StartsWith("{{foreach"))
127+
foreachIncluded = true;
128+
126129
if (!s.StartsWith("{{"))
127130
clear = true;
128-
else if (s.Contains("{{") && s.Contains("}}"))
131+
else if (s.Contains("{{") && s.Contains("}}") && !foreachIncluded)
132+
{
133+
if (sb.Length <= 1000) // avoid too big tag
134+
{
135+
var first = pool.First();
136+
var newText = first.Clone() as Text;
137+
newText.Text = s;
138+
first.Parent.InsertBefore(newText, first);
139+
foreach (var t in pool)
140+
{
141+
t.Text = "";
142+
}
143+
}
144+
clear = true;
145+
}
146+
else if (s.Contains("{{foreach") && s.Contains("endforeach}}") && foreachIncluded)
129147
{
130148
if (sb.Length <= 1000) // avoid too big tag
131149
{
@@ -139,7 +157,9 @@ private static void AvoidSplitTagText(OpenXmlElement xmlElement)
139157
}
140158
}
141159
clear = true;
160+
foreachIncluded = false;
142161
}
162+
143163
}
144164

145165
if (clear)
@@ -177,7 +197,9 @@ private static List<string> GetReplaceKeys(Dictionary<string, object> tags)
177197
if (item2 is Dictionary<string, object> dic)
178198
{
179199
foreach (var item3 in dic.Keys)
200+
{
180201
keys.Add("{{" + item.Key + "." + item3 + "}}");
202+
}
181203
}
182204
break;
183205
}
@@ -330,6 +352,19 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen
330352
foreach (var tag in tags)
331353
{
332354
var isMatch = t.Text.Contains($"{{{{{tag.Key}}}}}");
355+
356+
if (!isMatch && tag.Value is List<MiniWordForeach> forTags)
357+
{
358+
if (forTags.Any(forTag => forTag.Value.Keys.Any(dictKey =>
359+
{
360+
var innerTag = "{{" + tag.Key + "." + dictKey + "}}";
361+
return t.Text.Contains(innerTag);
362+
})))
363+
{
364+
isMatch = true;
365+
}
366+
}
367+
333368
if (isMatch)
334369
{
335370
if (tag.Value is string[] || tag.Value is IList<string> || tag.Value is List<string>)
@@ -350,6 +385,31 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen
350385
}
351386
t.Remove();
352387
}
388+
else if (tag.Value is List<MiniWordForeach> vs)
389+
{
390+
var currentT = t;
391+
var generatedText = new Text();
392+
currentT.Text = currentT.Text.Replace(@"{{foreach", "").Replace(@"endforeach}}", "");
393+
for (var i = 0; i < vs.Count; i++)
394+
{
395+
var newT = t.CloneNode(true) as Text;
396+
397+
foreach (var vv in vs[i].Value)
398+
{
399+
newT.Text = newT.Text.Replace("{{" + tag.Key + "." + vv.Key + "}}", vv.Value.ToString());
400+
}
401+
402+
if (i != vs.Count)
403+
{
404+
newT.Text += vs[i].Separator;
405+
}
406+
407+
generatedText.Text += newT.Text;
408+
}
409+
410+
run.Append(generatedText);
411+
t.Remove();
412+
}
353413
else if (tag.Value is MiniWordPicture)
354414
{
355415
var pic = (MiniWordPicture)tag.Value;

src/MiniWord/MiniWordForeach.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace MiniSoftware
8+
{
9+
public class MiniWordForeach
10+
{
11+
public Dictionary<string, object> Value { get; set; }
12+
public string Separator { get; set; }
13+
}
14+
}

tests/MiniWordTests/IssueTests.cs

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -626,39 +626,6 @@ public void MiniWordHyperLink_List()
626626
Assert.Contains("<Relationship Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink\" Target=\"https://google1.com\"", relsXml);
627627
}
628628

629-
[Fact]
630-
public void MiniWordIfStatement_FirstIf()
631-
{
632-
var path = PathHelper.GetTempFilePath();
633-
var templatePath = PathHelper.GetFile("TestBasicStatement.docx");
634-
var value = new Dictionary<string, object>()
635-
{
636-
["Name"] = new List<MiniWordHyperLink>(){
637-
new MiniWordHyperLink(){
638-
Url = "https://google.com",
639-
Text = "測試連結22!!"
640-
},
641-
new MiniWordHyperLink(){
642-
Url = "https://google1.com",
643-
Text = "測試連結11!!"
644-
}
645-
},
646-
["Company_Name"] = "MiniSofteware",
647-
["CreateDate"] = new DateTime(2021, 01, 01),
648-
["VIP"] = true,
649-
["Points"] = 123,
650-
["APP"] = "Demo APP",
651-
};
652-
MiniWord.SaveAsByTemplate(path, templatePath, value);
653-
//Console.WriteLine(path);
654-
var docXml = Helpers.GetZipFileContent(path, "word/document.xml");
655-
Assert.Contains("First if chosen: MiniSofteware", docXml);
656-
Assert.DoesNotContain("Second if chosen: MaxiSoftware", docXml);
657-
Assert.Contains("Points are greater than 100", docXml);
658-
Assert.Contains("CreateDate is not less than 2021", docXml);
659-
Assert.DoesNotContain("CreateDate is not greater than 2021", docXml);
660-
}
661-
662629
[Fact]
663630
public void MiniWordHyperLink_Array()
664631
{

0 commit comments

Comments
 (0)