diff --git a/README.md b/README.md index 39831694..a0e2f0f2 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/docs/README.md b/docs/README.md index 8efd8352..b1a142d1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -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) diff --git a/samples/xlsx/TestTemplateBasicIEmumerableFillConditional.xlsx b/samples/xlsx/TestTemplateBasicIEmumerableFillConditional.xlsx new file mode 100644 index 00000000..fce727bc Binary files /dev/null and b/samples/xlsx/TestTemplateBasicIEmumerableFillConditional.xlsx differ diff --git a/src/MiniExcel/MiniExcelLibs.csproj b/src/MiniExcel/MiniExcelLibs.csproj index 31cb769c..9675585a 100644 --- a/src/MiniExcel/MiniExcelLibs.csproj +++ b/src/MiniExcel/MiniExcelLibs.csproj @@ -1,7 +1,7 @@  net45;netstandard2.0 - 1.30.2 + 1.30.3 MiniExcel diff --git a/src/MiniExcel/OpenXml/ExcelOpenXmlTemplate.Impl.cs b/src/MiniExcel/OpenXml/ExcelOpenXmlTemplate.Impl.cs index f0a15989..222ab8ba 100644 --- a/src/MiniExcel/OpenXml/ExcelOpenXmlTemplate.Impl.cs +++ b/src/MiniExcel/OpenXml/ExcelOpenXmlTemplate.Impl.cs @@ -351,9 +351,51 @@ private void WriteSheetXml(Stream stream, XmlDocument doc, XmlNode sheetData, bo .Replace($"{{{{$rowindex}}}}", newRowIndex.ToString()) .AppendFormat(@"", 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; + + 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 @@ 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}}}}}"; @@ -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; @@ -900,5 +997,100 @@ private void UpdateDimensionAndGetRowsInfo(Dictionary 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; + } } } \ No newline at end of file diff --git a/tests/MiniExcelTests/MiniExcelTemplateAsyncTests.cs b/tests/MiniExcelTests/MiniExcelTemplateAsyncTests.cs index 2bf9c322..dc2cb125 100644 --- a/tests/MiniExcelTests/MiniExcelTemplateAsyncTests.cs +++ b/tests/MiniExcelTests/MiniExcelTemplateAsyncTests.cs @@ -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() + { + ["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() + { + ["employees"] = dt + }; + await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value); + + var demension = Helpers.GetFirstSheetDimensionRefValue(path); + Assert.Equal("A1:B18", demension); + } + } + [Fact] public async Task TemplateTest() {