Returning a grid from a function

Added update in later comment here: Returning a grid from a function

I’m trying to run some functions and have them return a grid object in to a card. If I run everything manually in a console and then start the dashboard or if I get rid of the functions and do everything explicitly in an endpoint it works.

It is returning a grid object but it is empty (however it returns the $proof from the second function meaning it did actually retrieve the data.

Here’s what the output looks like, the 6 is an arbitrary piece of data from the grid.:

Here is the dashboard I’m trying to run (in the .ps1, the functions are defined above the start of this code):

$x = New-UDEndpointInitialization -Function @("New-SNIncidentTable","New-SNResponse")

$Dash = New-UDDashboard  -Title "Monitoring System" -EndpointInitialization $x -Content {
    New-UDCard -Endpoint {
        $x = New-SNIncidentTable #This function returns a UDGrid
        $x} 
}
Start-UDDashboard -Dashboard $Dash -port 1000

Here’s the code I’m using for the first function, this gets a response from a URI and returns it as json. The other function returns the New-UDGrid


function New-SNResponse {
    param($URI)
    $user = "somename"
    $pass = "somepass"
    $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user, $pass)))

    # Set proper headers
    $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
    $headers.Add('Authorization',('Basic {0}' -f $base64AuthInfo))
    $headers.Add('Accept','application/json')
    # Specify HTTP method
    $method = "get"
    $body2 = {request.body ? "$body = `"" :""}
    $response = Invoke-WebRequest -Headers $headers -Method $method -Uri $uri 
    # Convert Result
    $JSONResponse = ($response.Content | ConvertFrom-Json).result
    Return $JSONResponse
}

Here’s the code for the second function:

function New-SNIncidentTable {

    $uri = "https://somedomain.service-now.com/api/now/table/incident?sysparm_display_value=true&sysparm_limit=1000&sysparm_query=active%3Dtrue%5EstateNOT%20IN730%2C3%5Eassignment_group%3D361ec2626f14ba0039d3dd1cbb3ee406"

    $OpenIncidents = New-SNResponse -URI $uri

    #Process and store results
    $IncidentData = @()

    Foreach ($Incident in $OpenIncidents) {
        $IncID = $Incident.Number
        $IncImpact = $Incident.Impact
        $IncPriority = $Incident.priority
        $IncShortDescription = $Incident.short_description
        $IncPrivacy = $Incident.u_notify_privacy
        $IncLastUpdated = $Incident.sys_updated_by
        $IncCreated = $Incident.sys_created_on
        $IncSubcategory = $Incident.subcategory
        $IncUrgency = $Incident.urgency
        $IncStatus = $Incident.State #730 = Resolved, 2 = In Progress, 1 = New
        $IncidentInfo = @{ID = $IncID}
        $IncidentInfo["Short Description"] = $IncShortDescription
        $IncidentInfo["Impact"] = $IncImpact
        $IncidentInfo["Priority"] = $IncPriority
        $IncidentInfo["Urgency"] = $IncUrgency
        $IncidentInfo["Privacy Incident"] = $IncPrivacy
        $IncidentInfo["Created"] = $IncCreated
        $IncidentInfo["Last Updated"] = $IncLastUpdated
        $IncidentInfo["Subcategory"] = $IncSubcategory
        $IncidentInfo["Status"] = $IncStatus
        #Add to array
        $IncidentData += $IncidentInfo
    }
    $proof = $openIncidents.count
    #Turn data in to a grid
    $GridOutput = New-UDGrid -Title "$proof" -Headers @("ID","Short Description","Impact","Priority","Urgency","Privacy Incident","Created","Last Updated","Subcategory","Status") -Properties @("ID","Short Description","Impact","Priority","Urgency","Privacy Incident","Created","Last Updated","Subcategory","Status") -Endpoint {
        $IncidentData | Out-UDGridData 
    }
    Return $GridOutput

}

Any help would be amazing! Thanks

Updating with some info.

  1. If I generate the grid globally before lauching uddashboard it seems to cause it to load when the page runs - this makes me think it might be tied to scoping or perhaps the way Out-UDGridData needs to be called? perhaps it isn’t portable in the variable?

  2. I merged the functions in to a single dashboard and it works properly but causes all the code to be in the UDCard which is not ideal.

Code for the example without using functions is here, this will load the grid… very confused on this one :frowning: :

$Dash = New-UDDashboard  -Title "Monitoring System" -Content {
    New-UDCard -Endpoint {
    $uri = "https://somecompany.service-now.com/api/now/table/incident?sysparm_display_value=true&sysparm_limit=1000&sysparm_query=active%3Dtrue%5EstateNOT%20IN730%2C3%5Eassignment_group%3D361ec2626f14ba0039d3dd1cbb3ee406"
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    $user = "someuser"
    $pass = "somepassword"
    $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user, $pass)))

    # Set proper headers
    $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
    $headers.Add('Authorization',('Basic {0}' -f $base64AuthInfo))
    $headers.Add('Accept','application/json')

    # Specify HTTP method
    $method = "get"

    $body2 = {request.body ? "$body = `"" :""}
    $response = Invoke-WebRequest -Headers $headers -Method $method -Uri $uri 

    # Convert Result
    $OpenIncidents = ($response.Content | ConvertFrom-Json).result
  
    #Process and store results
    $IncidentData = @()

    Foreach ($Incident in $OpenIncidents) {
        $IncID = $Incident.Number
        $IncImpact = $Incident.Impact
        $IncPriority = $Incident.priority
        $IncShortDescription = $Incident.short_description
        $IncPrivacy = $Incident.u_notify_privacy
        $IncLastUpdated = $Incident.sys_updated_by
        $IncCreated = $Incident.sys_created_on
        $IncSubcategory = $Incident.subcategory
        $IncUrgency = $Incident.urgency
        $IncStatus = $Incident.State #730 = Resolved, 2 = In Progress, 1 = New
        $IncidentInfo = @{ID = $IncID}
        $IncidentInfo["Short Description"] = $IncShortDescription
        $IncidentInfo["Impact"] = $IncImpact
        $IncidentInfo["Priority"] = $IncPriority
        $IncidentInfo["Urgency"] = $IncUrgency
        $IncidentInfo["Privacy Incident"] = $IncPrivacy
        $IncidentInfo["Created"] = $IncCreated
        $IncidentInfo["Last Updated"] = $IncLastUpdated
        $IncidentInfo["Subcategory"] = $IncSubcategory
        $IncidentInfo["Status"] = $IncStatus
        #Add to array
        $IncidentData += $IncidentInfo
    }
    $proof = $openIncidents.count
    #Turn data in to a grid
    $GridOutput = New-UDGrid -Title "$proof" -Headers @("ID","Short Description","Impact","Priority","Urgency","Privacy Incident","Created","Last Updated","Subcategory","Status") -Properties @("ID","Short Description","Impact","Priority","Urgency","Privacy Incident","Created","Last Updated","Subcategory","Status") -Endpoint {
        $IncidentData | Out-UDGridData }
    $GridOutput
    }    
}
Start-UDDashboard -Dashboard $Dash -port 1000

Try using a udtable instead of a udgrid and see how that works. I have had trouble using udgrid, where the same data can display well with udtable

Had a similar issue with a function that return a New-UDGrid object. This logic work well in 2.3.1 but in 2.6.2 an empty table would display.

In my case the problem was the function that help generate the UDGrid accepted parameters. In 2.3.1 the variables passed were in scope but in 2.6.2 they were not in scope.

My solution was to stop using the function to generate that UDGrid and just start hardcoding the needed parameters into the UDGrid generation logical. This make the logic a little less complex and perhaps easier to follow.

Long story short check the scoping of any variables you might be using. I’m guessing you have the same issue as I.

1 Like

Why don’t you get the result into a variable like $result out of the function and then $result | Out-UDGridData

That is basically what was working in 2.3.1. The function called required 2 parameters. For some reason in 2.6.2 the parameters where not in scope in the EndPoint block and resulted no records being returned.

1 Like

Thanks for the idea @alaaelmahdy but I couldn’t get it to work.

One of the reasons I’m using a function is that so my actual grid object in the page isn’t huge and filled with 10 headers and 10 property names which means I need the -header and other flags. As far as I know the $data | Out-udgriddata needs to be inside of the new-udgrid -Endpoint {} function. Even if I did just return the data, I’d still have the huge command in the page and most importantly - still wouldn’t return a grid from a function, just the data.

Just as an update, here’s some of what I’m doing as a partial solution - would love to find out if it’s actually possible to return a UDGrid as an object from a function and then place it on a page successfully.

Function to return the data and properties/headers I care about:

function New-SNIncidentTable {
    $uri = "https://somecompany.service-now.com/api/now/table/incident?sysparm_display_value=true&sysparm_limit=1000&sysparm_query=active%3Dtrue%5EstateNOT%20IN730%2C3%5Eassignment_group%3D361ec2626f14ba0039d3dd1cbb3ee406"

    $OpenIncidents = New-SNResponse -URI $uri

    #Process and store results
    $IncidentData = @()

    Foreach ($Incident in $OpenIncidents) {
        $IncID = $Incident.Number 
        $IncLink = "https://nuvasive.service-now.com/task.do?sys_id=$IncID"
        $IncImpact = $Incident.Impact
        .
        .
        .
        $IncidentInfo = @{ID = (New-UDLink -Text "$IncID" -Url $IncLink)}
        $IncidentInfo["Short Description"] = $IncShortDescription
        $IncidentInfo["Impact"] = $IncImpact
        .
        .
        .
        $IncidentInfo["Resolver"] = (New-UDButton -Text "Resolve" -OnClick {
            Show-UDModal -Content {
              New-UDInput -Title "Enter Resolution Reason" -Content {
                New-UDInputField -Type textbox -Name "Reason" -Placeholder "Resolved"
              } -Endpoint {
                Show-UDToast -Message "Resolved"
              }
           }
         })
        $IncidentData += $IncidentInfo
    }

    $SNHeaders = @("ID","Short Description","Impact",.......,"Resolver") 
    $SNProperties = @("ID","Short Description","Impact",......,"Resolver") 
    $Results = @{"data" = $IncidentData; "headers" = $SNHeaders; "Properties" = $SNProperties}

    Return $Results
}

Then I place this in the page where I want it to render:

New-UDCard -Endpoint {
    $incidentData = New-SNIncidentTable
    New-UDGrid -Title "SN Incidents" -Headers $incidentData.Headers -Properties $Incidentdata.Properties -Endpoint { $IncidentData.data | Out-UDGridData }
}

I’d LOVE to hear if ANYONE has found a way to have the above code be rendered as:

New-UDCard -Endpoint {
    $myGridOutput = Return-CustomUDGridObject
    $myGridOutput
}

Hey @theabraxas guess you been doing some head-banging over this…Having skimmed through these posts, are you not over-complicating things? Like I am thinking KISS approach
Keep
It
Simple
Silly
I am NOT implying I want to kiss you or implying your stupid, but keeping a simple approach to it. I am thinking can you not output your function to CSV format, then each time your function runs it generates a dynamic CSV output, then just put that CSV into a grid…? Just my 2 pence worth, or without it being a GRID you could build a simple HTML table looping over the data as long as it is always going to be X amount of columns wide…I’m sure somehow it is possible to do what you are trying to achieve but I don’t personally have a scooby-doo how you would do that…So I would get my function to output to CSV then put that dynamically generated CSV into the grid.

1 Like

I am also following the approach of always giving the grid some thing ready the grid should not be built in functions it’s a presentation method that should present some thing ready and not to be envolved ,returned or retrived from functions.

every thing is doiable but the best approach as PSdevUK said is to get a variable contains whatever data out of your function then $result | Out-UDGridData or let your grid just read from the csv and change the csv itself.

Thanks

2 Likes

Thanks @psDevUK that’s a good idea! Will give it a shot!

1 Like

From a code design prospective, this is the right approach to take. If one is displaying the same UD control 25 times and the UD control only varies by say 2 basic parameters, it makes sense to me to have a function accept the 2 parameters and return the UD control as an object to be rendered.

Hopefully my logic thinking is making sense. I’d rather write the logic once and call a function 25 times to render the control.

The method was working in 2.3.1 for a collapsible control displaying a UD-Chart. But in 2.6.2 this method wasn’t working. Sense I was only repeating the collapsible control 4 times, I just changed to defining the control 4 times rather than calling a function to do it.

1 Like