5

I'm trying to integrate Rivets.JS into my app. I'm getting the hang of binding values for basic form inputs and regular data in the page, but I can't figure out how to handle selects drop (down menus) with Rivets.

I can build a select with the each tag, like in this JSFiddle: http://jsfiddle.net/eimajenthat/94ca1xht/

But I want to bind the selected value in the menu to a model value (in my Fiddle, I would bind truck.job_id to the select).

Here is my JS:

$(document).ready(function(){
    window.view = rivets.bind($('#contentWrap'),{
        truck:{id:1,job_id:3},
        jobs:[
            {id:1,job_number:'thing1'},
            {id:2,job_number:'thing2'},
            {id:3,job_number:'thing3'},
            {id:4,job_number:'thing4'},
        ]
    });
});

Here is my HTML:

<div id="contentWrap">
    <select>
        <option rv-each-job="jobs" rv-value="job.id">{ job.job_number }</option>
    </select>
</div>

Bonus points: all the select elements in my app are augmented with Chosen. This means whenever I change the options or the selection via JavaScript, I need to trigger an update to Chosen for users to see it, like this:

$('#my-select').trigger('liszt:updated');

How do I trigger the code above whenever the model used for options (jobs in my Fiddle) is updated, or when the value of the select (trucks.job_id) is updated?

eimajenthat
  • 1,338
  • 3
  • 15
  • 33

1 Answers1

7

Well, this turned out to be pretty easy. I considered deleting the question, but maybe I'm not the only idiot out there trying to do this. Here's my updated Fiddle: http://jsfiddle.net/eimajenthat/94ca1xht/2/

Here's the JS:

$(document).ready(function(){
  rivets.formatters.chosen = function(value,selector) {
    $(selector).val(value).trigger('liszt:updated');
    return value;
  };
  window.view = rivets.bind($('#contentWrap'),{
    truck:{id:1,job_id:3},
    jobs:[
      {id:1,job_number:'thing1'},
      {id:2,job_number:'thing2'},
      {id:3,job_number:'thing3'},
      {id:4,job_number:'thing4'},
    ]
  });

  $('#chosen-version').chosen();
});

Here's the HTML:

<div id="contentWrap">
  <select rv-value="truck.job_id">
    <option rv-each-job="jobs" rv-value="job.id">{ job.job_number }</option>
  </select>
  <br/>
  <select id="chosen-version" rv-value="truck.job_id | chosen '#chosen-version'">
    <option rv-each-job="jobs" rv-value="job.id">{ job.job_number }</option>
  </select>
  <p>
    { truck.job_id }
  </p>
</div>

So basically, I forgot that you can specify a value for a <select> just like an <input>. When I realized that, I tried it with rv-value and that works too. From that point, it's really just about the same as binding a text field.

Then there's the Chosen question. Just a note, my app uses an older version of Chosen, and I don't want to upgrade, because it works just fine. I think the event trigger may have a different name now. Anyway, in order to get the Chosen select menu to update, you have to trigger an update event every time the value on the original <select> is updated. I've done this a lot with jQuery, but never with Rivets, since I just started using Rivets last night.

I needed a way to watch the variable and trigger my update event on the variable's change event. Poking around the Rivets docs, it looked like Adapters might fit the bill, since they seem to have monitoring capabilities, but when I dug in, I got a little confused.

Then I started thinking about Rivets Formatters. They have to run every time the variable is updated, in order to update the template. And it turns you they're just functions which take the new value as a parameter, and output the formatted version of the value. So, if you take the value and return it untouched, you've got a little function that reliably gets executed when the variable is updated. You can put your vaguely Germanic event trigger right in there, but you have to manually update the original select first with a call to jQuery's val(), because you are executing before the <select> has actually been updated.

Formatters also provide support for additional parameters, which is quite handy.

Pro Tip: You have to declare the formatter before you call rivets.bind(). Because.

Well, I certainly learned a lot. I guess we'll see if anyone else can benefit. Yay!

eimajenthat
  • 1,338
  • 3
  • 15
  • 33
  • Is there a way of getting the value *and* the id? – arcanine Mar 27 '15 at 12:20
  • Probably, but I couldn't find it. I would think you could setup a watch on truck.job_id using something like Watch.JS (https://github.com/melanke/Watch.JS/), but once you load the model data into Rivets, it seems to go into a mysterious vortex, and I haven't figured out how to access it. Rivets also seems to have its own built in watch capabilities, but I haven't been able to figure them out either. Pretty sure this is because I'm stupid and ignorant, not an actual failing on the part of Rivets. But my stupid ignorant self had a deadline, so I ended up switching to Angular for my project. – eimajenthat Mar 27 '15 at 14:10
  • Here's how I did it in Angular: http://jsfiddle.net/Ln6L5k3k/4/ I took out the generic select and only left the Chosen select, because in my use case all the selects are Chosen ones, so I haven't figured out how to do this with a non-Chosen select (pretty sure there are abundant examples out there, but I didn't need to do it). – eimajenthat Mar 27 '15 at 15:36
  • Also, I'm pretty sure there is a way to do this Rivets. I just couldn't figure it out. Likewise, I think there may be a cleaner way to do it with Angular than mine, but I stopped coding when it started working. I'm a little outside my coding comfort zone with both these libraries. – eimajenthat Mar 27 '15 at 15:39
  • yeah I understand, I don't think it's just you I feel like rivets documentation is lacking, I think in future I'm going to either look at react or don't use data binding until something better comes along – arcanine Mar 27 '15 at 18:59