How to set <select> dropdown in oneditprepare?

I have something like this in the definition of the edit screen and the commented option (value="emit") was there in a previous version but is gone now.
I would like to "migrate" the existing value "emit" to "drop" and set another checkbox.
While I was able to set the checkbox ($("#node-input-emit_msg_2nd").prop('checked', true);), I am unable to get the <select> dropbox filled in a way that the edit screen does not open empty.

<script type="text/html" data-template-name="rate-limiter">
    <div class="form-row">
        <select id="node-input-drop_select" style="width: 70%">
            <option value="queue" data-i18n="rate-limiter.queuemsg"></option>
            <option value="drop" data-i18n="rate-limiter.dropmsg"></option>
            <!--
            <option value="emit" data-i18n="rate-limiter.sendmsg"></option>
            -->
        </select>
    </div>
</script>
  1. I tried $("#node-input-drop_select").val('drop'), but that seems to do nothing (at least nothing noticabe).

  2. Setting this.drop_select = "drop" does not help immediately, but only after hitting cancel on the edit dialog the first time and on the second time opening the dialog the dropdown is filled.

  3. On jquery - Set select option 'selected', by value - Stack Overflow and drop down menu - How can I set the value of a DropDownList using jQuery? - Stack Overflow I found some other flavours... which did not work... e.g.

    • $("#node-input-drop_select").val('drop').change();
    • $("#node-input-drop_select option[value=drop]").attr('selected','selected');

What would be the correct and working way to solve this?


I am somehow missing a location to put code that runs after a node is updated, to get everything in order to match the new functionality, before the editor get's opened the first time. For the js file of the node it is not an issue, because I just put "migration" stuff at the beginning, but for the editor? Am I missing something?

If there is no way to do it properly, I was thinking to abuse the label function, because it seems to me, that it is the only safe place to put code that is called for every node upon initialisation. But does it get called if the node-red only starts without the editor opened in the browser?
And probably it is not the best way to do this, right?

   label: function() {
       // using this to set value for all
       if (this.drop_select === "emit") {
           this.drop_select = "drop";
           this.checkbox = true;
       }

       // actually determine label
       if (this.name) {
           return this.name;
       } else {
           return "rate-limiter";
       }
   },

Hi @cameo69

Try setting the selected property to true, instead of the attribute, jquery will 'redraw' using this method I believe

Ensure this happens in oneditprepare
I think you may also need to put the value in single quotes - I'm not sure

$("#node-input-drop_select option[value='drop']").prop('selected', true);

EDIT
In fact, I would do this after a quick delay, this allows the runtime to first do its thing - effectively setting a value that is no longer valid.

setTimeout(() => {
    $("#node-input-drop_select option[value='drop']").prop('selected', true);
}, 50)

Ideally this should be noted as a breaking change instead of hackery - that needs to be documented :wink:

Personally, I prefer to use single-quotes for JavaScript strings which leaves double-quotes for shell and HTML quotes. Either way, you either have to use the oposite quote type or escape the quote
"<... xxx=\"yyy\">".

1 Like

Yup - I'm a single type of person also, My eslint configs shout at me if not! :sweat_smile:

Man after my own heart! :grin:

This worked, with and without the timeout.

Is there an advantage of allowing "the runtime to first do its thing - effectively setting a value that is no longer valid."?

Right now this whole fiddling with HTML of the edit screen is taking way more time than I anticipated. :expressionless:

None - based on the fact it worked without the timeout.

I'm not 100% sure, I can't recall if oneditprepare is executed before the runtime loads values, hence the delayed action just in case - but seems its not needed .

@knolleary

Out of curiosity, at what point in the stack is oneditprepare and oneditsave called - is it after values have been fetched/committed and applied to/from the elements or after?

oneditprepare is called before any values are set on the inputs... because the inputs might not exist until after oneditprepare has been called.

Given the fact that I tried in oneditprepare...

...seems to me like oneditprepare is called after loading values.

I don't understand.
If I set in oneditprepare the selected item of a dropbox and that happened "before any values are set on the inputs"; wouldn't that mean that my set value should be overwritten by said "set on inputs"?

If I read this correctly, your issue may be a bit different (and less difficult) than you think. If you edit the html file to add new properties to the node configuration and restart NR, the editor will import the most recently deployed flow file, and any new properties will be undefined. This could cause the default selection in the dropdown to be blank (as well as other fields in the edit dialog). Making a selection and deploying fixes this. You can confirm that this is happening by dragging a new instance of your node into the workspace and opening its edit dialog. Everything should be ok in the new instance. If you are developing a new node, you can ignore the issue as long as you don't let it bother you during testing. If you are updating a node that has already been published, you might warn your users or just count on them to re-deploy flows containing the updated node. It may not be worth the effort to resolve what is basically a one-time glitch.

If I have completely missed the point here, please ignore the above and excuse me for taking your time.

Or better still, at the start of your on edit function, check whether the variable is populated and if not, use jQuery to set it to the default.

Yes, assuming that my diagnosis is correct. Still, it seems a bit of bother to write code that will execute at most once in the lifetime of an an instance, in order to guard against a cosmetic issue. Also, treating certain parameters differently than the rest, depending on when they were introduced during development, doesn't appeal to the perfectionist in me. (Although I admit to having done this in the js file in similar situations.) So, of course, the obsessive-compulsive isn't quite sure where to come down on this...

I did as @TotallyInformation said (to some degree).
In cases that I deemed relevant, I checked for typeof attribute == "undefined" and if true set a proper value.

I think my struggle was with the jquery and how to set the desired values. I think I would have liked it better to just set a normal attribute of the node before the user used the edit screen.

If it is necessary, well, I don't know. And you made a good point in writing code that run's only once.

However, if I update a node it should behave the same if possible or if not possible tell me about it. But silently doing something different is not a good option in my book. So to work the same for some settings, I had to correct values.

Besides, I am still booking this under "learning experience". :slight_smile:

BTW, just published a new version.

What node is this? I may have missed where you identified it.

  • 0.0.13 (2023-03-29)
    • added max queue size
    • added queueing modes a) drop oldest message and b) drop new messages
    • added option to emit dropped messages on 2nd output also for queueing modes
    • removed mode "Send intermediate messages on 2nd output", because it can be set via Drop intermediate messages + new checkbox to emit on 2nd output
1 Like

I realize that I am late to the party, but you can also call:

$("#node-input-drop_select").val("drop").trigger("change")

I have a property in one of my nodes that populates the select drop down in oneditprepare and I had to use this method to get the value to actually load by:

$("#node-input-drop_select").val(this.drop_select).trigger("change")

Which reads the current setting from the node config and changes the select box to that setting.

Triggering "change" event is a nice idea and works even before I defined $("#node-input-drop_select").on("change", function() {...; how is that possible?

Change is a standard event on the input select block - triggering the change event sets the "selected" property and also triggers the event to say that it is changed. For that reason, triggering change is nice specifically because the other events happen automatically and update the display of the input.

If you have a custom handler that listens to the "change" event I am not sure how it could trigger that before it is registered. What are you doing inside your function that listens to the change event?