Creating an item with custom template cannot be used for each object

@adam was able to successfully create an element using custom template but the problem is it cannot be used with foreach object in Ud
Error: An Item with the same key has already been added

Can you please provide some code example of what you are trying to do? Also, is that error appearing in the browser or in the log? If you get a log, that would be helpful as well.

component.jsx code

import React from 'react';
import ButtonLoader from 'react_button_loader';

class ReactButtonLoader extends React.Component {

  constructor() {
    super();
    
    this.state = {
      toggleLoader: false
    };
  }

  // state is for keeping control state before or after changes.

  
  handleClick = () => {
    if (this.handleClick){
    this.setState({toggleLoader: true});
    UniversalDashboard.publish('element-event', {
      type: "clientEvent",
      eventId: this.props.id + "onClick",
      eventName: '',
      eventData: ''
  });
  setTimeout(() => {
    this.setState({ toggleLoader: false });
  }, 2000);
  
  }
};


render() {
  return (
    <ButtonLoader
      isLoading={this.state.toggleLoader}
      background="#42b5c1"
      loaderType= 'jiggling-lines'
      onClick={this.handleClick.bind(this)}
      id={this.props.id}
    >
      {this.props.text}
    </ButtonLoader>
  );
}
}

export default ReactButtonLoader

index.js code

import ReactButtonLoader from './component';

UniversalDashboard.register("UDButtonLoader", ReactButtonLoader);

script.ps1

<#
.SYNOPSIS
    Sample control for UniversalDashboard.
.DESCRIPTION
    Sample control function for UniversalDashboard. This function must have an ID and return a hash table.
.PARAMETER Id
    An id for the component default value will be generated by new-guid.
.EXAMPLE
    PS C:\> <example usage>
    Explanation of what the example does
.INPUTS
    Inputs (if any)
.OUTPUTS
    Output (if any)
.NOTES
    General notes
#>
function New-ButtonLoader {
    param(
        [Parameter()]
        [string]$Id = (New-Guid).ToString(),
        [Parameter()]
        [string]$Text,
        [Parameter()]
        [string]$isLoading,
        [Parameter()]
        [string]$background,
        [Parameter()]
        [object]$onClick,
        [Parameter()]
        [string]$loaderType
    )

    if ($null -ne $OnClick) {
            if ($OnClick -is [scriptblock]) {
                $OnClick = New-UDEndpoint -Endpoint $OnClick -Id ($Id + "onClick")
                
            }
            elseif ($OnClick -isnot [UniversalDashboard.Models.Endpoint]) {
                throw "OnClick must be a script block or UDEndpoint"
            }
        }

    

        @{
            # The AssetID of the main JS File
            assetId = $AssetId 
            # Tell UD this is a plugin
            isPlugin = $true 
            # This ID must be the same as the one used in the JavaScript to register the control with UD
            type = "UDButtonLoader"
            # An ID is mandatory 
            id = $Id

            # This is where you can put any other properties. They are passed to the React control's props
            # The keys are case-sensitive in JS. 
            text = $Text
            isLoading = $isLoading
            background = $background
            loaderType = $loaderType
            onClick = $onClick
        }

    }

dashboard:

Get-UDDashboard | Stop-UDDashboard
$Schedule1 = New-UDEndpointSchedule -Every 10 -Second
$init = New-UDEndpointInitialization -Module "C:\Users\test\Downloads\ud-custom-control-template-master\ud-custom-control-template-master\src\output\UniversalDashboard.UDButtonLoader\UniversalDashboard.UDButtonLoader.psd1"

$Services = New-UDEndpoint -Schedule $Schedule1 -Endpoint {
   
              
           $Cache:Services = (Get-Service -ComputerName "lmspapp001" -Name "*Certification*","*Notification*","*Distribution*") | Select-Object Name,Status | Sort-Object Name | % { 
                    
                    
                    $FontColor = 'Green'

                    if ($_.Status -ne 'Running') {
                        
                    $FontColor = 'Red'
                     }
                    
                    
                    [PSCustomObject]@{
                        Name = $_.Name
                        Status = New-UDElement -Tag 'div' -Attributes @{ style = @{ color = $FontColor } } -Content { $_.Status.ToString() }
                        Select = if($_.Status -eq 'Stopped'){
                        New-ButtonLoader -Text "Start" -onClick {
                        Get-Service -ComputerName "lmspapp001" -Name $_.Name | Start-Service
                        Show-UDToast -Message "Service Started!" -MessageColor Green -Title $_.Name -Position topCenter -Duration 2500
                        }

                        }else{

                        if($_.Status -eq 'Running'){
                        New-ButtonLoader -Text "Stop" -onClick {
                        Get-Service -ComputerName "lmspapp001" -Name $_.Name | Stop-Service
                        Show-UDToast -Message "Service Stopped!" -MessageColor Red -Title $_.Name -Position topCenter -Duration 2500
                        
                        }
                     }
                  }
               }
            }
         }


$Page1 = New-UDPage -Name "Services" -Icon server -Content { 

         New-UDElement -Id "Services" -Tag div4 -EndPoint {
    
        New-UDGrid -Id "Grid" -Title "Services" -Headers @("Name","Status","Select") -Properties @("Name","Status","Select") -Endpoint {
        
                $Cache:Services | Out-UDGridData
               
                  }
                                 
              }  
                        
             New-UDButton -Text "Refresh" -OnClick {
             Sync-UDElement -Id "Grid" -Broadcast       
          } 
      } 


Start-UDDashboard -Endpoint @($Services) -Content{  
New-UDDashboard -Pages @($Page1) -Title "Test" -Color '#FF050F7F' -EndpointInitialization $init
} -Port 1000 -Name Test

working example:

Get-UDDashboard | Stop-UDDashboard
Import-Module "C:\Users\test\Downloads\ud-custom-control-template-master\ud-custom-control-template-master\src\output\UniversalDashboard.UDButtonLoader\UniversalDashboard.UDButtonLoader.psd1"

Start-UDDashboard -Dashboard (
    New-UDDashboard -Title "Map" -Content {
        New-ButtonLoader -Text "test" -onClick {Show-UDToast -Message "test"}
    } -EndpointInitialization (New-UDEndpointInitialization -Module ("C:\Users\test\Downloads\ud-custom-control-template-master\ud-custom-control-template-master\src\output\UniversalDashboard.UDButtonLoader\UniversalDashboard.UDButtonLoader.psd1"))

) -Port 1000 -AutoReload

also adam i really appreciate if you can provide an advice on how to turn the spinner off once the onClick completed , i have tried multiple things but could not figure it out so i have used a temp solution in the code above as setTimeout(() => {
this.setState({ toggleLoader: false });
}, 2000);
to turn it off but would be nice to know how to do it once the executed command completed.

thank you

@adam i have noticed that same issue currently the same if you are using New-UDMButton as well,only New-UDButton does not throw this error.

Ok. I am not sure why you are seeing the same key error. I just tried to put UDMuButton in a loop and it seems to work.

Get-UDDashboard | Stop-UDDashboard

$db = New-UDDashboard -Title "Navigation" -Content {
    1..10 | % {
        New-UDMuButton -Text "Test" -OnClick {

        }
    }
    
}
Start-UDDashboard -Dashboard $db -Port 10001

But to your other question, you will want to use the HTTP requests rather than the publish method on the UniversalDashboard object in JavaScript. publish won’t wait until the script is done.

Check out how to call the HTTP reqiest here: https://github.com/ironmansoftware/universal-dashboard/blob/master/src/UniversalDashboard.Materialize/Components/ud-counter.jsx#L24

When that returns, you will get a callback after the script is done and you can set your state.

Thank you adam i figured out the issue, its with the script itself.

thank you adam that did the trick.