1

I need to implement a complicated form. For example, there are fields for summands, sum and percentage of each summand in that sum. Illustration:

Value1:  1    10%
Value2:  4    40%
Value3:  5    50%
Sum:    10   100%

The real life example is much more complicated. Everything is editable. So, I assume several scenarios:

  1. Editing a value updates percentages and sum.
  2. Editing the sum updates values according to their percentages.
  3. Editing a percentage updates all other percentages to be 100%, which itself updates all the values and sum.

I am currently trying to implement something like this using Backbone.js (just because I'm familiar with it), but the solution already looks over-engineered, and I am just in the beginning.

I wonder, is there any other approach I can look into? I'm not an FRP person, but probably functional/reactive approach can help somehow?

If there is a framework or library designed to solve such kind of problems, I would be happy to look into it.

Bardt
  • 695
  • 1
  • 8
  • 17
  • For #3, if the first percentage increases by 10%, how should the other percentages change? Should 5% be subtracted from each? – Rick Hitchcock Nov 23 '14 at 14:38
  • I think AngularJS is more suitable to your case. This [answer](http://stackoverflow.com/questions/9682092/databinding-in-angularjs#answer-9693933) might help you – hindmost Nov 23 '14 at 14:40

3 Answers3

2

Seems like a task for Constraint programming. I'd recommend you to take a look at https://github.com/slightlyoff/cassowary.js and also this talk http://www.youtube.com/watch?v=72sWgwaAoyk

Roman Pominov
  • 1,403
  • 1
  • 12
  • 17
  • This one looks great, but I found a lack of documentation. Is there a complex doc or book about this library? – Bardt Dec 10 '14 at 05:06
1

I implemented similar task using Kefir.js not so long ago.

Please, find my example — http://jsfiddle.net/mistakster/kwx5j8wm/

The main aproach is:

  1. Create event stream for values, precents and sum.

    var streamVal = Kefir.fromBinder(function (emitter) {
        function handler() {
            var values = $.map($table.find('.val'), function (ele) {
                return $(ele).val();
            });
            emitter.emit(values);
        }
        $table.on('change', '.val', handler);
        return function () {
            $table.off('change', '.val', handler);
        };
    });
    
  2. Implement your business logic, which apply changes to other fields based on data from the streams.

So, that’s all. As simple as draw an owl. :-)

Vladimir Kuznetsov
  • 1,781
  • 1
  • 12
  • 12
0

Since you say other frameworks are acceptable, I've created a jQuery solution below.

Criterium #3 is a bit ambiguous, so let me know if this needs changing:

$('.val').change(function() {
  var tot= 0;
  $('.val').each(function() {
    tot+= parseInt($(this).val(),10);
  });
  $('#sum').val(tot);
  
  $('.pct').each(function(index) {
    $(this).val(($('.val')[index].value/tot*100).toFixed(0));
    $(this).data('prevValue', $(this).val());
    $(this).data('index', index);
  });
});

$('#sum').change(function() {
  var sum= $(this).val();
  $('.val').each(function(index) {
    $(this).val($('.pct')[index].value*sum/100);
  });
});

$('.pct').change(function() {
  var index= $(this).data('index');
  var prevValue= $(this).data('prevValue')
  
  $('.val')[index].value= ($('.val')[index].value*$(this).val()/prevValue).toFixed(0);
  $('.val').change();
});


$('.val').change();
.val, .pct, #sum, tpct {
  width: 3em;
  text-align: right;
}

th {
  text-align:right;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
 <tr><td>Value1:<td><input class="val" value="1"><td><input class="pct">%
 <tr><td>Value2:<td><input class="val" value="4"><td><input class="pct">%
 <tr><td>Value3:<td><input class="val" value="5"><td><input class="pct">%
 <tr><td>Sum:   <td><input id="sum"             ><th>100%
</table>
Rick Hitchcock
  • 35,202
  • 5
  • 48
  • 79