Add supprt for if/elseif/else statements inside cell (#487)

* 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
This commit is contained in:
eynarhaji 2023-05-01 20:36:15 +04:00 committed by GitHub
parent 1bd853affe
commit 0559847ecc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 298 additions and 3 deletions

View File

@ -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();
@ -828,7 +855,7 @@ MiniExcel.SaveAsByTemplate(path, templatePath, value);
```
#### 9. Other
#### 10. Other
##### 1. Checking template parameter key

View File

@ -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)

View File

@ -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>

View File

@ -351,9 +351,51 @@ namespace MiniExcelLibs.OpenXml
.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}}}}}";
@ -396,6 +438,34 @@ namespace MiniExcelLibs.OpenXml
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}}}}}";
@ -437,6 +507,33 @@ namespace MiniExcelLibs.OpenXml
}
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;
@ -900,5 +997,100 @@ namespace MiniExcelLibs.OpenXml
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;
}
}
}

View File

@ -693,6 +693,80 @@ namespace MiniExcelTests
}
}
[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()
{