Rails | Javascript synchronize several fields, avoid infinite loop

90 views Asked by At

I must split a price in two. Therefore I have the equation price_1 + price_2 = total_price. Because I'm very nice, I will allow the user to change any of the 3 params. I must reflect this on my frontend rails view.

Of course, database-wise, I will only store two of these values :) price_1 and total_price

So in my views I have :

<%= f.number_field :price_1, :step => 0.1, class: "form-control" %>
<%= f.number_field :total_price, :step => 0.1, class: "form-control" %>

What I think I could do for the third :

<input id="price_2" type:"number" step="0.1">

And sync with javascript. Of course, the problem being that the 3 inputs are editable. So let's say editing price_1 should change only price_2 and vice-versa, and that changing total_price should only change price_one (and maybe do something about price_2, if some clever user set price_1 to 0 and lower the total_price...)

I've seen very few questions about this (or maybe I couldn't find the good answer cause of bad keywords). This is a fast solution but that can surely fail :

var global_lock = true

$("#price_1").on("change"){
  global_lock = !global_lock  
  if(!global_lock){
    $("#price_2").attr("value", (Integer.parse($("total_price").attr("value")) - Integer.parse($("price_1").attr("value")))
  } 
}

Anything better ? That maybe I could integrate easily using a rails helper ?

EDIT :

let's abstract a bit :P. Instead I have a random equation f(price_1, price_2, total_price) = 0. Therefore my code becomes

var global_lock = 2

$("#price_1").on("change"){
      global_lock = (global_lock++) % 3;  
      if(global_lock == 0){
        //Jquery stuff that always modifies both price_2 and total_price
      } 
    }
//Similar for price_2 and total_price
1

There are 1 answers

3
doz87 On

I've been implementing something similar in my rails app.

You can use a number_field tag if you want to keep it all ruby in your view.

<%= number_field_tag(:price_2,nil,:step => 0.1) %>

My understanding of what you are trying to achieve is:

  1. You currently have 3 fields that the user can edit, named price_1, price_2 and total_price.
  2. total_price is the sum of price_1 and price_2
  3. When any one of the 3 fields is changed, you want to update the other 2 fields based on their respective equations.

The equations are as follows:

Price_1 is changed:

price_2 = total_price - price_1

Price_2 is changed:

price_1 = total_price - price_2

Total_price is changed:

If the change in total_price is greater than the original total_price then

price_1 = price_1 + (total_price - old_total_price)

If the change is less than the original total_price then

if price_1 < (old_total_price - total_price) then

price_1 = 0 and price_2 = price_2 - ( (old_total_price - total_price) - price_1)

else

price_1 = price_1 - (old_total_price - total_price)

And obviously if total_price is set to 0 then everything is 0

If I've got that right then I would write the function like this:

Javascript

I'm a little confused with your use of global_lock so let me know if I'm off track. I've written 3 functions that are bound to any changes, one for each field. Hope this is what you were looking for

var price_1 = $("#price_1");
var price_2 = $("#price_2");
var total_price = $("#total_price");

price_1.bind("change", function () {

    var p1 = Number(price_1.val());
    var total = Number(total_price.val());
    switch (true){

        case (p1 > total ):
            price_1.val(total);
            price_2.val(0);
            break;

        case (p1 < 0 ):
            price_1.val(0);
            price_2.val(total);
            break;

        default:
            price_2.val(Math.round((total - p1)*100)/100);
            break;
    }



});

price_2.bind("change", function () {

    var p2 = Number(price_2.val());
    var total = Number(total_price.val());
    switch (true){

        case (p2 > total ):
            price_1.val(0);
            price_2.val(total);
            break;

        case (p2 < 0 ):
            price_2.val(0);
            price_1.val(total);
            break;

        default:

            price_1.val(Math.round((total - p2)*100)/100);

            break;

    }



});

total_price.bind("change", function () {

    var total = Number(total_price.val());

    if (total < 0) {total = 0}

    var old_total = Number(price_1.val()) + Number(price_2.val());
    var total_difference = old_total - total;
    var p1 = Number(price_1.val());
    var p2 = Number(price_2.val());

    switch (true){

        case (p1 < total_difference):

            price_1.val(0);
            price_2.val(p2 - (total_difference - p1));

            break;

        default:

            price_1.val(p1 - total_difference);

            break;
    }
});