Command generated Form in v3

I would like to share something I worked on the last days.
If many people are interested in I can extend it and will open a GH repo.
But this is only my beta show off:

A UD Form can be generated by a command. I havent used it for things like New-ADUser or Set-ADUser but can also be possible. I just used this for my own PS Module this is jsut wrapping PS into a REST request.

But here is my example which is making a US form from the commands Test-CommandForm parameter:

function Test-CommandForm
    {
        param 
        (
            [Parameter(ParameterSetName= "Default")]
            [String]$String,

            [Parameter(ParameterSetName= "Default")]
            [ValidateSet('TRACE', 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'FATAL')]
            [String]$StringValidateSet,

            [Parameter(ParameterSetName= "Default")]
            [bool]$Bool,

            [Parameter(ParameterSetName= "Default")]
            [DataBinding(Command='Get-ReferenceObjectData', LabelAttribute = 'Name', ValueAttribute = 'ID')]
            $ReferenceObject
        )

        Show-UDToast -Duration 10000 -Message "do some logic with passed parameter"
    }

 function Get-ReferenceObjectData
    {
        @(
            [PSCustomObject]@{ID=(New-Guid); Name='test1'}
            [PSCustomObject]@{ID=(New-Guid); Name='test2'}
            [PSCustomObject]@{ID=(New-Guid); Name='test3'}
            [PSCustomObject]@{ID=(New-Guid); Name='test4'}
            [PSCustomObject]@{ID=(New-Guid); Name='test5'}
        )
    }
$Command = 'Test-CommandForm'
    New-UDForm -Content {
        New-UDGeneratedFormBody -Command $Command
    } -OnSubmit {
        
        $Parameters = ConvertFrom-Json $Body
        $Parameters = ConvertTo-HashTable -Object $Parameters

        & "$Command" @Parameters
    }

image

Currently only the following types of parameters are allowed:

  • string > textbox
  • string with a ValidationSet > select
  • bool > checkbox
  • parameter which requires an object from another command (e.g. Get-ReferenceObjectData) > select

This can be really helpful in order to minimize the code for a dashboard and automate it.
I used this currently for all ma New- or Set- commands for my own API PS Module.

This is how it looks like in my project by just using the following lines of code I can have a create and edit form for all types of objects as soon as there is an existing PS command.

PS: the commands New-UDNewButton and New-UDEditButton are not included in this code as it is just a show case how the “generated form by command” can be used.

New-UDNewButton -Command "New-ADWDWProject" -ObjectType "Project" -RedirectUrl '/datawarehouse/project'

New-UDEditButton -Command 'Set-ADWDWProject' -ObjectDisplayName $Item.Name -ObjectID $Item.ID -RedirectUrl '/datawarehouse/project'

This is the full code wrapped into a demo:

Import-Module UniversalDashboard.Community -Force

if(Get-UDDashboard)
{
    Get-UDDashboard | Stop-UDDashboard
}

#region helper function
function ConvertTo-HashTable
{
    param 
    (
        [Parameter (Mandatory = $true, ParameterSetName = "Default")]
        $Object
    )

    $Object.PSObject.Properties | foreach -begin {$h=@{}} -process {$h."$($_.Name)" = $_.Value} -end {$h}
}
#endregion

$Dashboard = New-UDDashboard -Title 'GenerateCommandForm' -Content {
    #region generate function
    function New-UDGeneratedFormBody
    {
        param 
        (
            [Parameter (Mandatory = $true, ParameterSetName = "Default")]
            [string]$Command,
    
            [Parameter (Mandatory = $false, ParameterSetName = "Default")]
            [string[]]$ExcludedParameters = @()
        )

        #region custom attribute
        Class DataBindingAttribute : Attribute {

            [string]$Command
            [string]$LabelAttribute
            [string]$ValueAttribute

            DataBindingAttribute()
            {
            }

            DataBindingAttribute([string]$Command, [string]$LabelAttribute, [string]$ValueAttribute)
            {
                $this.Command = $Command
                $this.LabelAttribute = $LabelAttribute
                $this.ValueAttribute = $ValueAttribute
            }
        }
        #endregion

        $Parameters = (Get-Command $Command).Parameters.keys| %{(Get-Command $Command).Parameters["$_"]}
        $Parameters = $Parameters | ?{!$_.ParameterSets["__AllParameterSets"]}
    
        #exclude parameters
        $Parameters = $Parameters | ?{$_.Name -notin $ExcludedParameters}
    
        foreach($Parameter in $Parameters)
        {
            $ValidateSetAttribute = $Parameter.Attributes | ?{$_.TypeId.Name -eq 'ValidateSetAttribute'}
            $DataBindingAttribute = $Parameter.Attributes | ?{$_.TypeId.Name -eq 'DataBindingAttribute'}
    
            if($DataBindingAttribute)
            {
                $Data = @(& $DataBindingAttribute.Command)
                New-UDSelect -Id $Parameter.Name -Label $Parameter.Name -Option {
                    $Data | Sort $DataBindingAttribute.LabelAttribute | %{New-UDSelectOption -Name $_."$($DataBindingAttribute.LabelAttribute)" -Value $_."$($DataBindingAttribute.ValueAttribute)"}
                }            
            }
            elseif($ValidateSetAttribute)
            {
                New-UDSelect -Id $Parameter.Name -Label $Parameter.Name -Option {
                    $ValidateSetAttribute.ValidValues | Sort | %{New-UDSelectOption -Name $_ -Value $_}
                }            
            }
            elseif($Parameter.ParameterType -in @('string', 'System.String'))
            {
                New-UDTextbox -Id $Parameter.Name -Label $Parameter.Name
            }
            elseif($Parameter.ParameterType -in @('bool', 'System.Boolean'))
            {
                New-UDCheckbox -Id $Parameter.Name -Label $Parameter.Name
            }
            else
            {
                throw "unexpected command parameter [$($Parameter.Name)] type [$($Parameter.ParameterType)]"
            }
        }
    }
    #endregion

    function Get-ReferenceObjectData
    {
        @(
            [PSCustomObject]@{ID=(New-Guid); Name='test1'}
            [PSCustomObject]@{ID=(New-Guid); Name='test2'}
            [PSCustomObject]@{ID=(New-Guid); Name='test3'}
            [PSCustomObject]@{ID=(New-Guid); Name='test4'}
            [PSCustomObject]@{ID=(New-Guid); Name='test5'}
        )
    }

    function Test-CommandForm
    {
        param 
        (
            [Parameter(ParameterSetName= "Default")]
            [String]$String,

            [Parameter(ParameterSetName= "Default")]
            [ValidateSet('TRACE', 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'FATAL')]
            [String]$StringValidateSet,

            [Parameter(ParameterSetName= "Default")]
            [bool]$Bool,

            [Parameter(ParameterSetName= "Default")]
            [DataBinding(Command='Get-ReferenceObjectData', LabelAttribute = 'Name', ValueAttribute = 'ID')]
            $ReferenceObject
        )

        Show-UDToast -Duration 10000 -Message "do some logic with passed parameter"
    }

    #region main logic
    $Command = 'Test-CommandForm'
    New-UDForm -Content {
        New-UDGeneratedFormBody -Command $Command
    } -OnSubmit {
        
        $Parameters = ConvertFrom-Json $Body
        $Parameters = ConvertTo-HashTable -Object $Parameters

        & "$Command" @Parameters
    }
    #endregion
}
Start-UDDashboard -Name 'GenerateCommandForm' -Dashboard $Dashboard -Port 1000 -Wait
8 Likes

Wow, I had never thought of doing something like this, but it could really simplify some things that I do. When you present the parameters, do you also have a way to show which ones are mandatory?

I thought about it but havent implemented it yet … but should be possible with the Validation script block of UDForm. Maybe I can have a look on this tomorrow. :slight_smile:

1 Like

This is really really cool! Thanks for sharing