javascriptphpjqueryajaxcouch-cms

How to access existing tr and fill its td using json from ajax success without generating new row


I am using CouchCMS. In CouchCMS there is a concept of repeatable regions. This in fact generates tables and displays the repeatable contents in it.

I have the repeatable region defined as:

<cms:repeatable name="item_detail" label="Item Detail" order="10" >
    
    <cms:editable name="product" label="Product" type="dropdown" opt_values="Select =- | <cms:pages masterpage='product/product.php' order='asc' orderby='product_name'><cms:show product_name /><cms:if '<cms:not k_paginated_bottom />'>|</cms:if></cms:pages>" order="1" />
    <cms:editable name="product_hsn" label="HSN" type="text" order="2" />hsn,qty,price,gst,amount
    <cms:editable name="product_qty" label="Quantity" type="text" order="3" />
    <cms:editable name="product_price" label="Price" type="text" order="4" />
    <cms:editable name="product_tax" label="Tax" type="text" order="5" />
    <cms:editable name="product_line_total_amount" label="Amount" type="text" order="6" />

</cms:repeatable>  

Where the editables are the regions where we can fill in the data by bounding them to the respective textboxes/ selects, etc.

Now What I am trying to do is:

  1. Select a value from the dropdown of the editable named "product".
  2. When an option is selected, an AJAX is called. This AJAX in turn returns some data.
  3. I am able to get the data, console log it or display it in a div or a table.

But what I really want to do is:

  1. Since the repeatable region is shown is the form of a table, the exists. I want to be able to just get the AJAX JSON data displayed in the of the existing data.
  2. If you see the editables above, a table exists with each editables' select/textbox in the , there are:

Now the repeatable region creates a structure as follows ( for the above defined repeatable region):

Product HSN Quantity Price Tax Amount Delete
<div>
    <p class="addRow" id="addRow_f_item_detail"><a>Add a Row</a></p>
</div>

I can add new rows also right out of the box when using repeatable regions. If one observes ids and the names have a zero [0] this keeps on incrementing as one would add the new rows. The script helping in this above code generation is:

if ( !window.COUCH ) var COUCH = {};
    $(function(){
        $('table.rr > tbody').sortable({
            axis: "y",
            handle: ".dg-arrange-table-rows-drag-icon",
            helper: function (e, ui) { 
                // https://paulund.co.uk/fixed-width-sortable-tables
                ui.children().each(function() {                
                    $(this).width($(this).width());
                });
            return ui;
        },
        update: function( event, ui ){
            var row = ui.item;
            var tbody = $( row ).closest( 'tbody' );
            tbody.trigger('_reorder');
        },
        start: function( event, ui ){    
            var row = ui.item;
            row.trigger('_reorder_start');
        },
        stop: function( event, ui ){
            var row = ui.item;
            row.trigger('_reorder_stop');
        },
    });
});
COUCH.rrInit = function( field_id, default_row ){
    var $field = $('#'+field_id);
    $field.tableGear({addDefaultRow:default_row, stackLayout:1});
    $field.on('click', '.col-actions .add-row', function(){
        var $this = $(this);
        var row_id = $this.attr('data_mosaic_row');
        var add_btn = $('#addRow_'+field_id+' a');
        add_btn.trigger("click", [row_id]);
    });
}
COUCH.t_confirm_delete_row = "Delete this row?";
COUCH.t_no_data_message = "- No Data -";

Just in case if required this is my AJAX code, using which I am able to add a new but it is in a New while I want the to be appended to the same that contains the existing repeatable regions. AJAX CODE:

$(document).on('change','select',function() {
    var data = "";
    $.ajax({
        type:"GET",
        url : "<cms:show k_site_link />generate/quotation-ajax.php",
        data: 
            "select_id="+$(this).val(),
        async: false
    }).done(function(data) {
        console.log(data);
        var trHTML = '';
        $.each(data.product_details, function (i, item) {
            trHTML += "<tr id='f_item_detail-" + i + "'>" + '<td class="editable k_element_product_hsn"><div style="position:relative;"><input type="bound" name=" f_item_detail[0][product_hsn]" id="f_item_detail-[0]-product_hsn" class="form-control" value="' + item.product_hsn + '"/></div></td>' +
            // '<td style="position:relative;"><input type="bound" name=" f_item_detail[0][product_price]" id="f_item_detail-[0]-product_price" class="form-control" value="' + item.product_price + '"/></td>' +
            // '<td style="position:relative;"><input type="bound" name=" f_item_detail[0][product_tax]" id="f_item_detail-[0]-product_tax" class="form-control" value="' + item.product_tax + '"/></td>' +
            '</tr>';
        });
        $('#f_item_detail').append(trHTML);
    })
});

And my AJAX file has the code:

<?php require_once('../couch/cms.php'); ?>
<cms:set selected_product="<cms:gpc 'select_id' method='get' />" scope="global" />
<cms:content_type 'application/json'/>
<cms:template title="Quotation AJAX" hidden='1' parent="_generate_" />
    {
        "product_details": 
        [
            <cms:pages masterpage='product/product.php' custom_field="product_name=<cms:show selected_product />" >
            {
                "product_hsn": "<cms:addslashes><cms:show product_hsn/></cms:addslashes>",
                "product_price": "<cms:addslashes><cms:show min_selling_cost/></cms:addslashes>",
                "product_tax": "<cms:addslashes><cms:show tax_on_purchase/></cms:addslashes>"   
            }<cms:if "<cms:not k_paginated_bottom/>">,</cms:if>
            </cms:pages>
        ]
    }
<?php COUCH::invoke(); ?>

What I am looking for: Add the AJAX success JSON values to the respective textboxes in the existing , and rather than adding a new or or . I am unable to set the correct jQuery. Any help would be really appreciated.

Thanks in advance. Regards! @Swati: Full HTML in this fiddle (with some changes in the AJAX part, which partially works and outputs what I want to achieve. The value is put into the textbox but for each new row the same textbox value is updated from the first row, if i could update the textbox values row wise it would be great)

EDIT #1 I have used your code (@Swati) as follows and yes it works fine (to an extent).

<script type="text/javascript">
    $(document).ready(function(){
        $("#f_item_detail-0-product").select2();
        $('input#f_item_detail-0-product_hsn').attr('readonly', true).addClass("form-control");
        $('input#f_item_detail-0-product_qty').attr('onchange', 'line_total()');
        $('input#f_item_detail-0-product_price').attr('onchange', 'line_total()');
        $('input#f_item_detail-0-product_tax').attr('readonly', true).addClass("form-control");
        $('input#f_item_detail-0-line_tax_amount').attr('readonly', true).addClass("form-control");
        $('input#f_item_detail-0-product_line_total_amount').attr('readonly', true).addClass("form-control");
    });
    
    var counter = 0;
    $(document).ready(function() {
        $(".addRow").click(function(){
            counter++;
            $("#f_item_detail-" + counter + "-product").select2();
        });
    });

    $(document).on('change','select',function() {
        var data = "";
        var i = 0;
        var indexs = $(this).closest("tr").index();//get index no
        console.log(indexs);

        $.ajax({
            type:"GET",
            url : "<cms:show k_site_link />generate/quotation-ajax.php",
            data: 
                "select_id="+$(this).val(),
            async: false
        }).done(function(data) {
            $('#f_item_detail-' + indexs + '-product_hsn').val(data.product_details[i].product_hsn).attr('readonly', true).addClass("form-control");

            $('#f_item_detail-' + indexs + '-product_qty').attr('onchange', 'line_total()');

            $('#f_item_detail-' + indexs + '-product_price').val(data.product_details[i].product_price).attr('onchange', 'line_total()');

            $('input#f_item_detail-' + indexs + '-product_tax').val(data.product_details[i].product_tax).attr('readonly', true).addClass("form-control");

            $('#f_item_detail-' + indexs + '-line_tax_amount').attr('readonly', true).addClass("form-control");

            $('#f_item_detail-' + indexs + '-product_line_total_amount').val(data.product_details[i].product_line_total_amount).attr('readonly', true).addClass("form-control");
        });
    });
            
    function line_total(){
        var line_qty = $('input#f_item_detail-' + indexs + '-product_qty').val();
        var line_tax = $('input#f_item_detail-' + indexs + '-product_tax').val();
        var line_cost =  $('input#f_item_detail-' + indexs + '-product_price').val();
        var line_tax_amount = parseFloat(((line_cost * line_tax)/100) * line_qty).toFixed(2);
        var result = parseFloat((+line_qty * +line_cost) + +line_tax_amount).toFixed(2);
        $('#f_item_detail-' + indexs + '-line_tax_amount').val(line_tax_amount).attr('hidden',true);
        $('#f_item_detail-' + indexs + '-product_line_total_amount').val(result);
    }

</script>

It is solving the issue of going back and editing the product and hence updating the line item value as you had suggested.

But if you see the function line_total() it breaks. And the total are not calculated. What do you suggest? How can we use the indexs value or something else. Also, I would be greatful if you could also suggest me how can we display the GST Amount total and Amount Total at the end with a Grand Total (GST Amount Total + Amount Total), I would be really greatful.

I am not good with javascript or jQuery at all.


Solution

  • Whenever your select-box gets change you can simply get closest tr from that select-box then .find() to find required inputs and add value there .

    Demo Code :

    $(document).on('change', 'select', function() {
      var selector = $(this).closest("tr") //get closest tr
      /* $.ajax({
         type: "GET",
         url: "<cms:show k_site_link />generate/quotation-ajax.php",
         data: "select_id=" + $(this).val(),
         async: false
       }).done(function(data) {*/
      //find your input and add value there
      selector.find('.k_element_product_hsn input').val("ac"); //data.product_details[i].product_hsn
      selector.find('.k_element_product_price input').val(124); //data.product_details[i].product_price
      selector.find('.k_element_product_tax input').val(23); //data.product_details[i].product_tax
      selector.find('.k_element_product_line_total_amount input').val(4356); //data.product_details[i].product_line_total_amount
      selector.find('.k_element_product_qty input').val(2); //data.product_details[i].product_qty
    
    
      /*}
      })*/
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
    
    <table>
      <tbody>
        <tr id="newDataRow_f_item_detail" class="newRow even">
          <td class="dg-arrange-table-rows-drag-icon">&nbsp;</td>
          <td class="editable k_element_product">
            <div style="position:relative;">
              <select name="data[xxx][product]" idx="data-xxx-product" id="data-xxx-product">
                <option value="-">Select</option>
                <option value="3Ply Mask">3Ply Mask</option>
                <option value="Laptop i3 4th Gen">Laptop i3 4th Gen</option>
              </select>
            </div>
          </td>
          <td class="editable k_element_product_hsn">
            <div style="position:relative;"><input type="text" idx="data-xxx-product_hsn" id="data-xxx-product_hsn" name="data[xxx][product_hsn]" value="">
            </div>
          </td>
          <td class="editable k_element_product_qty">
            <div style="position:relative;"><input type="text" idx="data-xxx-product_qty" id="data-xxx-product_qty" name="data[xxx][product_qty]" value="">
            </div>
          </td>
          <td class="editable k_element_product_price">
            <div style="position:relative;"><input type="text" idx="data-xxx-product_price" id="data-xxx-product_price" name="data[xxx][product_price]" value="">
            </div>
          </td>
          <td class="editable k_element_product_tax">
            <div style="position:relative;"><input type="text" idx="data-xxx-product_tax" id="data-xxx-product_tax" name="data[xxx][product_tax]" value="">
            </div>
          </td>
          <td class="editable k_element_product_line_total_amount">
            <div style="position:relative;"><input type="text" idx="data-xxx-product_line_total_amount" id="data-xxx-product_line_total_amount" name="data[xxx][product_line_total_amount]" value="">
            </div>
          </td>
          <td class="delete"><input type="checkbox" name="delete[]" value="" id="deleteNULL_STRING" style="display: none;" /><label for="deleteNULL_STRING">    <img src="http://localhost/CTO/GXCPL-Billing/couch/addons/repeatable/tablegear/delete.gif" alt="Delete Row" /></label></td>
        </tr>
        <tr id="newDataRow_f_item_detail" class="newRow even">
          <td class="dg-arrange-table-rows-drag-icon">&nbsp;</td>
          <td class="editable k_element_product">
            <div style="position:relative;">
              <select name="data[xxx][product]" idx="data-xxx-product" id="data-xxx-product">
                <option value="-">Select</option>
                <option value="3Ply Mask">3Ply Mask</option>
                <option value="Laptop i3 4th Gen">Laptop i3 4th Gen</option>
              </select>
            </div>
          </td>
          <td class="editable k_element_product_hsn">
            <div style="position:relative;"><input type="text" idx="data-xxx-product_hsn" id="data-xxx-product_hsn" name="data[xxx][product_hsn]" value="">
            </div>
          </td>
          <td class="editable k_element_product_qty">
            <div style="position:relative;"><input type="text" idx="data-xxx-product_qty" id="data-xxx-product_qty" name="data[xxx][product_qty]" value="">
            </div>
          </td>
          <td class="editable k_element_product_price">
            <div style="position:relative;"><input type="text" idx="data-xxx-product_price" id="data-xxx-product_price" name="data[xxx][product_price]" value="">
            </div>
          </td>
          <td class="editable k_element_product_tax">
            <div style="position:relative;"><input type="text" idx="data-xxx-product_tax" id="data-xxx-product_tax" name="data[xxx][product_tax]" value="">
            </div>
          </td>
          <td class="editable k_element_product_line_total_amount">
            <div style="position:relative;"><input type="text" idx="data-xxx-product_line_total_amount" id="data-xxx-product_line_total_amount" name="data[xxx][product_line_total_amount]" value="">
            </div>
          </td>
          <td class="delete"><input type="checkbox" name="delete[]" value="" id="deleteNULL_STRING" style="display: none;" /><label for="deleteNULL_STRING">    <img src="http://localhost/CTO/GXCPL-Billing/couch/addons/repeatable/tablegear/delete.gif" alt="Delete Row" /></label></td>
        </tr>
      </tbody>
    </table>

    Updated 1 :

    You can get index of tr which is change then using that index we can update that input values .

    Updated Jquery code :

     $(document).on('change', 'select', function() {
      var data = "";
      var i = 0;
      var indexs = $(this).closest("tr").index();//get index no
      console.log(indexs)
    
      $.ajax({
        type: "GET",
        url: "<cms:show k_site_link />generate/quotation-ajax.php",
        data: "select_id=" + $(this).val(),
        async: false
      }).done(function(data) {
    
        $('#f_item_detail-' + indexs + '-product_hsn').val(data.product_details[i].product_hsn).attr('readonly', true).addClass("form-control");
    
        $('#f_item_detail-' + indexs + '-product_qty').attr('onchange', 'add_number()');
    
        $('#f_item_detail-' + indexs + '-product_price').val(data.product_details[i].product_price).attr('onchange', 'add_number()');
    
        $('input#f_item_detail-' + indexs + '-product_tax').val(data.product_details[i].product_tax).attr('readonly', true).addClass("form-control");
    
        $('#f_item_detail-' + indexs + '-line_tax_amount').attr('readonly', true).addClass("form-control");
    
        $('#f_item_detail-' + indexs + '-product_line_total_amount').val(data.product_details[i].product_line_total_amount).attr('readonly', true).addClass("form-control");
      });
    
    });
    

    Update 2 :

    You can pass this as a parameter to your line_total() then use that to get closest tr index and then do calculation according to that .

    Updated Jquery code :

    $(document).on('change', 'select', function() {
      var indexs = $(this).closest("tr").index();
      var selector = $(this); //save selector
      var i = 0;
       $.ajax({
        type: "GET",
        url: "<cms:show k_site_link />generate/quotation-ajax.php",
        data: "select_id=" + $(this).val(),
        async: false
      }).done(function(data) {
        console.log("de");
        $('#f_item_detail-' + indexs + '-product_hsn').val(data.product_details[i].product_hsn).attr('readonly', true).addClass("form-control");
    
        $('#f_item_detail-' + indexs + '-product_qty').attr('onchange', 'line_total(this)'); //pass this here ...
    
        $('#f_item_detail-' + indexs + '-product_price').val(data.product_details[i].product_price).attr('onchange', 'line_total(this)'); //pass this here
    
        $('input#f_item_detail-' + indexs + '-product_tax').val(data.product_details[i].product_tax).attr('readonly', true).addClass("form-control");
    
        $('#f_item_detail-' + indexs + '-line_tax_amount').attr('readonly', true).addClass("form-control");
    
        $('#f_item_detail-' + indexs + '-product_line_total_amount').val(data.product_details[i].product_line_total_amount).attr('readonly', true).addClass("form-control");
        line_total(selector); //call this
      });
    
    });
    
    
    
    function line_total(selector) {
      //do same here
      var indexs = $(selector).closest("tr").index()
      var line_qty = $('input#f_item_detail-' + indexs + '-product_qty').val() != "" ? $('input#f_item_detail-' + indexs + '-product_qty').val() : 1;
      var line_tax = $('input#f_item_detail-' + indexs + '-product_tax').val();
      var line_cost = $('input#f_item_detail-' + indexs + '-product_price').val();
      var line_tax_amount = parseFloat(((line_cost * line_tax) / 100) * line_qty).toFixed(2);
      var result = parseFloat((+line_qty * +line_cost) + +line_tax_amount).toFixed(2);
      $('#f_item_detail-' + indexs + '-line_tax_amount').val(line_tax_amount).attr('hidden', true);
      $('#f_item_detail-' + indexs + '-product_line_total_amount').val(result);
      grand_total(); //call this
    }
    
    function grand_total() {
      var grand = 0;
      $(".k_element_product_line_total_amount input").each(function() {
        grand += $(this).val() != "" ? parseFloat($(this).val()) : 0
      })
      $("#grand_total").text(grand + 100); //100 is gst change it...according to your need and change id where you need to display grand total
    }