Dynamic config dashboard

Thanks for putting everything into tutorial

For future users "please subscribe, smash that like button and don't forget to turn on notification to get latest updates" :smile:

Seriously man, thanks for vlogging all your stuff

1 Like

I'll be reviewing your homework Csongor. :rofl:

1 Like

Another live stream done, a form, form submittion, and miscallenaous buttons are working. Very happy with the progress.
I have a few questions, but could be more HTML/CSS questions that uibuilder. I can ask them here as well, but maybe there should be a generic "uibuilder tips & tricks" topic, or separate topic for each?

  • Are there any good examples of a fly-in status message like the toast message in ui-notifications? I assume this would be some floating div with some javasript that moved it out of view after a timeout period.
  • Any best practice of handling validation errors with uib-element Simple Form? Error show up above of below the respective form fields?

By all means start a "UIBUILDER Tips & Tricks" tag if you like. :slight_smile:

Never tried that but in general, you'd create the div off-screen and animate it into view.

The default brand css gives some simple error highlighting I seem to remember so check out the forms section of the css so you can see how that is done. CSS classes such as form:invalid xxxx should allow you to identify inputs that are invalid, maybe input:invalid?

You might be able to use things like input:invalid + div to select an adjacent element to display/hide. ref.

Yes, now I remember the :invalid option. But there is no place for the validation message to be included in the simple form JSON right? I mean something like this:

            {
                "id": "userid",
                "type": "text",
                "required": true,
                "label": "User ID:",
                "value": "info@node-red.org",
                "error": "User id already exists, pick a different userid"
            },

I am just saying this, becuase otherwise I would need separate uib-elements probably one for each field to display/hide the validation error divs. Maybe the code can assume that there is a ID xxx_error or xxx_notification where xxx is the ID of the entry field.

I will try to add an error blank to forms in the future. You could easily insert something dynamically though if you wanted to.

You mean as a separate uib-element to populate the "error div"?

Yes, though you possibly want just a uib-tag if inserting a single tag. But you might actually want to insert a placeholder div on all inputs in advance.

At present, there is no zero-code way to update multiple elements with the same addition so you would want to capture the output from a uib-tag and amend it to use the update method instead of replace. With update, you choose the parent as usual but in this case, that would be a CSS Selector that would select - all inputs div wrappers lets say (you'd perhaps need to play with things to get the right selection). Then you should (I've actually not tried this myself I don't think) be able to append a new div marked with a class to hide it. I think this would be best done with some simple front-end JavaScript as it would be more responsive but you could do it from Node-RED as well I think.

I'll try to find some time to run up an example.

1 Like

I should also have said that you should remember that you can always grab a copy of the current HTML from the front-end using a simple command from Node-RED:

You can pass that to a uib-save node if you want to have it saved into a file.

This would let you play with the HTML after using no-code to set up the basics.

Maybe better than an example. Here is the content of a function node. Put this immediately after the uib-element node that produces your form.

It manipulates the low-code JSON output to add in the extra divs. Adjust to suit.

// grab the output that contains the child `div`s of the actual form (the rows of the form)
const frmInputs = msg._ui[0].components[0].components[1].components

// Walk through each row
frmInputs.forEach((inp, i) => {
    // Work out the ID for the actual input for this row
    let inpId = frmInputs[i].components[1].id
    // Calculate a matching ID for the error div
    inpId = inpId.replace(/r(\d+)-.*$/, 'r$1-err')
    // Add a new child div to this row
    inp.components.push({
        "type": "div",
        "id": inpId,
        "attributes": {
            // You can use any class you want of course. Starting with all divs hidden
            "class":" hidden inputerror",
        },
        // You might want to use an extra input array to define standard text for each
        // error msg div.
        // slot: stdErrorText[i],

        // You probably also want to not put an error div on the button row.
        // but I'll leave that as an exercise for others. :-)
    })
})

return msg

It will work with ANY form output from uib-element. All I would say is that, because I'll be needing to update uib-element form output in the future, you might need to check that this still works in future releases. I always try to avoid potentially breaking changes but it is possible that this could be one, not totally certain.

Because each form row now has a div to contain error text below the input with a unique ID, you can now easily change the classes or it might be possible to come up with a selector that targets all error divs where its previous sibling has the :invalid pseudo-class. ChatGPT probably knows! :grinning:

If not, you can use the invalid event and change classes dynamically.


Let me know how you get on. I will certainly be using the learning from this thread to do future updates to uib-elements form output so that you don't need all the messing. I knew forms were a bit simplistic and needed work, this helps focus on what needs doing.

But as you can see, the joy of the no-code output being standard JSON is that it is possible to manipulate it however you want.

OK, even better.

// grab the output that contains the child `div`s of the actual form (the rows of the form)
const frmInputs = msg._ui[0].components[0].components[1].components

// Walk through each row
frmInputs.forEach((inp, i) => {
    // Add a new child div to this row
    inp.components.push({
        "type": "div",
        // You might want to use an extra input array to define standard text for each
        // error msg div.
        // slot: stdErrorText[i],
        slot: 'ERROR',

        // You probably also want to not put an error div on the button row.
        // but I'll leave that as an exercise for others. :-)
    })
})

return msg

So we've got rid of the ID's & attributes because we don't need them at all. Here is some CSS that will show the error divs of EVERY invalid input in the form and hide them otherwise:

form :invalid + div {
    display: block;
    background-color: red;
}
form > div > div {
    display: none;
}

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.