Skip to content

Commit 4a739fc

Browse files
authored
Merge pull request #54 from eynarhaji/main
add support for if statements inside templates
2 parents de67aea + 80fdc49 commit 4a739fc

File tree

9 files changed

+458
-11
lines changed

9 files changed

+458
-11
lines changed

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)
50.1 KB
Binary file not shown.

samples/docx/TestIfStatement.docx

13 KB
Binary file not shown.

src/MiniWord/MiniWord.Implment.cs

Lines changed: 195 additions & 8 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
{
@@ -82,7 +81,9 @@ private static void Generate(this OpenXmlElement xmlElement, WordprocessingDocum
8281
var dicKey = $"{listKey}.{e.Key}";
8382
dic.Add(dicKey, e.Value);
8483
}
85-
84+
85+
ReplaceStatements(xmlElement, tags);
86+
8687
ReplaceText(newTr, docx, tags: dic);
8788
table.Append(newTr);
8889
}
@@ -92,6 +93,8 @@ private static void Generate(this OpenXmlElement xmlElement, WordprocessingDocum
9293
}
9394
}
9495
}
96+
97+
ReplaceStatements(xmlElement, tags);
9598

9699
ReplaceText(xmlElement, docx, tags);
97100
}
@@ -102,6 +105,7 @@ private static void AvoidSplitTagText(OpenXmlElement xmlElement)
102105
var pool = new List<Text>();
103106
var sb = new StringBuilder();
104107
var needAppend = false;
108+
var foreachIncluded = false;
105109
foreach (var text in texts)
106110
{
107111
var clear = false;
@@ -118,10 +122,28 @@ private static void AvoidSplitTagText(OpenXmlElement xmlElement)
118122
// TODO: check tag exist
119123
// TODO: record tag text if without tag then system need to clear them
120124
// TODO: every {{tag}} one <t>for them</t> and add text before first text and copy first one and remove {{, tagname, }}
121-
125+
126+
if(s.StartsWith("{{foreach"))
127+
foreachIncluded = true;
128+
122129
if (!s.StartsWith("{{"))
123130
clear = true;
124-
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)
125147
{
126148
if (sb.Length <= 1000) // avoid too big tag
127149
{
@@ -135,7 +157,9 @@ private static void AvoidSplitTagText(OpenXmlElement xmlElement)
135157
}
136158
}
137159
clear = true;
160+
foreachIncluded = false;
138161
}
162+
139163
}
140164

141165
if (clear)
@@ -173,7 +197,9 @@ private static List<string> GetReplaceKeys(Dictionary<string, object> tags)
173197
if (item2 is Dictionary<string, object> dic)
174198
{
175199
foreach (var item3 in dic.Keys)
200+
{
176201
keys.Add("{{" + item.Key + "." + item3 + "}}");
202+
}
177203
}
178204
break;
179205
}
@@ -186,6 +212,129 @@ private static List<string> GetReplaceKeys(Dictionary<string, object> tags)
186212
return keys;
187213
}
188214

215+
private static void ReplaceStatements(OpenXmlElement xmlElement, Dictionary<string, object> tags)
216+
{
217+
var paragraphs = xmlElement.Descendants<Paragraph>().ToList();
218+
219+
while (paragraphs.Any(s => s.InnerText.Contains("@if")))
220+
{
221+
var ifIndex = paragraphs.FindIndex(0, s => s.InnerText.Contains("@if"));
222+
var endIfFinalIndex = paragraphs.FindIndex(ifIndex, s => s.InnerText.Contains("@endif"));
223+
224+
var statement = paragraphs[ifIndex].InnerText.Split(' ');
225+
226+
var checkStatement = EvaluateStatement(tags[statement[1]], statement[2], statement[3]);
227+
228+
if (checkStatement)
229+
{
230+
for (int i = ifIndex+1; i <= endIfFinalIndex-1; i++)
231+
{
232+
paragraphs[i].Remove();
233+
}
234+
}
235+
236+
paragraphs[ifIndex].Remove();
237+
paragraphs[endIfFinalIndex].Remove();
238+
239+
paragraphs = xmlElement.Descendants<Paragraph>().ToList();
240+
}
241+
}
242+
243+
private static bool EvaluateStatement(object tagValue, string comparisonOperator, string value)
244+
{
245+
var checkStatement = false;
246+
247+
switch (tagValue)
248+
{
249+
case double dtg when double.TryParse(value, out var doubleNumber):
250+
switch (comparisonOperator)
251+
{
252+
case "==":
253+
checkStatement = !dtg.Equals(doubleNumber);
254+
break;
255+
case "!=":
256+
checkStatement = dtg.Equals(doubleNumber);
257+
break;
258+
case ">":
259+
checkStatement = dtg <= doubleNumber;
260+
break;
261+
case "<":
262+
checkStatement = dtg >= doubleNumber;
263+
break;
264+
case ">=":
265+
checkStatement = dtg < doubleNumber;
266+
break;
267+
case "<=":
268+
checkStatement = dtg > doubleNumber;
269+
break;
270+
}
271+
272+
break;
273+
case int itg when int.TryParse(value, out var intNumber):
274+
switch (comparisonOperator)
275+
{
276+
case "==":
277+
checkStatement = !itg.Equals(intNumber);
278+
break;
279+
case "!=":
280+
checkStatement = itg.Equals(intNumber);
281+
break;
282+
case ">":
283+
checkStatement = itg <= intNumber;
284+
break;
285+
case "<":
286+
checkStatement = itg >= intNumber;
287+
break;
288+
case ">=":
289+
checkStatement = itg < intNumber;
290+
break;
291+
case "<=":
292+
checkStatement = itg > intNumber;
293+
break;
294+
}
295+
296+
break;
297+
case DateTime dttg when DateTime.TryParse(value, out var date):
298+
switch (comparisonOperator)
299+
{
300+
case "==":
301+
checkStatement = !dttg.Equals(date);
302+
break;
303+
case "!=":
304+
checkStatement = dttg.Equals(date);
305+
break;
306+
case ">":
307+
checkStatement = dttg <= date;
308+
break;
309+
case "<":
310+
checkStatement = dttg >= date;
311+
break;
312+
case ">=":
313+
checkStatement = dttg < date;
314+
break;
315+
case "<=":
316+
checkStatement = dttg > date;
317+
break;
318+
}
319+
320+
break;
321+
case string stg:
322+
switch (comparisonOperator)
323+
{
324+
case "==":
325+
checkStatement = stg != value;
326+
break;
327+
case "!=":
328+
checkStatement = stg == value;
329+
break;
330+
}
331+
332+
break;
333+
}
334+
335+
return checkStatement;
336+
}
337+
189338
private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocument docx, Dictionary<string, object> tags)
190339
{
191340
var paragraphs = xmlElement.Descendants<Paragraph>().ToArray();
@@ -203,6 +352,19 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen
203352
foreach (var tag in tags)
204353
{
205354
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+
206368
if (isMatch)
207369
{
208370
if (tag.Value is string[] || tag.Value is IList<string> || tag.Value is List<string>)
@@ -223,6 +385,31 @@ private static void ReplaceText(OpenXmlElement xmlElement, WordprocessingDocumen
223385
}
224386
t.Remove();
225387
}
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+
}
226413
else if (tag.Value is MiniWordPicture)
227414
{
228415
var pic = (MiniWordPicture)tag.Value;

0 commit comments

Comments
 (0)