如何计算两个方向的表单字段值?

时间:2014-04-27 16:12:39

标签: javascript jquery forms

我尝试构建项目的表单,我可以在其中放置不同的公式来计算某些字段。

主要问题:我想让公式双向工作,例如

  • 当用户输入项目的price时,表单应根据sumprice * price)计算qty
  • 当用户输入sum时,表单应根据pricesum / sum)计算项qty

我找到了Calcx -- great and powerful jQuery plugin for building a calculation form并根据我的需要修改了其中一个例子,但没想到有可能以某种方式让它像我上面的描述那样工作。

还有一些其他问题我找不到解决方案:

  • 具有公式和集readonly: false的字段应该是可编辑的。我的示例中包含类.sum的字段仍然不可编辑。为什么?
  • 我在+ - 字段周围添加了-qty按钮到incr / decr功能,但为了让它们正常工作,我必须分离Calcx功能并再次附加。是否有更简单的方法来实现这一目标?

免责声明我的问题的解决方案也可能涉及除Calcx之外的其他技术。也许某些插件或框架有更好的工具满足我的需求。

对于历史记录,我也在这里添加代码示例,但要使用它可能更适合查看JSfiddle

<!DOCTYPE html>
<html>
<head>
  <title>testc calcx</title>
  <meta charset=utf-8>
  <meta name=description content="testime">
  <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
  <script type="text/javascript" src="/js/jquery-calx-1.1.9.js"></script>

<style type="text/css">
  .plusminus {
    font-weight: bold;
    font-family: monospace;
    font-size: 1.3em;
    border: 1px green solid;
    padding: 0px 5px;
  }
</style>

  <script>

  $(document).ready(function(){

      $('#itemlist').calx();

      $('.sum').calx({
        readonly: false
      });

      $('.plusminus').click( 
        function () {
          $('#itemlist').calx('detach');  

          var operation = $( this ).text();
          var row = $( this ).attr('id').split('_').slice(1);
          var qty = $( '#qty_' + row ).val();

          if ( operation == '+' ) {
            $( '#qty_' + row ).val( ++qty );
          } else if ( operation == '-' ) {
            $( '#qty_' + row ).val( --qty );
          } else {
            alert( "Something wrong! " + $( this ).attr('id') );
          }
          $('#itemlist').calx();  
          $('#itemlist').calx('refresh');
        }
      );
  });

  </script>
</head>
<body>

<form id="itemlist">

  <input type="text" placeholder="Item" id="A1" size="20" value="HDD Baracuda Black 2TB" />

    <span id="min_1" class="plusminus">-</span>
    <input type="text" placeholder="Qty" id="qty_1" value="1" size="2" data-format="0" />
    <span id="plus_1" class="plusminus" >+</span>

  <input type="text" placeholder="Price" id="C1" size="5" data-format="$ 0,0[.]00" />
  <input type="text" placeholder="Disc." id="D1" size="3" data-format="0[.]00 %" />
  <input type="text" placeholder="Sum" id="E1" size="6" class="sum" data-formula="($qty_1*$C1)*(1-$D1)" data-format="$ 0,0[.]00" />
  <br />

  <input type="text" placeholder="Item" id="A2" size="20" value="Motherboard ASus XYZ" />

    <span id="min_2" class="plusminus">-</span>
    <input type="text" placeholder="Qty" id="qty_2" value="1" size="2" data-format="0" />
    <span id="plus_2" class="plusminus" >+</span>

  <input type="text" placeholder="Price" id="C2" size="5" data-format="$ 0,0[.]00" />
  <input type="text" placeholder="Disc." id="D2" size="3" data-format="0[.]00 %" />
  <input type="text" placeholder="Sum" id="E2" size="6" class="sum" data-formula="($qty_2*$C2)*(1-$D2)" data-format="$ 0,0[.]00" />
  <br />

  <input type="text" placeholder="Item" id="A3" size="20" value="Memory Kingston DDR3 4GB" />

    <span id="min_3" class="plusminus">-</span>
    <input type="text" placeholder="Qty" id="qty_3" value="1" size="2" data-format="0" />
    <span id="plus_3" class="plusminus" >+</span>

  <input type="text" placeholder="Price" id="C3" size="5" data-format="$ 0,0[.]00" />
  <input type="text" placeholder="Disc." id="D3" size="3" data-format="0[.]00 %" />
  <input type="text" placeholder="Sum" id="E3" size="6" class="sum" data-formula="($qty_3*$C3)*(1-$D3)" data-format="$ 0,0[.]00" />
  <br />

  <input type="text" placeholder="" id="total_1" data-formula="SUM($E1,$E3)" data-format="$ 0,0[.]00" />
</form>

</body>
</html>

5 个答案:

答案 0 :(得分:8)

我建议你使用库ractive.js。看看它的双向数据绑定是多么简单和自动:http://learn.ractivejs.org/two-way-binding/1/

<label>Enter your name: <input value='{{name}}'></label>
<p>Hello, {{name}}!</p>

所以你可以这样做:

<label>Price: <input value='{{price}}'></label>
<label>Quantity: <input value='{{quantity}}'></label>
<label>Sum: <input value='{{price * quantity}}'></label>

通过这种简单的方式,您可以使用{{price * quantity}}等表达式并保存大量代码。 http://learn.ractivejs.org/expressions/2/

您可以在{{}}和事件之间添加js函数:

http://learn.ractivejs.org/event-proxies/1/

您可以在60秒内了解如何设置ractive:http://www.ractivejs.org/60-second-setup

在其交互式教程中轻松学习:http://learn.ractivejs.org/hello-world/1/

编辑1:

我添加了此jsfiddle作为示例。

答案 1 :(得分:1)

我不喜欢图书馆,特别是如果我可以避免它们。你可以在这里清楚地避免它们。

我基本上把每个项目的所有数据都放在了自制的

<row></row>

有了这个,我可以使用

$(this).parent();

在每个输入上,我立即拥有所需的行。从那里我将找到其他价值观。

jsFiddle

$(document).ready(function(){

    function calcTotal(){
        var items = $("#itemlist row").length;        
        var sums = $(".sum");

        var total = 0
        for(var i = 0; i < items; i++){
            if(sums.eq(i).val()){
                total += parseFloat(sums.eq(i).val());
            }
        }

        $("#total").val(parseFloat(total).toFixed(2));
    }

    function calcSum(object){
        var row = $(object).parent();                
        var qty = row.find(".qty");
        var prc = row.find(".prc");
        var sum = row.find(".sum");        
        var disc = row.find(".disc");

        var newSum = qty.val()*prc.val()*(1-disc.val()/100);

        sum.val(parseFloat(newSum).toFixed(2));

        calcTotal();
    }

    function calcPrice(object){
        var row = $(object).parent();                
        var qty = row.find(".qty");
        var prc = row.find(".prc");
        var sum = row.find(".sum");        
        var disc = row.find(".disc");

        var newPrice = -100*sum.val()/(disc.val()-100)/qty.val();

        prc.val(parseFloat(newPrice).toFixed(2));

        calcTotal();
    }


    $('.plusminus').click(function(){            
        var operator = parseFloat($(this).text()+"1");
        var row = $(this).parent();
        var qty = row.find(".qty");
        var qtyVal = parseFloat(qty.val());

        qty.val(parseFloat(qtyVal+operator));

        calcSum(this);
    });

    $(".prc, .disc, .qty").blur(function(){   
        calcSum(this);
    });

    $(".sum").blur(function(){
        calcPrice(this);
    });

});

<body>
<form id="itemlist">  
    <row>
    <input type="text" placeholder="Item" class="name" size="20" value="HDD Baracuda Black 2TB" />

    <span id="min_1" class="plusminus">-</span>
    <input type="text" placeholder="Qty" class="qty" value="1" size="2"/>
    <span id="plus_1" class="plusminus" >+</span>

  <input type="text" placeholder="Price" class="prc" size="5"/>
  <input type="text" placeholder="Disc." class="disc" size="3"/>
  <input type="text" placeholder="Sum" class="sum" size="6"/>
    </row>
    <row>
  <input type="text" placeholder="Item" class="name" size="20" value="Motherboard ASus XYZ" />

    <span id="min_1" class="plusminus">-</span>
    <input type="text" placeholder="Qty" class="qty" value="1" size="2"/>
    <span id="plus_1" class="plusminus" >+</span>

  <input type="text" placeholder="Price" class="prc" size="5"/>
  <input type="text" placeholder="Disc." class="disc" size="3"/>
  <input type="text" placeholder="Sum" class="sum" size="6"/>
    </row>
    <row>
  <input type="text" placeholder="Item" class="name" size="20" value="Memory Kingston DDR3 4GB" />

    <span id="min_1" class="plusminus">-</span>
    <input type="text" placeholder="Qty" class="qty" value="1" size="2"/>
    <span id="plus_1" class="plusminus">+</span>

  <input type="text" placeholder="Price" class="prc" size="5"/>
  <input type="text" placeholder="Disc." class="disc" size="3"/>
  <input type="text" placeholder="Sum" class="sum" size="6"/>
    </row>
  <input type="text" placeholder="Total" id="total"/>
</form>
</body>

哦,我稍微改了一下加号 - 对不起。

答案 2 :(得分:0)

似乎我的问题不够明确。我发布了自己的问题解决方案。它只使用jQuery,并不像一些额外的插件那样通用。但它的行为完全符合我的要求,而且我添加了在同一表单中包含远程子表单的功能。这是jsFiddle version可以使用,下面是解决方案。

现在可以更改行总和,以便根据它计算价格,反之亦然。

<!DOCTYPE html>
<html>
<head>
  <title>test calculated forms</title>
  <meta charset=utf-8>
  <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>

<style type="text/css">
  .plusminus {
    font-weight: bold;
    font-family: monospace;
    font-size: 1.3em;
    border: 1px green solid;
    padding: 0px 5px;
  }
</style>

  <script>

  $(document).ready(function(){
      $('.plusminus').click( 
        function () {

          var operation = $( this ).text();
          var attr = $( this ).attr('id').split('_');
          var cell_id = attr[0] + '_B_' + attr[2];
          var qty = $( '#' + cell_id ).val();

          if ( operation == '+' ) {
            $( '#' + cell_id ).val( ++qty );
          } else if ( operation == '-' ) {
            $( '#' + cell_id ).val( --qty );
          } else {
            alert( "Something wrong! " + $( this ).attr('id') );
          }
          recalculate_row_sum( cell_id );
        }
      );

      $( '.form_sum' ).on( 'change', function() {
        recalculate_unit_price( $( this ).attr('id') );
      });

      $( '.form_price' ).on( 'change', function() {
        recalculate_row_sum( $( this ).attr('id') );
      });

      $( '.form_discount' ).on( 'change', function() {
        recalculate_row_sum( $( this ).attr('id') );
      });

      $( '.form_qty' ).on( 'change', function() {
        recalculate_row_sum( $( this ).attr('id') );
      });

      init_ranges();
  });


  // you may have many independent ranges on same form, to init them on load is init_ranges-function
  function init_ranges () {
    var ranges = $( '#ranges' ).val().split( '-' );

    for (var i = ranges[0]; i <= ranges[1]; i++) {
      init_range_rows( i );
    }
  }

  function init_range_rows ( range ) {
    var edges = $( '#R' + range ).val().split( '-' );

    for (var i = edges[0]; i <= edges[1]; i++) {
      recalculate_row_sum( 'R' + range + '_C_' + i );
    }
  }


  function recalculate_unit_price ( cell_id, data ) {
    var row_data = get_row_data( cell_id, data );
    var unit_price = ( row_data.row_sum/(100 -row_data.discount)*100/row_data.qty ).toFixed(2);
    $( '#' + row_data.range + '_C_' + row_data.row ).val( unit_price );

    recalculate_range_total( cell_id, row_data );
  }

  function recalculate_row_sum ( cell_id, data ) {
    var row_data = get_row_data( cell_id, data );
    var sum = ( row_data.unit_price*(100 - row_data.discount)/100*row_data.qty ).toFixed(2);
    $( '#' + row_data.range + '_E_' + row_data.row ).val( sum );

    recalculate_range_total( cell_id, row_data );
  }

  function recalculate_range_total ( cell_id, data ) {
    var row_data = get_row_data( cell_id, data );
    var edges = $( '#' + row_data.range ).val().split( '-' );
    var total_amount = 0;

    for (var i = edges[0]; i <= edges[1]; i++) {
      total_amount += $( '#' + row_data.range + '_E_' + i ).val() * 1;
    }
    $( '#' + row_data.range + '_total' ).val( ( total_amount ).toFixed(2) );
  }

  function get_row_data ( cell_id, cached_data ) {
    if ( typeof cached_data === 'defined' ) {
      return cached_data; 
    }

    var row_info = cell_id.split( '_' );
    var data = {
      range: row_info[0],
      col: row_info[1],
      row: row_info[2],
      unit_price: $( '#' + row_info[0] + '_C_' + row_info[2] ).val() || 0 , 
      qty: $( '#' + row_info[0] + '_B_' + row_info[2] ).val() || 0, 
      discount: $( '#' + row_info[0] + '_D_' + row_info[2] ).val() || 0, 
      row_sum: $( '#' + row_info[0] + '_E_' + row_info[2] ).val() || 0
    };
    return data;
  }

  </script>
</head>
<body>

<form id="itemlist" method="post" enctype="multipart/form-data" >

  <input type="hidden" id="ranges" name="ranges" value="1-3" />

  <input type="text" placeholder="Item" id="R1_A_1" name="A_1" size="20" class="form_item" value="HDD Baracuda Black 2TB" />

    <span id="R1_min_1" class="plusminus">-</span>
    <input type="text" placeholder="Qty" id="R1_B_1" name="B_1" value="1" size="2" class="form_qty" />
    <span id="R1_plus_1" class="plusminus" >+</span>

  <input type="text" placeholder="Price" id="R1_C_1" name="C_1" size="5" class="form_price" value="27" />
  <input type="text" placeholder="Disc." id="R1_D_1" name="D_1" size="3" class="form_discount" />
  <input type="text" placeholder="Sum" id="R1_E_1" name="E_1" size="6" class="form_sum" />
  <br />

  <input type="text" placeholder="Item" id="R1_A_2" name="A_2" size="20" class="form_item" value="Motherboard ASus XYZ" />

    <span id="R1_min_2" class="plusminus">-</span>
    <input type="text" placeholder="Qty" id="R1_B_2" name="B_2" value="1" size="2" class="form_qty" />
    <span id="R1_plus_2" class="plusminus" >+</span>

  <input type="text" placeholder="Price" id="R1_C_2" name="C_2" size="5" class="form_price" value="46" />
  <input type="text" placeholder="Disc." id="R1_D_2" name="D_2" size="3" class="form_discount" />
  <input type="text" placeholder="Sum" id="R1_E_2" name="E_2" size="6" class="form_sum" />
  <br />

  <input type="text" placeholder="Item" id="R1_A_3" name="A_3" size="20" class="form_item" value="Memory Kingston DDR3 4GB" />

    <span id="R1_min_3" class="plusminus">-</span>
    <input type="text" placeholder="Qty" id="R1_B_3" name="B_3" value="1" size="2" class="form_qty" />
    <span id="R1_plus_3" class="plusminus" >+</span>

  <input type="text" placeholder="Price" id="R1_C_3" name="C_3" size="5" class="form_price" value="18" />
  <input type="text" placeholder="Disc." id="R1_D_3" name="D_3" size="3" class="form_discount" />
  <input type="text" placeholder="Sum" id="R1_E_3" name="E_3" size="6" class="form_sum" />
  <br />

  <input type="text" placeholder="Total Amount" id="R1_total" name="R1_total" />

  <br />
  <br />

  <input type="text" placeholder="Item" id="R2_A_4" name="A_4" size="20" class="form_item" value="Other foo" />

    <span id="R2_min_4" class="plusminus">-</span>
    <input type="text" placeholder="Qty" id="R2_B_4" name="B_4" value="1" size="2" class="form_qty" />
    <span id="R2_plus_4" class="plusminus" >+</span>

  <input type="text" placeholder="Price" id="R2_C_4" name="C_4" size="5" class="form_price" value="18" />
  <input type="text" placeholder="Disc." id="R2_D_4" name="D_4" size="3" class="form_discount" />
  <input type="text" placeholder="Sum" id="R2_E_4" name="E_4" size="6" class="form_sum" />
  <br />

  <input type="text" placeholder="Item" id="R2_A_5" name="A_5" size="20" class="form_item" value="Other bar" />

    <span id="R2_min_5" class="plusminus">-</span>
    <input type="text" placeholder="Qty" id="R2_B_5" name="B_5" value="1" size="2" class="form_qty" />
    <span id="R2_plus_5" class="plusminus" >+</span>

  <input type="text" placeholder="Price" id="R2_C_5" name="C_5" size="5" class="form_price" value="18" />
  <input type="text" placeholder="Disc." id="R2_D_5" name="D_5" size="3" class="form_discount" />
  <input type="text" placeholder="Sum" id="R2_E_5" name="E_5" size="6" class="form_sum" />
  <br />

  <input type="text" placeholder="Total Amount" id="R2_total" name="R2_total" />

  <br />
  <br />

  <input type="text" placeholder="Item" id="R3_A_6" name="A_6" size="20" class="form_item" value="Some X" />

    <span id="R3_min_6" class="plusminus">-</span>
    <input type="text" placeholder="Qty" id="R3_B_6" name="B_6" value="1" size="2" class="form_qty" />
    <span id="R3_plus_6" class="plusminus" >+</span>

  <input type="text" placeholder="Price" id="R3_C_6" name="C_6" size="5" class="form_price" value="18" />
  <input type="text" placeholder="Disc." id="R3_D_6" name="D_6" size="3" class="form_discount" />
  <input type="text" placeholder="Sum" id="R3_E_6" name="E_6" size="6" class="form_sum" />
  <br />

  <input type="text" placeholder="Item" id="R3_A_7" name="A_7" size="20" class="form_item" value="Some Y" />

    <span id="R3_min_7" class="plusminus">-</span>
    <input type="text" placeholder="Qty" id="R3_B_7" name="B_7" value="1" size="2" class="form_qty" />
    <span id="R3_plus_7" class="plusminus" >+</span>

  <input type="text" placeholder="Price" id="R3_C_7" name="C_7" size="5" class="form_price" value="18" />
  <input type="text" placeholder="Disc." id="R3_D_7" name="D_7" size="3" class="form_discount" />
  <input type="text" placeholder="Sum" id="R3_E_7" name="E_7" size="6" class="form_sum" />
  <br />

  <input type="text" placeholder="Item" id="R3_A_8" name="A_8" size="20" class="form_item" value="Some Z" />

    <span id="R3_min_8" class="plusminus">-</span>
    <input type="text" placeholder="Qty" id="R3_B_8" name="B_8" value="1" size="2" class="form_qty" />
    <span id="R3_plus_8" class="plusminus" >+</span>

  <input type="text" placeholder="Price" id="R3_C_8" name="C_8" size="5" class="form_price" value="18" />
  <input type="text" placeholder="Disc." id="R3_D_8" name="D_8" size="3" class="form_discount" />
  <input type="text" placeholder="Sum" id="R3_E_8" name="E_8" size="6" class="form_sum" />
  <br />

  <input type="text" placeholder="Item" id="R3_A_9" name="A_9" size="20" class="form_item" value="Some Õ" />

    <span id="R3_min_9" class="plusminus">-</span>
    <input type="text" placeholder="Qty" id="R3_B_9" name="B_9" value="1" size="2" class="form_qty" />
    <span id="R3_plus_9" class="plusminus" >+</span>

  <input type="text" placeholder="Price" id="R3_C_9" name="C_9" size="5" class="form_price" value="18" />
  <input type="text" placeholder="Disc." id="R3_D_9" name="D_9" size="3" class="form_discount" />
  <input type="text" placeholder="Sum" id="R3_E_9" name="E_9" size="6" class="form_sum" />
  <br />

  <input type="text" placeholder="Total Amount" id="R3_total" name="R3_total" />

  <input type="hidden" id="R1" name="R1" value="1-3" />
  <input type="hidden" id="R2" name="R2" value="4-5" />
  <input type="hidden" id="R3" name="R3" value="6-9" />

  <br />
  <br />

  <input type="submit" name="test" value="submit all forms" />

</form>

</body>
</html> 

我仍然希望在这里看到更多通用和有效的解决方案!

答案 3 :(得分:0)

您不太可能找到相应的组件,因为大多数工作流都针对单向处理进行了优化:至少有两个参数来定义结果。其他方向含糊不清。以PR = UP * Q为单位,以单价(UP),数量(Q)和价格(PR)为例。没有真正的常见用例&#34;启用PR编辑;这意味着什么:你需要计算一个新的UP = PR / Q或一个新的Q = PR / UP? (如果你现在可以决定,那并不意味着没有任何选项,或者只有另一个选择是足够的。拥有3个字段你有a = fn_a(b,c),b = fn_b( a,c)和c = fn_c(a,b)来计算值。当编辑其中一个(比如X)时,它会淘汰X = fn_X ...计算,但你还有两个可供选择的(和如果发生了真正的变化,那么通常两者都会分配不同的值。)两个公式之间的选择可能取决于输入的上下文和/或值和/或控件最后使用的顺序。这根本不是框架问题,但它是页面独特功能的一个组成部分。如果你已经设计了它应该如何工作,那么你可以从框架中获得一些的帮助.AngularJS允许你将每个控件绑定到一个属性对象,因此对象可以存储相关状态(例如,控件的使用顺序)并相应地改变其计算行为(例如,总是重新计算)最近最近输入的参数基于所有更新的参数。)

答案 4 :(得分:0)

修改html代码我在没有外部插件的情况下创建了这个脚本jQuery Fiddle
此脚本计算tot-row,tot-group,price-per-unit,average-price,并显示最后更改的值。
可以更改行总和,并重新计算价格 可能由于我的无知,我不明白范围的问题,请你澄清一下吗?

<script type="text/javascript">
$(document).ready(function(){

     $('input.Row').each(function(){$(this).val('Row nuber: '+$(this).parent('div').find('div').length)})
     function calculateTot(elem){
         var ind=elem.parent().index()+1
         var totSum=0,averagePrice=0
         var collection1=elem.parents('div').find('input.sum')
         var collection2=elem.parents('div').find('input.price')
         for(i=0;i<collection1.length;i++){
              totSum+=parseFloat(collection1.get(i).value)
              averagePrice+=parseFloat(collection2.get(i).value)
              }
         elem.parents('div').find('input.Amount').val(totSum.toFixed(2))
         elem.parents('div').find('input.AvPrice').val((averagePrice/elem.parents('div').find('input.price').length).toFixed(2))
         elem.get(0).className==='plusminus'?elem.parents('div').find('input.Change').val('Changed: Qty row:'+ind):
         elem.parents('div').find('input.Change').val('Changed: '+elem.get(0).className+' row:'+ind)
     }

    function calculateScount(elem){
        var qty =parseInt(elem.parent().find('input.qty').val())
        var price=parseFloat(elem.parent().find('input.price').val())
        var thisVal=parseFloat(elem.val())
        var scount=(price*qty)/100*thisVal
        elem.parent().find('input.sum').val((price*qty-scount).toFixed(2))
        calculateTot(elem) 
     }

     function calculate(elem){
        var qty =parseInt(elem.parent().find('input.qty').val())
        var price=parseFloat(elem.parent().find('input.price').val())
        var scontValue=parseFloat(elem.parent().find('input.discount').val())
        elem.parent().find('input.sum').val((price*qty).toFixed(2));
        calculateTot(elem)
        if(scontValue>0){calculateScount(elem.parent().find('input.discount'))}
      }

      function retro(elem){
        var scontValue=parseFloat(elem.parent().find('input.discount').val())
        elem.parent().find('input.price').val(parseFloat(elem.val()/elem.parent().find('input.qty').val()).toFixed(2))
        calculateTot(elem)
        if(scontValue>0){calculateScount(elem.parent().find('input.discount'))} 
      }

      $('.plusminus').click(function(){
        var val=$(this).text()  
        if(val==='+'){
            var qty=parseInt($(this).prev().val())
            qty++
            $(this).prev().val(qty)
            calculate($(this))
        }

        if(val==='-'&&parseInt($(this).next().val())>=1){
            var qty=parseInt($(this).next().val())
            qty--
            $(this).next().val(qty)
            calculate($(this))
        }

       });
     $('input.qty,input.price').on('change',function(){calculate($(this))})  
     $('input.discount').on('change',function(){calculateScount($(this))})  
     $('input.sum').on('change',function(){retro($(this))})      
});
</script>