Skip to content

Commit

Permalink
Add supprt for if/elseif/else statements inside cell (#487)
Browse files Browse the repository at this point in the history
* add support for grouped cells

* fix for rows after endgroup

* minor fix for text after grouping

* fix unit tests

* fix unit tests and docs

* minor fix

* Add support to vertical merge cells

* minor fixes in merge cells

* minor fix

* fix complex scenario

* finalize changes

* Add support for if/elseif/else statements
  • Loading branch information
eynarhaji authored May 1, 2023
1 parent 1bd853a commit 0559847
Show file tree
Hide file tree
Showing 6 changed files with 298 additions and 3 deletions.
31 changes: 29 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -808,8 +808,35 @@ After

![without_group_after](https://user-images.githubusercontent.com/38832863/218646974-4a3c0e07-7c66-4088-ad07-b4ad3695b7e1.PNG)

#### 8. If/ElseIf/Else Statements inside cell

#### 8. DataTable as parameter
Rules:
1. Supports DateTime, Double, Int with ==, !=, >, >=, <, <= operators.
2. Supports String with ==, != operators.
3. Each statement should be new line.
4. Single space should be added before and after operators.
5. There shouldn't be new line inside of statements.
6. Cell should be in exact format as below.

```csharp
@if(name == Jack)
{{employees.name}}
@elseif(name == Neo)
Test {{employees.name}}
@else
{{employees.department}}
@endif
```

Before

![if_before](https://user-images.githubusercontent.com/38832863/235360606-ca654769-ff55-4f5b-98d2-d2ec0edb8173.PNG)

After

![if_after](https://user-images.githubusercontent.com/38832863/235360609-869bb960-d63d-45ae-8d64-9e8b0d0ab658.PNG)

#### 9. DataTable as parameter

```csharp
var managers = new DataTable();
Expand All @@ -828,7 +855,7 @@ MiniExcel.SaveAsByTemplate(path, templatePath, value);
```


#### 9. Other
#### 10. Other

##### 1. Checking template parameter key

Expand Down
2 changes: 2 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

---

### 1.30.3
- [New] support if/else if/else statements inside cell (via @eynarhaji)

### 1.30.2
- [New] support grouped rows (via @eynarhaji)
Expand Down
Binary file not shown.
2 changes: 1 addition & 1 deletion src/MiniExcel/MiniExcelLibs.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net45;netstandard2.0</TargetFrameworks>
<Version>1.30.2</Version>
<Version>1.30.3</Version>
</PropertyGroup>
<PropertyGroup>
<AssemblyName>MiniExcel</AssemblyName>
Expand Down
192 changes: 192 additions & 0 deletions src/MiniExcel/OpenXml/ExcelOpenXmlTemplate.Impl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -351,9 +351,51 @@ private void WriteSheetXml(Stream stream, XmlDocument doc, XmlNode sheetData, bo
.Replace($"{{{{$rowindex}}}}", newRowIndex.ToString())
.AppendFormat(@"</{0}>", row.Name);

var rowXmlString = rowXml.ToString();
var extract = "";
var newCellValue = "";

var ifIndex = rowXmlString.IndexOf("@if", StringComparison.Ordinal);
var endifIndex = rowXmlString.IndexOf("@endif", StringComparison.Ordinal);

if (ifIndex != -1 && endifIndex != -1)
{
extract = rowXmlString.Substring(ifIndex, endifIndex - ifIndex + 6);
}

var lines = extract.Split('\n');

if (rowInfo.IsDictionary)
{
var dic = item as IDictionary<string, object>;

for(var i = 0; i < lines.Length; i++)
{
if(lines[i].Contains("@if") || lines[i].Contains("@elseif"))
{
var newLines = lines[i].Replace("@elseif(", "").Replace("@if(", "").Replace(")", "").Split(' ');

var value = dic[newLines[0]];
var evaluation = EvaluateStatement(value, newLines[1], newLines[2]);

if (evaluation)
{
newCellValue += lines[i + 1];
break;
}
}
else if(lines[i].Contains("@else"))
{
newCellValue += lines[i + 1];
break;
}
}

if (!string.IsNullOrEmpty(newCellValue))
{
rowXml.Replace(extract, newCellValue);
}

foreach (var propInfo in rowInfo.PropsMap)
{
var key = $"{{{{{rowInfo.IEnumerablePropName}.{propInfo.Key}}}}}";
Expand Down Expand Up @@ -396,6 +438,34 @@ private void WriteSheetXml(Stream stream, XmlDocument doc, XmlNode sheetData, bo
else if (rowInfo.IsDataTable)
{
var datarow = item as DataRow;

for(var i = 0; i < lines.Length; i++)
{
if(lines[i].Contains("@if") || lines[i].Contains("@elseif"))
{
var newLines = lines[i].Replace("@elseif(", "").Replace("@if(", "").Replace(")", "").Split(' ');

var value = datarow[newLines[0]];
var evaluation = EvaluateStatement(value, newLines[1], newLines[2]);

if (evaluation)
{
newCellValue += lines[i + 1];
break;
}
}
else if(lines[i].Contains("@else"))
{
newCellValue += lines[i + 1];
break;
}
}

if (!string.IsNullOrEmpty(newCellValue))
{
rowXml.Replace(extract, newCellValue);
}

foreach (var propInfo in rowInfo.PropsMap)
{
var key = $"{{{{{rowInfo.IEnumerablePropName}.{propInfo.Key}}}}}";
Expand Down Expand Up @@ -437,6 +507,33 @@ private void WriteSheetXml(Stream stream, XmlDocument doc, XmlNode sheetData, bo
}
else
{
for(var i = 0; i < lines.Length; i++)
{
if(lines[i].Contains("@if") || lines[i].Contains("@elseif"))
{
var newLines = lines[i].Replace("@elseif(", "").Replace("@if(", "").Replace(")", "").Split(' ');

var value = rowInfo.PropsMap[newLines[0]].PropertyInfo.GetValue(item);
var evaluation = EvaluateStatement(value, newLines[1], newLines[2]);

if (evaluation)
{
newCellValue += lines[i + 1];
break;
}
}
else if(lines[i].Contains("@else"))
{
newCellValue += lines[i + 1];
break;
}
}

if (!string.IsNullOrEmpty(newCellValue))
{
rowXml.Replace(extract, newCellValue);
}

foreach (var propInfo in rowInfo.PropsMap)
{
var prop = propInfo.Value.PropertyInfo;
Expand Down Expand Up @@ -900,5 +997,100 @@ private void UpdateDimensionAndGetRowsInfo(Dictionary<string, object> inputMaps,
dimension.SetAttribute("ref", $"A1:{letter}{digit + maxRowIndexDiff}");
}
}

private static bool EvaluateStatement(object tagValue, string comparisonOperator, string value)
{
var checkStatement = false;

switch (tagValue)
{
case double dtg when double.TryParse(value, out var doubleNumber):
switch (comparisonOperator)
{
case "==":
checkStatement = dtg.Equals(doubleNumber);
break;
case "!=":
checkStatement = !dtg.Equals(doubleNumber);
break;
case ">":
checkStatement = dtg > doubleNumber;
break;
case "<":
checkStatement = dtg < doubleNumber;
break;
case ">=":
checkStatement = dtg >= doubleNumber;
break;
case "<=":
checkStatement = dtg <= doubleNumber;
break;
}

break;
case int itg when int.TryParse(value, out var intNumber):
switch (comparisonOperator)
{
case "==":
checkStatement = itg.Equals(intNumber);
break;
case "!=":
checkStatement = !itg.Equals(intNumber);
break;
case ">":
checkStatement = itg > intNumber;
break;
case "<":
checkStatement = itg < intNumber;
break;
case ">=":
checkStatement = itg >= intNumber;
break;
case "<=":
checkStatement = itg <= intNumber;
break;
}

break;
case DateTime dttg when DateTime.TryParse(value, out var date):
switch (comparisonOperator)
{
case "==":
checkStatement = dttg.Equals(date);
break;
case "!=":
checkStatement = !dttg.Equals(date);
break;
case ">":
checkStatement = dttg > date;
break;
case "<":
checkStatement = dttg < date;
break;
case ">=":
checkStatement = dttg >= date;
break;
case "<=":
checkStatement = dttg <= date;
break;
}

break;
case string stg:
switch (comparisonOperator)
{
case "==":
checkStatement = stg == value;
break;
case "!=":
checkStatement = stg != value;
break;
}

break;
}

return checkStatement;
}
}
}
74 changes: 74 additions & 0 deletions tests/MiniExcelTests/MiniExcelTemplateAsyncTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,80 @@ public async Task TestIEnumerableGrouped()
}
}

[Fact]
public async Task TestIEnumerableConditional()
{
{
var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx");
var templatePath = @"../../../../../samples/xlsx/TestTemplateBasicIEmumerableFillConditional.xlsx";

//1. By POCO
var value = new
{
employees = new[] {
new {name="Jack",department="HR"},
new {name="Lisa",department="HR"},
new {name="John",department="HR"},
new {name="Mike",department="IT"},
new {name="Neo",department="IT"},
new {name="Loan",department="IT"}
}
};
await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value);

var demension = Helpers.GetFirstSheetDimensionRefValue(path);
Assert.Equal("A1:B18", demension);
}

{
var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx");
var templatePath = @"../../../../../samples/xlsx/TestTemplateBasicIEmumerableFillConditional.xlsx";

//2. By Dictionary
var value = new Dictionary<string, object>()
{
["employees"] = new[] {
new {name="Jack",department="HR"},
new {name="Jack",department="HR"},
new {name="John",department="HR"},
new {name="John",department="IT"},
new {name="Neo",department="IT"},
new {name="Loan",department="IT"}
}
};
await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value);

var demension = Helpers.GetFirstSheetDimensionRefValue(path);
Assert.Equal("A1:B18", demension);
}

{
var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx");
var templatePath = @"../../../../../samples/xlsx/TestTemplateBasicIEmumerableFillConditional.xlsx";

//3. By DataTable
var dt = new DataTable();
{
dt.Columns.Add("name");
dt.Columns.Add("department");
dt.Rows.Add("Jack", "HR");
dt.Rows.Add("Lisa", "HR");
dt.Rows.Add("John", "HR");
dt.Rows.Add("Mike", "IT");
dt.Rows.Add("Neo", "IT");
dt.Rows.Add("Loan", "IT");
}
var value = new Dictionary<string, object>()
{
["employees"] = dt
};
await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value);

var demension = Helpers.GetFirstSheetDimensionRefValue(path);
Assert.Equal("A1:B18", demension);
}
}

[Fact]
public async Task TemplateTest()
{
Expand Down

0 comments on commit 0559847

Please sign in to comment.