node-red / node-red

Low-code programming for event-driven applications
http://nodered.org
Apache License 2.0
18.93k stars 3.31k forks source link

Check/Uncheck all items of a TypedInput multiple checkbox list #4728

Closed Overlap0190 closed 3 weeks ago

Overlap0190 commented 1 month ago

Current Behavior

Hello, I'm having troubles with the TypedInput widget. I created a little demo project that consists of two elements a checkbox and a TypedInput field with multiple selectable elements. So when the checkbox is checked all elements in the TypedInput field should be selected and when unchecked all elements should be deselected. The deselecting doesn't work correctly. If all elements are deselected with the checkbox the text in the TypedInput field changes from X selected to 0 selected but when the TypedInput list is opened suddenly all of the elements are selected and after closing the list the text changes to X selected. If the edit window is closed with done and reopened after unchecking the checkbox the elements in the TypedInput list are all deselected like I would expect.

Expected Behavior

I would expect the TypedInput field list to have all elements deselected when setting the value with an empty string.

Steps To Reproduce

  1. Open the edit dialog of the node
  2. Open the TypedInput field with the little down arrow
  3. All elements are selected
  4. Uncheck the checkbox
  5. Again open the TypedInput field with the little down arrow
  6. All elements are selected

Example flow

demo-node.html:

<script type="text/html" data-template-name="demo-node">
    <div class="form-row">
        <label for="node-input-toggleAllCommands" class="checked">Toggle all</label>
        <input type="checkbox" id="node-input-toggleAllCommands">
    </div>
    <div class="form-row">
        <label for="node-input-commandSelection">Command selection</label>
        <input type="text" id="node-input-commandSelection">
        <input type="hidden" id="node-input-commandSelectionType">
    </div>
</script>

<script type="module">
    (function () {
        const commands = [
            { value: "CMD_1", label: "Command 1" },
            { value: "CMD_2", label: "Command 2" },
            { value: "CMD_3", label: "Command 3" },
            { value: "CMD_4", label: "Command 4" },
        ];
        let checkboxState;

        RED.nodes.registerType("demo-node", {
            category: "Demo",
            color: "#3FADB5",
            defaults: {
                toggleAllCommands: { value: true },
                commandSelection: { value: "" },
            },
            inputs: 1,
            outputs: 1,
            icon: "font-awesome/fa-cogs",
            paletteLabel: "Demo Node",
            label: function () {
                return this.name || "Demo Node";
            },
            oneditprepare: function () {
                $("#node-input-commandSelection").typedInput({
                    typeField: $("#node-input-commandSelectionType"),
                    types: [{
                        value: "Commands",
                        multiple: "true",
                        options: commands
                    }]
                });

                $("#node-input-toggleAllCommands").on("change", function () {
                    const value = $(this).is(":checked");
                    if (value === checkboxState) {
                        return;
                    }
                    checkboxState = value;

                    $("#node-input-commandSelection").typedInput("value",
                        value ? commands.map(cmd => cmd.value).join(",") : ""
                    );
                });
            }
        });
    })();
</script>

demo-node.ts:

import { Node, NodeDef, NodeAPI } from "node-red";

export default function (RED: NodeAPI) {
    function demoNode(this: Node, n: NodeDef) {
        RED.nodes.createNode(this, n);
    }
    RED.nodes.registerType("demo-node", demoNode);
}

Environment

GogoVega commented 1 month ago

Hi, I confirm, I found the cause.

Note: another problem I see is checkboxState because every time you open the node dialog, commandSelection will either be forced all selected or all deselected. In short, it doesn't keep its saved state

Overlap0190 commented 4 weeks ago

@GogoVega Thank you for the fast fix.

Note: another problem I see is checkboxState because every time you open the node dialog, commandSelection will either be forced all selected or all deselected. In short, it doesn't keep its saved state

I had exactly this problem and tried to solve it with the checkboxState variable. So far it works but it seems to me that this is not really a Node-RED issue, isn't it?

GogoVega commented 4 weeks ago

Yes, not an NR issue, it's linked to the editor so you shouldn't save its state in the node.

I recommend:

Overlap0190 commented 4 weeks ago

Yes, not an NR issue, it's linked to the editor so you shouldn't save its state in the node.

I recommend:

* a three-state button

* two toggle buttons

* use `RED.events.on("editor:open", function () {})` to ignore the selection/deselection until the node is opened (finished preparing)

I chose a different approach by replacing .on("change", function () {}) with .on("click", function () {}). Now it behaves exactly like I want it to.