🇺🇦 We stand with Ukraine! 🇺🇦

Best way to auto store settings

I have some UI values that I would like to persist, and I'm working on the principal that writing those to the plugin settings is the correct way to do.

I'm drawing my inspiration from the timelapse tab at the moment, which has some settings, and a big 'Save Settings' button. However what I'd like is for the setting to be saved every time it's updated (e.g. when a control is blurred.

The best I have come up with so far is to have a checkbox that is bound to a computed KO observable implemented like this:

self.confirmAllGcode = ko.computed({
        read: function() {

        write: function (value) {
            OctoPrint.settings.savePluginSettings("calibration_tests", {confirmAllGcode: value}, null)
        owner: this

However, even though the call to save the plugin settings returns success, the backend octoprint settings yaml file doesn't change. Am I missing something obvious?

NB I've got the writing to work correctly - stupid typo in the plugin name! Not sure how to get the reasing to work correctly yet though...

I would generally advise against some kind of approach like this, just be aware of the amount of additional requests you are sending back to OctoPrint and the increase in network traffic, before it becomes a problem.

Is that really likely to be an issue (sending an int wrapped in some json)? What I want to avoid is having the client out of sync with the settings.

Essentially I use the UI to build a bit of gcode (client side) which gets sent to the printer. I find myself coming back to it later to generate the 'same' bit of gcode without realising that, in fact, the settings I had tweaked previously had been lost because I didn't click 'save' and so the resultant gcode is different.

I guess I could push the (changed) settings to the backend every time the gcode is sent, that could be an option?

Generating the gcode in the backend would also be an option but I've stayed away from that because there's a huge amount of overhead to (say) adding a new parameter to the gcode generation and getting that new param to the backend code. Doing it client side is significantly simpler!

It is a bit of concern if you do it on change of an input, a little less of an issue if you do it on blur. I would recommend against it still like Charlie mentioned, but you could highlight the button when the values change to hopefully remind you to click it? If you still want to try it the other way do an event knockout binding using blur.

an even more sane approach to reduce the amount of calls to the backend python side is using onAfterTabChange viewmodel callback...

As ever, really good suggestions (if a little off the original topic!) I find that being able to run ideas through here is super useful, I think I may settle on having a "save settings" button (that highlights when necessary) and also save settings whenever g-code is generated

OK I've been working on this a little more and am a bit stuck. And example of what I want to achieve: I want a setting that is visible in the settings dialog, and also on a ,main tab. I have a save settings button on the main tab that will push any changed value to the backend.

I'm subscribing to the value in the settingsViewModel so that I can make sure that the tab UI has the correct value as specified in the settings dialog.

This is all working fine, but what I want to do is to only enable the "save settings" button when there is a change to be saved. The naïve implementation results in the tab button becoming enabled if someone change the settings dialog, because the value has changed in the settings view model. Note that this happens even if the user cancels out of the settings dialog without saving changes.

An alternative is to 'get latest values' from the backend whenever (e.g.) the tab gets focus (and when the settings dialog is dismissed, or something similar). That seems like a reliable way of doing it, but is that making too many calls to the backend?

It would be far easier to see the whole code in context if you have it available somewhere, but your last statement about using the onAfterTabChange and onSettingsHidden viewmodel callbacks (or with onSettingsUpdated) to reset your internal observable that changes the button's styling/behavior on the tab would be an acceptable approach without any other additional calls to the backend.

Another approach is to make a pureComputed observable function for your styling/button behavior and then have a comparison between internally stored settings and what's stored in the settingsViewModel.

More than happy to share my code - it's just in complete upheaval at the moment so haven't pushed it to git hub yet!

I couldn't find onSettingsUpdated but did find onSettingsBeforeSave which could be exactly the missing piece of the puzzle: the hook for when settings actually change (rather than just the values in the viewmodel being changed). I'll mess with that for a bit and see how far I get.

Yeah, sorry about that. That callback is on the python side as on_settings_save. The js viewmodel callbacks are listed here.

Right - I think I have it all working now, using the OnSettingsBeforeSave event to synchronise the button enabled state.

If you are interested in the code I ended up with it's in:

(calibrationtests.js and calibrationtests_tab.jinja2)