Option to Uniquely Identify Table Row

Found a “bug” (of my own creation) based on this thread here

The bug I created is the logic when removing a row. I based it on the ‘PrinterName’ value assuming it would be unique within the CSV files, however some files have “duplicate” PrinterName values. The team is going to try and correct those as ideally PrinterName should be unique. However, if someone mistakenly adds a duplicate PrinterName value or deletes an incorrect duplicate PrinterName row, the entire table/CSV file gets deleted. I’d like to avoid that and am wondering if there is a property in the $EventData or logic I can build within the -OnClick action to return all rows except for the row where user clicked on the ‘Delete’ button. Below is the relevant code section for the dynamic table:

New-UDDynamic -Id 'table' -Content {
    $CsvRootPath = 'D:\Files\PrinterTest'
    $Session:CsvPath = Join-Path $CsvRootPath "$($Session:Endpoint).csv"
    if (Test-Path $Session:CsvPath) {
        [Array]$RawData = Import-Csv $Session:CsvPath -ErrorAction Stop
        $DefaultPrinter = ($RawData | Select-Object -First 1).PrinterName
        $DefaultSet = $false # used to avoid multiple "Default" result returns
        $FormattedData = $RawData.ForEach({
            $Obj = [PSCustomObject]@{
                DefaultPrinter = $null
                Hostname = $_.Hostname
                PrinterName = $_.PrinterName
                PrinterIP = $_.PrinterIP
                PrinterDriver = $_.PrinterDriver
            }
            If($_.PrinterName -eq $DefaultPrinter -and $DefaultSet -eq $false){ # Check/Define default printer
                $Obj.DefaultPrinter = 'True'
                $DefaultSet = $true
            }
            $Obj #Returns object to $FormattedData parent array
        })
        # Create Table from Data #
        $Columns = @(
            New-UDTableColumn -Property Hostname -Title 'Hostname'
            New-UDTableColumn -Property PrinterName -Title 'Printer Name'
            New-UDTableColumn -Property PrinterIP -Title 'Printer IP'
            New-UDTableColumn -Property PrinterDriver -Title 'Printer Driver'
            New-UDTableColumn -Property DefaultPrinter -Title 'Default Printer'
            New-UDTableColumn -Property Delete -Title Delete -Render {
                New-UDButton -Text 'Delete' -OnClick {
                    $PrinterName = $EventData.PrinterName
                    Show-UDModal -Content {
                        New-UDForm -Content {
                            New-UDCheckBox -Label "Check box and click Submit to confirm deletion of [$PrinterName], or choose Cancel." -Id 'Confirmation'
                        } -OnSubmit {
                            If ((Get-UDElement -Id 'Confirmation').checked -eq $true) {
                                ## PrinterName property is no good if there are duplicates, need to uniquely identify the row/value somehow ##
                                $Output = $FormattedData.Where({$_.PrinterName -notin $PrinterName})
                                $Output | Export-Csv $Session:CsvPath -NoTypeInformation -Force -UseQuotes Never
                                Sync-UDElement -Id 'table'
                                Hide-UDModal
                            }
                            Else {
                                Hide-UDModal
                            }
                        } -OnCancel {
                            Hide-UDModal
                        }
                    } -FullWidth -MaxWidth 'md'
                }
            }
        )
        New-UDTable -Data $FormattedData -Columns $Columns -ShowSelection
    } Else {
        New-UDAlert -Severity 'error' -Content { New-UDHtml "Unable to find file for <strong>$($Session:Endpoint)</strong>" }
    }
}
Product: PowerShell Universal
Version: 1.5.19

I have similar issue for user management.

For add, I put a validation (using where-object) in place to check if the same user exist in table - it will throw a UDtoas if it does.
For deletion, I have a GUID generated within the render and assign each rows with this unique GUID to its element - example: UDButton -Id ‘delete-btn-$guid’ -Onclick { Delete-Function }.

So something akin to this?

New-UDTableColumn -Property Delete -Title Delete -Render {
    $Guid = New-Guid
    New-UDButton -Text 'Delete' -Id "btn-$Guid" -OnClick {
        # perform deletion here
    }
}

If so, the part that still isn’t clicking for me, is how to use that “btn-” value to perform the deletion. The actual objects from the table don’t contain the GUID (as far as I can tell), they only contain the CSV data (Hostname, PrinterName, PrinterIP, PrinterDriver). For example, this is the $EventData exported upon clicking the ‘Delete’ button. I can see the “btn-2fb05af…” within the “rendereddelete” section, but I’m not sure how to reference that when trying to remove it from the CSV:

{
  "DefaultPrinter": null,
  "Hostname": "Name01",
  "PrinterName": "Printer01",
  "PrinterIP": "10.10.10.10",
  "PrinterDriver": "DriverName01",
  "rendereddelete": {
    "type": "mu-button",
    "id": "btn-2fb0b5af-6bd0-4b55-abc8-0cd0d33d9941",
    "isPlugin": true,
    "href": "",
    "fullWidth": false,
    "text": "Delete",
    "color": "default",
    "variant": "contained",
    "size": "",
    "icon": null,
    "disabled": false,
    "iconAlignment": "left",
    "assetId": null,
    "style": null,
    "onClick": {
      "ScriptBlock": <Removed from output>,
      "HasCallback": true,
      "Variables": "System.Collections.Generic.Dictionary`2[System.String,System.Object]",
      "ArgumentList": null,
      "Name": "btn-2fb0b5af-6bd0-4b55-abc8-0cd0d33d9941",
      "Schedule": null,
      "SessionId": "02ecde53-d80b-4fe3-abbb-0e26ffa0d000",
      "Role": null,
      "MovingAverage": "UniversalDashboard.Models.MovingAverage",
      "AverageExecutionTime": 0.0,
      "ContentType": null
    }
  }
}

My (not working) delete function is just trying to select the data except that one row:

# The "delete function"
If ((Get-UDElement -Id 'Confirmation').checked -eq $true) {
    # ***This is the problem where there may be duplicate printer names in the CSV***
    $Output = $FormattedData.Where({$_.PrinterName -notin $PrinterName})
    # Export remaining data
    $Output | Select-Object $Properties | Export-Csv $Session:CsvPath -NoTypeInformation -Force -UseQuotes Never
}

#This is where $FormattedData comes from, just in case it matters
[Array]$RawData = Import-Csv $Session:CsvPath -ErrorAction Stop
$FormattedData = $RawData.ForEach({
    $Obj = [PSCustomObject]@{
        DefaultPrinter = $null
        Hostname = $_.Hostname
        PrinterName = $_.PrinterName
        PrinterIP = $_.PrinterIP
        PrinterDriver = $_.PrinterDriver
    }    
})

Since you’re using CheckBox, have you tried assigning the unique GUID to each Confirmation element?

Depending on usage, on some of my use-case, I added a property into the data object where I assign a unique GUID to it. So, if ever need to reference a row, I refer to this specific GUID - this helps in avoiding any accidental duplicates actions.

That worked. I was able to add New-Guid to the initial foreach loop when formatting the CSV data. For the addition section, I added a guid to the current data set (via $EventData) like this:

$EventData | Add-Member -MemberType NoteProperty -Name GUID -Value $((New-GUID).GUID) -Force

With those setup I modified this

Where-Object {$_.PrinterName -eq $CurrentSelection}

To this

Where-Object {$_.GUID -eq $CurrentGUID}
# or #
Where-Object {$_.GUID -eq $EventData.GUID}

Glad that it worked.

I normally shorten the GUID to 10 chars using this function.

Function Get-UID {

    $guid = [guid]::NewGuid()

    $id = [string]$guid

    $id = $id.Replace("-", "")

    $id = $id.substring(0, 10)

    return $id

}