Get New-UDTreeNode selected value

Product: PowerShell Universal
Version: 1.4.7

Good day!

I’m currently in the process of building a stepper to walk my offshore team through a few things using PowerCLI. In my code, I am building a folder tree (if you’ve used vCenter, it’s basically the VM folders), which works fine. Where I am running into problems is getting the value of the selected node to be able to pass to a subsequent script.

I tried using the -OnNodeClicked option to view any $EventData, but I get nothing. Does anyone know how to retrieve the selected node value from a UDTreeNode?

Code below for reference:

        New-UDStep -OnLoad {

        $json = ConvertFrom-Json $Body

        $vcServer = $json.context.vcenterSelect

        $vcCred = $json.context.vcenterLogin

        $ssoUser = $json.context.ssoUser

        $ssoRsaToken = $json.context.ssoRSAToken

        # First, we build the tree for the VM folders.

        New-UDGrid -Container -Content {

            New-UDGrid -Item -ExtraSmallSize 3 -Content {

                New-UDCard -Elevation 5 -Title "VM Folder Selection" -Content {

                    $json = ConvertFrom-Json $Body

                    $vcServer = $json.context.vcenterSelect

                    $vcCred = $json.context.vcenterLogin

                    $datacenters = Get-Datacenter -Server $Cache:vcSession

                    [System.Collections.ArrayList]$subFolderTreeList = @()

                    [System.Collections.ArrayList]$folderTreeList = @()

                    

                    New-UDTreeView -Id 'FolderSelect' -Node {

                        #Do this block for each virtual datacenter.

                        foreach ($datacenter in $datacenters) {

                            # Get and generate the vCenter vmFolder MoRef from the ExtensionData.

                            $topParentFolderRef = (Get-Datacenter -Server $Cache:vcSession).ExtensionData.vmFolder

                            $topParentFolder = "$($topParentFolderRef.Type)-$($topParentFolderRef.value)"

                            

                            # Get all the vm Folders.

                            $allVMFolders = Get-Folder -Server $Cache:vcSession -Type VM | Get-View

                            # Get all the top level folders, sorted by name                        

                            $topFolders = $allVMFolders | ?{ $_.Parent -eq $topParentFolder } | Sort-Object Name

                            

                            # Get each child folder (any folder where the datacenter vmFolder MoRef is NOT the Parent).

                            $childFolders = $allVMFolders | ?{ $_.Parent -ne $topParentFolder }

                            # For each child folder, add the Name and PARENT MoRef to the ArrayList.                        

                            foreach ($childFolder in $childFolders) {                    

                                if ($childFolder.Parent -in $topFolders.MoRef) { 

                                    $parent = ($topFolders | ?{$_.MoRef -eq $childFolder.Parent}).MoRef

                                    $subFolderTreeList.Add(@{$childFolder.Name = $parent}) | Out-Null

                                }

                            }

                            # Now we build the tree. For each top level folder (folder where the Parent MoRef is the datacenter's vmFolder MoRef),

                            # Add an entry that does not expand. Otherwise, add the top level entry followed by each of the child entries.                        

                            foreach ($item in $topFolders) {

                                $children = $false

                                for ($i=0; $i -lt $subFolderTreeList.Keys.Count; $i++) { 

                                    if ($subFolderTreeList.Values[$i] -eq $item.MoRef) { 

                                        $children = $true

                                    }

                                }

                                if ($children) {

                                    New-UDTreeNode -Name $item.Name -id $item.MoRef -Children {

                                        for ($i=0; $i -lt $subFolderTreeList.Keys.Count; $i++) { 

                                            if ($subFolderTreeList.Values[$i] -eq $item.MoRef) {

                                                # Since we have the folder name, but only it's PARENT MoRef, we need to determine the folder's

                                                # actual MoRef here so we can pass the location properly. This helps ensure that we have the 

                                                # correct path/location when there are multiple sub-folders with the same name under different

                                                # parent folders.

                                                $folderID = (Get-Folder -Server $Cache:vcSession -Type VM -Name $subFolderTreeList.Keys[$i] `

                                                | Get-View | ?{ $_.Parent -eq $item.MoRef }).MoRef

                                                New-UDTreeNode -Name $subFolderTreeList.Keys[$i] -id $folderID

                                            }

                                        }

                                    }

                                }

                                else {

                                    New-UDTreeNode -Name $item.Name -id $item.MoRef

                                }

                            }           

                        }

                    } -OnNodeClicked { 

                        Show-UDModal -Content { "Data is $EventData" }

                    }

                }                   

            }

            New-UDGrid -Item -ExtraSmallSize 3 -Content {

                New-UDCard -Elevation 5 -Title "Local Admin Account" -Content {

                    New-UDTextbox -Id 'custUser' -Label "Customer Username"

                    New-UDTextbox -Id 'custPassword' -Label "Customer Password" -Type password                        

                }                   

            }                                     

        }

    } -Label "Choose customer and Local User/Password" # End Step 2

Second this. I too have been looking for a way to do this with Active Directory OU Structures… I want to be able to pass the selected tree node to a variable.

The problem here is that the TreeView is expecting something returned from OnNodeClicked.

What I’m finding is that if I do something like this, then the children nodes are never shown because my OnNodeClicked event handler doesn’t return anything.

New-UDDashboard -Title "Hello, World!" -Content {
    New-UDTreeView -Node { 
        New-UDTreeNode -Name root -Children {
            New-UDTreeNode -Name 'Level 2 - Item 1' 
            New-UDTreeNode -Name 'Level 2 - Item 2'
        } 
    } -OnNodeClicked {
        Show-UDToast ($EventData | ConvertTo-Json)
    }
}

The work around is to make it dynamic rather than defining the tree structure right away.

New-UDDashboard -Title "Hello, World!" -Content {
    New-UDTreeView -Node { 
        New-UDTreeNode -Name Servers
    } -OnNodeClicked {
        if ($EventData.Name -eq 'Servers') {
               New-UDTreeNode -Name Server1
               New-UDTreeNode -Name Server2
               New-UDTreeNode -Name Server3
        }
        else {
               Show-UDToast $EventData.Name
        }
    }
}

Is that what you are looking for?

I think we can improve OnNodeClicked to work with a static tree structure but will have to fix the JS component.

1 Like

Hi, @adam!

That’s my experience as well (using the OnNodeClicked event, the child nodes disappear). I’ll give the dynamic approach a try and see how it goes.Hopefully this will get me past my sticking point.

Thanks for the tip!

How do I use this approach for nested nodes? Say for example an OU structure that has many sub containers?

I think what I’m asking is; Can you use the -OnNodeClicked for each of the New-UDTreeNode so that new TreeNodes appear when they are clicked by replicating the same if statement in your example?

Struggling to get my head around it!

EDIT: Just looking at this example here for Dynamic Tree View and I think this is what I’m looking for. Can we still get the $EventData.name using this method?

TIA

Yep. $EventData.Name is available using that method.

Hi, @adam!

I just wanted to circle back to let you know I got this working with your recommendation!

For anyone else getting stumped on this, here’s the code snippet of my tree view:

      New-UDTreeView -Id 'FolderSelect' -Node {

                        #Do this block for each virtual datacenter.

                        foreach ($datacenter in $datacenters) {

                            # Get and generate the vCenter vmFolder MoRef from the ExtensionData.

                            $topParentFolderRef = (Get-Datacenter -Server $Cache:vcSession).ExtensionData.vmFolder

                            $topParentFolder = "$($topParentFolderRef.Type)-$($topParentFolderRef.value)"

                            

                            # Get all the vm Folders.

                            $allVMFolders = Get-Folder -Server $Cache:vcSession -Type VM | Get-View

                            # Get all the top level folders, sorted by name                        

                            $topFolders = $allVMFolders | ?{ $_.Parent -eq $topParentFolder } | Sort-Object Name

                            

                            # Get each child folder (any folder where the datacenter vmFolder MoRef is NOT the Parent).

                            $childFolders = $allVMFolders | ?{ $_.Parent -ne $topParentFolder }

                            # For each child folder, add the Name and PARENT MoRef to the ArrayList.                        

                            foreach ($childFolder in $childFolders) {                    

                                if ($childFolder.Parent -in $topFolders.MoRef) { 

                                    $parent = ($topFolders | ?{$_.MoRef -eq $childFolder.Parent}).MoRef

                                    $subFolderTreeList.Add(@{$childFolder.Name = $parent}) | Out-Null

                                }

                            }

                            # Now we build the tree. For each top level folder (folder where the Parent MoRef is the datacenter's vmFolder MoRef),

                            # Add an entry that does not expand. Otherwise, add the top level entry followed by each of the child entries.                        

                            foreach ($item in $topFolders) { New-UDTreeNode -Name $item.Name -id $item.MoRef }           

                        }

                    } -OnNodeClicked {

                        $parentNode = $EventData.Name

                        $parentNodeID = $EventData.ID

                        $Cache:selectedVmFolderID = $EventData.id

                        for ($i=0; $i -lt $subFolderTreeList.Keys.Count; $i++) { 

                            if ($subFolderTreeList.Values[$i] -eq $parentNodeID) { 

                                $folderID = (Get-Folder -Server $Cache:vcSession -Type VM -Name $subFolderTreeList.Keys[$i] `

                                | Get-View | ?{ $_.Parent -eq $parentNodeID }).MoRef

                                New-UDTreeNode -Name $subFolderTreeList.Keys[$i] -id $folderID

                            }

                        }

                    }

Now please can you do one for Active Directory OU’s? :smiley: I’m stumped!

@dank42 - I had some time today since I played hooky from work. Give it a shot.

# Import Required Modules
Import-Module ActiveDirectory

New-UDStepper -Steps {

# Step 1 - Enter domain name
New-UDStep -OnLoad {
    # Enter Domain name
    New-UDGrid -Container -Content {                           
        New-UDGrid -Item -Content {
            New-UDCard -Elevation 5 -Title "Enter Domain Name" -Content {
                New-UDTextbox -Id 'domainName' -Label "Domain Name"
            }   
        }
    }
} -Label "Enter Domain Name" # End Step 1

# Build the OU tree
New-UDStep -OnLoad {
    New-UDGrid -Container -Content {                           
        New-UDGrid -Item -Content {
            New-UDCard -Elevation 5 -Title "AD OUs" -Content {

                $json = ConvertFrom-Json $Body
                $domain = $json.Context.domainName

                #Craete a variable for the domain DN
                $domainDn = (Get-ADDomain -Identity $Domain).DistinguishedName

                $ouList = Get-ADOrganizationalUnit -Filter * -SearchScope Subtree -Server $Domain -Properties ParentGuid -ErrorAction SilentlyContinue | `
                    Sort-Object Name | Select Name,DistinguishedName,ParentGuid 

                [System.Collections.ArrayList]$allOUs = @()

                #Check that we have some output
                if ($ouList) {

                    #Loop through each OU, create a custom object and add to $allOUs
                    foreach ($item in $ouList){
                        $ParentGuid = ([GUID]$item.ParentGuid).Guid
                        $ParentObject = Get-ADObject -Identity $ParentGuid -Server $Domain -ErrorAction SilentlyContinue

                        if ($ParentObject) {

                            $OuInfo = [PSCustomObject]@{
                                Name = $item.Name
                                DistinguishedName = $item.DistinguishedName
                                ParentDn = $ParentObject.DistinguishedName
                                DomainDn = $DomainDn
                        
                            } 

                            $allOUs += $OuInfo
                        }  
                    } 
                }

                New-UDTreeView -Id 'ouTree' -Node {

                    foreach ($ou in $allOUs) {
                        if ( $ou.ParentDn -eq $domainDn ) { New-UDTreeNode -Name $ou.Name -id $ou.DistinguishedName }
                    }
                } -OnNodeClicked {
                    $childOUs = $allOUs | ?{ $_.ParentDn -eq $EventData.id } | Sort-Object Name
                    foreach ($child in $childOUs) { 
                        #Show-UDToast -Message $child.ParentDn -Duration 5000
                        New-UDTreeNode -Name $child.Name -id $child.DistinguishedName
                    }  
                    #Show-UDToast -Message $EventData.id -Duration 5000
                }
            }
        }
    }
} -Label "Select OU" # End Step 2  
} -OnFinish {
  New-UDTypography -Text 'Nice! You did it!' -Variant h3
  New-UDElement -Tag 'div' -Id 'result' -Content {$Body}    
} # End Stepper

OU Structure in AD
image

OU Structure in Dasboard

1 Like

@adam @dank42

Just wanted to circle back with what I think will be the final code for my original ask. Thanks to @adam for the tip about making the sub-trees dynamic, and thanks to @dank42 for making me rethink my code. I was able to apply the AD code I came up with to VMware as well!

Final Dynamic Tree Code

    $datacenters = Get-Datacenter -Server $Session:vcSession
                
    #Do this block for each virtual datacenter.
    foreach ($datacenter in $datacenters) {

# Get and generate the vCenter vmFolder MoRef from the ExtensionData.
$topParentFolder = (Get-Datacenter -Server $Session:vcSession).ExtensionData.vmFolder.ToString()
#$topParentFolder = "$($topParentFolderRef.Type)-$($topParentFolderRef.value)"

# Get all the vm Folders.
$vmFolders = Get-Folder -Server $Session:vcSession -Type VM | Get-View

[System.Collections.ArrayList]$allFolders = @()      

#Check that we have output
if ($vmFolders) {

	#Loop through each ITEM, create a custom object and add to $allFolders
	foreach ($item in $vmFolders){

		$ParentFolderName = (Get-Folder -Server $Session:vcSession -id $item.Parent).Name

		if ($ParentFolderName) {

			$folderInfo = [PSCustomObject]@{
				FolderName = $item.Name
				FolderMoRef = $item.MoRef.ToString()
				ParentFolderMoRef = $item.Parent.ToString()
				ParentFolderName = (Get-Folder -Server $Session:vcSession -id $item.Parent).Name
			} 

			$allFolders += $folderInfo
		}
	}
}

$allFolders = $allFolders | Sort-Object FolderName

New-UDTreeView -Id 'FolderSelect' -Node {

	foreach ($folder in $allFolders) {
		if ($folder.ParentFolderMoRef -eq $topParentFolder) { New-UDTreeNode -Name $folder.FolderName -id $folder.FolderMoRef }
	}

} -OnNodeClicked {

	$childFolders = $allFolders | ?{ $_.ParentFolderMoRef -eq $EventData.id } | Sort-Object FolderName
	foreach ($child in $childFolders) { 
		#Show-UDToast -Message $child -Duration 5000
		New-UDTreeNode -Name $child.FolderName -id $child.FolderMoRef
	}  
	#Show-UDToast -Message $EventData.id -Duration 5000
}
}

Wow, I certainly wasn’t expecting such a detailed example! Many thanks for providing this, it’s exactly what I needed.

You deserve nothing but good things in life.

Thanks again!