Passing settings from Plugin Settings page to JS

I'm not really a Front end developer so I'm struggling a bit in adding some more advanced settings for my plugin.

I want to add this http://jsfiddle.net/rniemeyer/7RDc3/ to my plugins settings page. I know if I can get this example to work I will be able to modify it for what I need.

I have already configured my plugin using the assets and templates mixins and that works fine for passing a single setting back and forth from the plugin development guide in the docs.

In my templates/myplugin_settings.jinja2 I have this:

<div class='liveExample'> 
    
    <form>
        <p>You have asked for <span data-bind='text: gifts().length'>&nbsp;</span> gift(s)</p>
        <table data-bind='visible: gifts().length > 0'>
            <thead>
                <tr>
                    <th>Gift name</th>
                    <th>Price</th>
                    <th>Test</th>
                    <th />
                </tr>
            </thead>
            <tbody data-bind='foreach: gifts'>
                <tr>
                    <td><input class='required' data-bind='value: name, uniqueName: true' /></td>
                    <td><input class='required number' data-bind='value: price, uniqueName: true' /></td>
                    <td><a href='#' data-bind='click: $root.removeGift'>Delete</a></td>
                </tr>
            </tbody>
        </table>
     
        <button data-bind='click: addGift'>Add Gift</button>
        <button data-bind='enable: gifts().length > 0' type='submit'>Submit</button>
    </form>
    
</div>

In my static/js/events.js I have this:

$(function () {
  function GiftModel(parameters) {
    var self = this;

    self.loginState = parameters[0]; // requested as first dependency below
    self.settings = parameters[1]; // requested as second dependency below
    self.someOtherViewModel = parameters[2]; // requested as first optional dependency below

    // more of your view model's implementation
    self.gifts = ko.observableArray([
      { name: "Tall Hat", price: "39.95" },
      { name: "Long Cloak", price: "120.00" }
    ]);

    self.addGift = function () {
      self.gifts.push({
        name: "",
        price: ""
      });
    };

    self.removeGift = function (gift) {
      self.gifts.remove(gift);
    };

    self.save = function (form) {
      alert("Could now transmit to server: " + ko.utils.stringifyJson(self.gifts));
      // To actually transmit to server as a regular form post, write this: ko.utils.postJson($("form")[0], self.gifts);
    };
  };


  // we don't explicitly declare a name property here
  // our view model will be registered under "myCustomViewModel" (implicit
  // name derived from constructor name) and "yourCustomViewModel" (explicitly
  // provided as additional name)
  OCTOPRINT_VIEWMODELS.push({
    construct: GiftModel,
    additionalNames: ["OctoPrintTwilioEvents", "gifts"],
    dependencies: ["loginStateViewModel", "settingsViewModel"],
    elements: ["liveExample"]
  });
})

When I load the main page of the UI I get this in my web browsers console:

packed_core.js?556ae6e8:1002 Could not bind view model SettingsViewModel to target #settings_dialog : ReferenceError: Unable to process binding "text: function(){return gifts().length }"
Message: gifts is not defined

So I kinda get what this means? I think it's saying when the "SettingsViewModel" ran it tried to load my plugins template and that template references the gifts view model when hasn't been created yet?

It seems unlikely that we can't use a custom view model on our settings page? but idk. I don't know knockout and I'm hoping someone can help me get to a working position so I can modify it to fit my needs.

What I am trying to do was have a table with three columns. Column one would be an Event Name, two would be a boolean for taking a picture and three would be some text. That way a user could specify an event and take a picture and text it to themselves with a msg.

Do you have custom_bindings=False in the template configs in the plugin's Python side?

As soon as I seen your code snippet I remembered seeing that in my code.

I have now switched it to true and the error goes away and my js fiddle is working <3 :tada:

Now I just need to find a way to save these values and get them from python, but I can probably do that by looking at some other plugins code.

Thanks @Charlie_Powell

1 Like

I think when I turned on custom_bindings it stopped the rest of my settings page from saving state.

At the top I have the previous form which has some simple fields like this:

<form class="form-horizontal">

	<div class="control-group" title="{{ _('Printer name so you can tell which printer finished.') }}">
		<label class="control-label">{{ _('Unique (to you) Printer name') }}</label>
		<div class="controls">
			<input type="text" class="input-block-level" data-bind="value: settings.plugins.smsnotifier.printer_name">
		</div>
	</div>

then at the bottom I have the new form that is tied to my new ko view model:

<form id='customEvents' class="form-horizontal">
	<div class="control-group">
		<table id="eventsTable" data-bind='visible: gifts().length > 0'>
				<thead>
						<tr>
								<th class='columnName'>Event Name</th>
								<th class='columnMsg'>Text</th>
								<th class='columnPic'>Pic</th>
								<th class='columnDelete'></th>
						</tr>
				</thead>
				<tbody data-bind='foreach: gifts'>
						<tr>
								<td class='columnName'><input type='text' data-bind='value: name, uniqueName: true' /></td>
								<td class='columnMsg'><textarea rows="1" data-bind='value: message, uniqueName: true'></textarea></td>
								<td class='columnPic'><input type='checkbox' data-bind='checked: pic, uniqueName: true' /></td>
								<td class='columnDelete'><button data-bind='click: $root.removeGift'>Delete</button></td>
						</tr>
				</tbody>
		</table>
		<button id='addEvent' data-bind='click: addGift'>Add Event</button>

		<p id='currentEvents'>You are sending messages for <span data-bind='text: gifts().length'>&nbsp;</span> event(s)</p>
	</div>
</form>

It seems now that custom_bindings=True that my "old" settings don't save. I can type stuff into them and click save and if I reopen the plugin settings page the content will see be there. If I stop the octoprint server and reopen the settings tab everything will be gone except whats in my new ko view model.

Is their a way I can have the both? I you can return a list from get_template_configs or do I need to reimplement all my existing form inputs in my new ko view model?

Thanks,
Levi