Those search-improving features are just a tip of an iceberg of what was added in the last few months. One of the things I worked on was performance when working with huge datasets. You see, working with one table having 300 users with 30 fields each is a no-brainer. Even a basic ConvertTo-HTML cmdlet with some CSS can be used and display that data. The problems start when you have to display 50000 users, 50000 permissions, or any other data type. What makes it even more complicated if you have five or more tables with the same amount of data. While it may not seem a lot, 50000 objects within a table can generate an 80MB HTML file. I'm sometimes working with data with as much as 250MB in a single HTML file. If you ever tried to open an HTML file of such size generated in earlier versions of PSWriteHTML, it would make your browser want to explode. So what does the new version adds? New-HTMLTableOption cmdlet was added that allows you to control how data for a table is written in HTML. By default when the table is created it uses HTML tags. Using New-HTMLTableOption allows you to change how data is stored – you can choose HTML, JavaScript, or AjaxJSON.
$Users = Get-ADUser -Filter * -Properties LastLogonDate, PasswordLastSet
New-HTML {
New-HTMLTableOption -DataStore JavaScript
New-HTMLTable -DataTable $Users -Title 'Table with Users' -HideFooter -PagingLength 10 -SearchBuilder
} -ShowHTML -FilePath "$PSScriptRoot\Example-Builder.html" -Online
This little change forces PSWriteHTML to generate a table's content as JavaScript data rather than a typical HTML table. This, in turn, with few other features that DataTables provides (that I enable by default), allows for storing a large amount of data without impact on performance. Similarly, the function can also set DataStore to AjaxJSON, which would save datasets for tables in separate JSON files. This means HTML only contains data to configure the table, but the data itself is stored outside of HTML. While it's a nice feature, it requires a WEB Server to work. This can work when you host pages generated by PSWriteHTML somewhere but is quite useless for portable usage. It's important to know that whether this is HTML or JavaScript store, the differences are small and should not affect how your tables look. From a performance perspective, it's a game-changer, though. While working on that functionality, I had to write my own ConvertTo-JSON cmdlet because of PowerShell's limitations in the native versions. Since I now was in control of how data is written to JSON/JavaScript configuration, I could add few more features using the same cmdlet. When pushing data to HTML, many of my problems were how arrays or dates were shown and how I had no control. Usually, if I wanted to be sure how my dates are displayed in HTML, I would need to do preparations outside of PSWriteHTML. In the case of arrays, it was the same. Not anymore!
$Users = Get-ADUser -Filter * -Properties LastLogonDate, PasswordLastSet
New-HTML {
New-HTMLTableOption -DataStore JavaScript -DateTimeFormat 'dd.MM.yyyy HH:mm:ss' -ArrayJoin -ArrayJoinString ','
New-HTMLTable -DataTable $Users -Title 'Table with Users' -HideFooter -PagingLength 10 -SearchBuilder
} -ShowHTML -FilePath "$PSScriptRoot\Example-Builder.html" -Online
Using New-HTMLTableOption and JavaScript store, you can force which time format DataTime objects are written in the HTML and how arrays are treated. Consider this small example
$Objects = @(
[PSCustomObject] @{ Name = 'Przemek'; Tags = 'PowerShell', 'IT', 'SomethingElse'; Value = 15; Date = (Get-Date).AddYears(-20) }
[PSCustomObject] @{ Name = 'Adam'; Tags = 'Rain', 'MorseCode'; Value = 30; Date = (Get-Date).AddYears(-20) }
)
New-HTML {
New-HTMLTable -DataTable $Objects -Title 'Table with Users' -HideFooter -PagingLength 10 -SearchBuilder
} -ShowHTML -FilePath "$PSScriptRoot\Example-TableOptions.html" -Online
As you can see, the Tags property was not displayed properly, and DateTime has a date pattern from a computer that generated it. Thanks to New-HTMLTableOption, you're now able to control this behavior.
$Objects = @(
[PSCustomObject] @{ Name = 'Przemek'; Tags = 'PowerShell', 'IT', 'SomethingElse'; Value = 15; Date = (Get-Date).AddYears(-20) }
[PSCustomObject] @{ Name = 'Adam'; Tags = 'Rain', 'MorseCode'; Value = 30; Date = (Get-Date).AddYears(-20) }
)
New-HTML {
New-HTMLTableOption -DataStore JavaScript -DateTimeFormat 'yyyy.MM.dd' -ArrayJoin -ArrayJoinString ','
New-HTMLTable -DataTable $Objects -Title 'Table with Users' -HideFooter -PagingLength 10 -SearchBuilder
} -ShowHTML -FilePath "$PSScriptRoot\Example-TableOptions.html" -Online
This means you don't have to worry about the array not having the proper format, and you can control date output the way your organization needs. While this feature is mostly used in JavaScript, I've also ported it back to HTML. This means that also for HTML, you can now force Arrays to become string connected by defined char or have a DateTime that suits you. It's especially required for sources where JavaScript can't be used, such as emails.
New-HTML {
New-HTMLTableOption -DataStore HTML -DateTimeFormat 'yyyy-MM-dd' -ArrayJoin -ArrayJoinString ','
New-HTMLTable -DataTable $Objects -Title 'Table with Users' -HideFooter -PagingLength 10 -SearchBuilder
} -ShowHTML -FilePath "$PSScriptRoot\Example-TableOptions.html" -Online
What's important to know, especially for dealing with DateTime formats, is that both PowerShell (.NET) and JavaScript differently handle date to string formatting. New-HTMLTableOption does the conversion of dates using PowerShell – before it is written to file, so in this case, any date-time format used must be the way PowerShell deals with it. However, any HTML functionality (when used in browser) such as sorting or conditional formatting works with JavaScript-based DateTime formatting. In that case, you need to remember to use JavaScript Date Format. There is one exception to this rule – Conditional Formatting with an Inline switch. Inline switch forces conditional formatting to be done on the PowerShell level, which is useful for emails that don't have JavaScript functionality. Time generation is impacted, but full functionality is available. Conditional formatting without an inline switch adds few code lines to generated HTML, and the comparison is made on the HTML level when displaying it on screen. I know it may be confusing but trying to get those two DateTime formats into a single one is too big an effort with many risks that I don't want to take. At least not at this point. To show you how to date sorting of 3 different types is handled, you can see this example.
$DataTable1 = @(
[PscustomObject] @{ DateTest = '2027-09-12'; DateUS = '3/31/2020'; Dates = (Get-Date).AddDays(2); BoolAsString = 'true'; BoolTest = $true; Test = 'ABC'; Test2 = 'Name1'; Test3 = 'Name3'; 'Test4' = 1 }
[PscustomObject] @{ DateTest = '2021-01-12'; DateUS = '3/23/2020'; Dates = (Get-Date).AddDays(0); BoolAsString = 'false'; BoolTest = $false; Test = 'Opps'; Test2 = 'Name2'; Test3 = 'Name2'; 'Test4' = 2 }
[PscustomObject] @{ DateTest = '1982-08-15'; DateUS = '3/5/2020'; Dates = (Get-Date).AddDays(-7); BoolAsString = 'false'; BoolTest = $null; Test = 'Oh No'; Test2 = 'Name3'; Test3 = 'KitKat'; 'Test4' = 3 }
[PscustomObject] @{ DateTest = '2021-03-12'; DateUS = '4/5/2020'; Dates = (Get-Date).AddDays(13); BoolAsString = 'null'; BoolTest = $true; Test = 'Name'; Test2 = 'Name4'; Test3 = 'Name3'; 'Test4' = 0 }
[PscustomObject] @{ DateTest = '2021-03-12'; DateUS = '3/15/2020'; Dates = (Get-Date).AddDays(5); BoolAsString = 'true'; BoolTest = $false; Test = 'Name'; Test2 = 'Name5'; Test3 = 'Name4'; 'Test4' = $null }
[PscustomObject] @{ DateTest = '2025-01-17'; DateUS = '3/5/2020'; Dates = (Get-Date).AddDays(0); BoolAsString = 'True'; BoolTest = $false; Test = 'Name'; Test2 = 'Name2'; Test3 = 'KitKat'; 'Test4' = 10 }
[PscustomObject] @{ DateTest = '2021-03-12'; DateUS = '7/5/2020'; Dates = (Get-Date).AddDays(21); BoolAsString = 'true'; BoolTest = $true; Test = 'Name'; Test2 = 'Name2'; Test3 = 'Bounty'; 'Test4' = 5 }
[PscustomObject] @{ DateTest = '2021-12-12'; DateUS = '12/5/2021'; Dates = (Get-Date).AddDays(5); BoolAsString = 'True'; BoolTest = $true; Test = 'Name'; Test2 = 'Name2'; Test3 = 'Test'; 'Test4' = 0 }
)
New-HTML {
New-HTMLTableOption -DataStore HTML -ArrayJoin -BoolAsString
New-HTMLTable -DataTable $DataTable1 -DateTimeSortingFormat 'DD.MM.YYYY HH:mm:ss', 'M/D/YYYY', 'YYYY-MM-DD'
} -ShowHTML -FilePath $PSScriptRoot\Example-DateTimeSorting.html -Online
Sorting is smart enough to figure out which column has which date and act accordingly.