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" ![]()
Seriously man, thanks for vlogging all your stuff
I'll be reviewing your homework Csongor. ![]()
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?
By all means start a "UIBUILDER Tips & Tricks" tag if you like. ![]()
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.
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! ![]()
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;
}