# 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:** 18\
**Page:** 1

<div class="post-metadata">

**Author:** ![BartButenaers](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/bartbutenaers/32/10476_2.png) [@BartButenaers](https://discourse.nodered.org/u/BartButenaers)\
**Post date:** [7 June 2019 21:50 UTC](https://discourse.nodered.org/t/node-config-screen-show-details-of-a-table-row/11958/1 "2019-06-07T21:50:28Z")

</div>

Hi folks,

In my newest node config screen, a table is displayed. Each row in that table can have a different number of properties/columns.

So I show the _ **common** _ properties/columns in the table, but I need to be able to show the other _ **extra** _ properties somewhere (so the user can edit them):

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

In other words: when the user selects a row in the table, I need to display somehow the extra properties of that row. But when I show them below the table (as in the screenshot), there is no visual indication that the properties belong to the selected row 🥺

Does anybody have a suggestion how I could implement this, or other nodes that do something similar. I assume some kind of popup is not an option?

Thanks !!  
Bart

---

<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 00:41 UTC](https://discourse.nodered.org/t/node-config-screen-show-details-of-a-table-row/11958/2 "2019-06-08T00:41:16Z")

</div>

When you create the admin ui for a node, you get access to jQuery and jQuery UI in addition to the Node-RED specific ui elements like editableList.

As you are using editableList, I would probably start by trying a 2-line layout with the extra fields appearing in a second line - really all one row of the editableList but since the list row simply contains HTML, you can split the input form over 2 lines.

---

<div class="post-metadata">

**Author:** ![BartButenaers](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/bartbutenaers/32/10476_2.png) [@BartButenaers](https://discourse.nodered.org/u/BartButenaers)\
**Post date:** [8 June 2019 05:53 UTC](https://discourse.nodered.org/t/node-config-screen-show-details-of-a-table-row/11958/3 "2019-06-08T05:53:21Z")

</div>

> [@TotallyInformation](#):
>
> As you are using editableList, I would probably start by trying a 2-line layout with the extra fields appearing in a second line

Morning @TotallyInformation, indeed some nodes solve it like that. But that isn't really my cup of tea. I have added **column headers**. Because I find it very confusing having to fill in multiple input fields, without column headers guiding me... When I add a second line, the headers don't make sense anymore...

---

<div class="post-metadata">

**Author:** ![zenofmud](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/zenofmud/32/316_2.png) [@zenofmud](https://discourse.nodered.org/u/zenofmud)\
**Post date:** [8 June 2019 09:34 UTC](https://discourse.nodered.org/t/node-config-screen-show-details-of-a-table-row/11958/4 "2019-06-08T09:34:22Z")

</div>

Maybe make each row a seperate table?

---

<div class="post-metadata">

**Author:** ![BartButenaers](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/bartbutenaers/32/10476_2.png) [@BartButenaers](https://discourse.nodered.org/u/BartButenaers)\
**Post date:** [8 June 2019 12:13 UTC](https://discourse.nodered.org/t/node-config-screen-show-details-of-a-table-row/11958/5 "2019-06-08T12:13:36Z")

</div>

Hey @zenofmud,  
Perhaps I might give that idea a chance. Found treetables, datasets, ... but they are all quite mind blowing for a hobby 🤔 Think this [jsfiddle](https://jsfiddle.net/gyrocode/abkbg9nx/) example could be a good place to start for my adventure. Although I'm wondering if I could replace my table perhaps by this [JSON editor](https://github.com/json-editor/json-editor) or this [JSON editor](https://github.com/josdejong/jsoneditor): would be nice if I could just create a JSON scheme, and let the editor do all the heavy work. I 'think' I just have to pass a json string and a json scheme, then the editor generates a screen and gives me a json output at the end (which I could just send to my server-side Node-RED flow). Not sure if it works that way. Anybody has good or bad experiences with this?

---

<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:02 UTC](https://discourse.nodered.org/t/node-config-screen-show-details-of-a-table-row/11958/6 "2019-06-08T16:02:53Z")

</div>

> [@BartButenaers](#):
>
> solve it like that. But that isn't really my cup of tea. I have added **column headers**. Because I find it very confusing having to fill in multiple input fields, without column headers guiding me.

Sorry, meant to say this yesterday.

You know that you get a header with the editableList? All you need to do is set sensible widths using CSS. Then you can have the best of both worlds.

---

<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:10 UTC](https://discourse.nodered.org/t/node-config-screen-show-details-of-a-table-row/11958/7 "2019-06-08T16:10:35Z")

</div>

> [@BartButenaers](#):
>
> create a JSON scheme, and let the editor do all the heavy work

There are certainly tools to do that but I don't know of any for jQuery - not that I use jQuery much so that isn't to say they don't exist.

The default framework for uibuilder v2 uses bootstrap-vue which has such a thing built in and which I'm using for my home dashboard but that doesn't really help you for the admin ui.

---

<div class="post-metadata">

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

</div>

> [@TotallyInformation](#):
>
> You know that you get a header with the editableList?

@TotallyInformation. No, unfortunately I didn't knew that. Until now 😉... Do you perhaps know a node that I can use as an example?

---

<div class="post-metadata">

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

</div>

[https://nodered.org/docs/api/ui/editableList/#header](https://nodered.org/docs/api/ui/editableList/#header)

---

<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.

---

<div class="post-metadata">

**Author:** ![BartButenaers](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/bartbutenaers/32/10476_2.png) [@BartButenaers](https://discourse.nodered.org/u/BartButenaers)\
**Post date:** [13 June 2019 14:49 UTC](https://discourse.nodered.org/t/node-config-screen-show-details-of-a-table-row/11958/11 "2019-06-13T14:49:11Z")

</div>

> [@BartButenaers](#):
>
> But that isn't really my cup of tea

@TotallyInformation, @knolleary,  
Thanks for the pointer guys!  
Must admit I was wrong about the **editableList** : seems that the editableList is really my cup of tea!  
Once you get familiar with it, it is a _ **very** _ powerful and rather easy to use UI component.  
With very few code, I managed to create just what I wanted:

![cam_viewer](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/2X/0/0e61a5f482f59a0dc36d5a17ccfebc9f43050503.gif)

For those who ever need something similar, you can find my code in the [node-red-contrib-ui-camera-viewer](https://github.com/bartbutenaers/node-red-contrib-ui-camera-viewer/blob/master/cam_viewer.html).  
Bart

---

<div class="post-metadata">

**Author:** ![knolleary](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/knolleary/32/3_2.png) [@knolleary](https://discourse.nodered.org/u/knolleary)\
**Post date:** [13 June 2019 15:52 UTC](https://discourse.nodered.org/t/node-config-screen-show-details-of-a-table-row/11958/12 "2019-06-13T15:52:08Z")

</div>

Hi @BartButenaers, minor point, but I'd suggest using `fa-angle-right` and `fa-angle-down` as the collapsed/expanded icons on the left so you are consistent with the other places in the UI that we have collapsible sections.

---

<div class="post-metadata">

**Author:** ![BartButenaers](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/bartbutenaers/32/10476_2.png) [@BartButenaers](https://discourse.nodered.org/u/BartButenaers)\
**Post date:** [13 June 2019 19:24 UTC](https://discourse.nodered.org/t/node-config-screen-show-details-of-a-table-row/11958/13 "2019-06-13T19:24:37Z")

</div>

> [@knolleary](#):
>
> I'd suggest using `fa-angle-right` and `fa-angle-down` as the collapsed/expanded icons

Consider it done:

![cam_viewer_arrow](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/2X/9/997b4185e8e660c1cf5a952c256c796590395958.gif)

---

<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:** [14 June 2019 12:13 UTC](https://discourse.nodered.org/t/node-config-screen-show-details-of-a-table-row/11958/14 "2019-06-14T12:13:08Z")

</div>

By the way, how to people record these gifs? I really should do some for uibuilder.

---

<div class="post-metadata">

**Author:** ![dceejay](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/dceejay/32/38_2.png) [@dceejay](https://discourse.nodered.org/u/dceejay)\
**Post date:** [14 June 2019 14:40 UTC](https://discourse.nodered.org/t/node-config-screen-show-details-of-a-table-row/11958/15 "2019-06-14T14:40:04Z")

</div>

On a mac I use RecordIt

---

<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:** [14 June 2019 16:03 UTC](https://discourse.nodered.org/t/node-config-screen-show-details-of-a-table-row/11958/16 "2019-06-14T16:03:52Z")

</div>

Thanks. There is a Windows version of RecordIt too. Another popular choice seems to be ScreenToGif.

[https://www.screentogif.com/](https://www.screentogif.com/)

[http://recordit.co/](http://recordit.co/)

ScreenToGif seems to have more options/control but RecordIt seems the simplest to use. Inevitably for me, I'll probably choose the former. 🙂

Actually, ScreenToGif has another advantage - it doesn't need to be installed, they have a standalone version.

---

<div class="post-metadata">

**Author:** ![BartButenaers](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/bartbutenaers/32/10476_2.png) [@BartButenaers](https://discourse.nodered.org/u/BartButenaers)\
**Post date:** [14 June 2019 16:26 UTC](https://discourse.nodered.org/t/node-config-screen-show-details-of-a-table-row/11958/17 "2019-06-14T16:26:11Z")

</div>

Hey @TotallyInformation,  
I use GifCam on a Windows portable ...

---

<div class="post-metadata">

**Author:** ![Steve-Mcl](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/steve-mcl/32/4826_2.png) [@Steve-Mcl](https://discourse.nodered.org/u/Steve-Mcl)\
**Post date:** [15 June 2019 08:10 UTC](https://discourse.nodered.org/t/node-config-screen-show-details-of-a-table-row/11958/18 "2019-06-15T08:10:04Z")

</div>

> [@TotallyInformation](#):
>
> how to people record these gifs

ShareX

[https://goo.gl/search/ShareX](https://goo.gl/search/ShareX)  
ShareX,
