# Node config screen - Show details of a table row

**URL:** <https://discourse.nodered.org/t/node-config-screen-show-details-of-a-table-row/11958>\
**Category:** Developing Nodes\
**Created:** [7 June 2019 21:50 UTC](https://discourse.nodered.org/t/node-config-screen-show-details-of-a-table-row/11958 "2019-06-07T21:50:28Z")\
**Posts on this page:** 1\
**Showing post:** 10

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [8 June 2019 16:18 UTC](https://discourse.nodered.org/t/node-config-screen-show-details-of-a-table-row/11958/10 "2019-06-08T16:18:21Z")

</div>

> [@BartButenaers](#):
>
> Do you perhaps know a node that I can use as an example?

Hmm, let me think - do I know anyone using editableList? Maybe I do ... wait one ...

Here is a simple section taken from the uibuilder v2 admin panel:

```html
    <!-- ---- Hideable NPM Section ---- -->
    <div id="npm-props">
        <h3>Manage Front-End Libraries</h3>
        <p>
            Install, remove or update npm packages that provide front-end libraries such as
            VueJS, jQuery, MoonJS, etc.
        </p>
        <p>
            You can search for packages on the 
            <a href="https://www.npmjs.com/" target="_blank">official npm site</a> 
            or on <a href="https://npms.io/" target="_blank">npms.io</a>.
            Take the name and paste it below then use one of the buttons.
        </p>
        <!-- Package List -->
        <div class="form-row">
            <ol id="node-input-packageList"></ol>
        </div>
    </div>

```

And here is some code to go with it:

```javascript
RED.nodes.registerType('uibuilder', {
    ....
    /** AddItem function for package list
     * @param {JQuery<HTMLElement>} element the jQuery DOM element to which any row content should be added
     * @param {number} index the index of the row
     * @param {string|*} data data object for the row. {} if add button pressed, else data passed to addItem method
     */
    function addPackageRow(element,index,data) {
        var hRow = ''

        if (Object.entries(data).length === 0) {
            // Add button was pressed so we have no packageName, create an input form instead
            hRow='<input type="text" id="packageList-input-' + index + '"> <button id="packageList-button-' + index + '">Install</button>'
        } else {
            // addItem method was called with a packageName passed
            hRow = data
        }
        // Build the output row
        var myRow = $('<div id="packageList-row-' + index + '" class="packageList-row-data">'+hRow+'</div>').appendTo(element)

        // Create a button click listener for the install button for this row
        $('#packageList-button-' + index).click(function(){
            //console.log('.packageList-row-data button::click', $('#packageList-input-' + index).val() )

            // show activity spinner
            $('i.spinner').show()
            
            const packageName = '' + $('#packageList-input-' + index).val()

            if ( packageName.length !== 0 ) {
                // Call the npm installPackage API (it updates the package list)
                $.get( 'uibnpmmanage?cmd=install&package=' + packageName, function(data){
                    //console.log('.packageList-row-data get::uibnpm', data )

                    if ( data.success === true) {
                        console.log('PACKAGE INSTALLED')

                        // Replace the input field with the normal package name display
                        myRow.html(packageName)
                    } else {
                        console.log('ERROR ON INSTALLATION ' )
                        console.dir( data.result )
                    }

                    // Hide the progress spinner
                    $('i.spinner').hide()

                }).fail(function(jqXHR, textStatus, errorThrown) {
                    console.error( '[uibuilder:addPackageRow:get] Error ' + textStatus, errorThrown )

                    $('i.spinner').hide()
                    return 'addPackageRow failed'
                    // TODO otherwise highlight input
                })
            } // else Do nothing

        }) // -- end of button click -- //

    } // --- End of addPackageRow() ---- //

    /** RemoveItem function for package list */
    function removePackageRow(packageName) {
        console.log('PACKAGE NAME: ', packageName)

        // If package name is an empty object - user removed an add row so ignore
        if ( (packageName === '') || (typeof packageName !== 'string') ) {
            return false
        }

        // show activity spinner
        $('i.spinner').show()

        // Call the npm installPackage API (it updates the package list)
        $.get( 'uibnpmmanage?cmd=remove&package=' + packageName, function(data){
            console.log('removePackageRow get::uibnpm', data )

            if ( data.success === true) console.log('PACKAGE REMOVED')
            else {
                console.log('ERROR ON REMOVAL ', data.result )
                // Put the entry back again
                $('#node-input-packageList').editableList('addItem',packageName)
            }

            $('i.spinner').hide()

        }).fail(function(jqXHR, textStatus, errorThrown) {
            console.error( '[uibuilder:removePackageRow:get] Error ' + textStatus, errorThrown )
            
            // Put the entry back again
            $('#node-input-packageList').editableList('addItem',packageName)

            $('i.spinner').hide()
            return 'removePackageRow failed'
            // TODO otherwise highlight input
        })
        
    } // ---- End of removePackageRow ---- //

    /** Get full package list via API and show in admin ui
     * param {string} url 
     * param {boolean} rebuild - Rebuild the vendorPaths list
     */
    function packageList() {
        $.getJSON('uibvendorpackages', function(vendorPaths) {
            console.log('uibuilder:packageList:uibvendorpackages', vendorPaths)

            $('#node-input-packageList').editableList('empty');

            const pkgList = Object.keys(vendorPaths)
            pkgList.forEach(function(packageName,index){
                if ( packageName !== 'socket.io' )
                    $('#node-input-packageList').editableList('addItem',packageName)
            })
        })
    } // --- End of packageList --- //

    ....
        oneditprepare: function () {
            ....

            //#region ---- npm ---- //
            // NB: Assuming that the edit section is closed
            // Show the npm section, hide the main & adv sections
            $('#show-npm-props').click(function(e) {
                e.preventDefault() // don't trigger normal click event
                $('#main-props').hide()
                $('#adv-props').hide()
                $('#show-adv-props').html('<i class="fa fa-caret-right"></i> Advanced Settings')
                $('#npm-props').show()

                // TODO Improve feedback
                //#region Setup the package list
                $('#node-input-packageList').editableList({
                    addItem: addPackageRow, // function
                    removeItem: removePackageRow, // function(data){},
                    resizeItem: function(row,index) {},
                    header: $('<div>').append('<h4 style="display: inline-grid">Installed Packages</h4>'),
                    height: 'auto',
                    addButton: true,
                    removable: true,
                    scrollOnAdd: true,
                    sortable: false,
                })

                /** Initialise default values for package list
                 * NOTE: This is build dynamically each time the edit panel is opened
                 * we are not saving this since external changes would result in
                 * users having being prompted to deploy even when they've made
                 * no changes themselves to a node instance.
                 */
                packageList()

                // spinner
                $('.red-ui-editableList-addButton').after(' <i class="spinner"></i>')
                $('i.spinner').hide()
                //#endregion --- package list ---- //

            })
            // Hide the npm section, show the main section
            $('#npm-close').click(function(e) {
                e.preventDefault() // don't trigger normal click event

                $('#main-props').show()
                $('#npm-props').hide()
            })
            //#endregion ---- npm ---- //

           ...
       },

       ....
}

```

Sorry, that's a bit of a dump, hopefully you can see how it is done.

While I'm not using columns as such, those are just some html and some CSS to control the width.

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/2X/d/d860c36104a86cff417073b79372d9a784570994.png)

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/2X/b/bd6dd3b7504634b1286202cc98e4617d4adef0d4.png)

You can easily break up rows either wrapping elements with `<div>` or a simple `<br>` if you don't want paragraph spacing.

---

_[View the full topic](https://discourse.nodered.org/t/node-config-screen-show-details-of-a-table-row/11958)._
