Search code examples
javascripthtmljqueryajaxclone

Result from ajax is duplicated in clone element html (from an array)


In php there is an array with items from a category. There can be several categories. The task is to make a table of products from the selected categories. If the selected categories are two or more, the result is duplicated on the page.

If one category is selected, everything works correctly. When i select two categories, the arrays are different. the first array creates a table and puts the results from two queries into it, then the second array does the same, so it is duplicated. i need the first array to create a table from the first query and the second array to create a second table from the second query.

see clone elements on page - result

How do I remove duplicates of the values of the second block from the first block?

categories_id.forEach(function(category_id) {
  $.ajax({
    type: 'GET',
    url: 'ajax-tab.php' + '?rand=' + new Date().getTime() + '&ajax=true&controller=AdminInventory&action=searchProductsByCategory&token=' + token + '&search_category_id=' + category_id,
    cache: false,
    dataType: "json",
    ajax: true,
    success: function(data) {
      let $injectAfter = $('.inventory_products_lines_head').last();
      $.each(data.products, function(key, val) {
        status = '';
        if (val.active == 1) {
          status = '<i style="color: #72C279;" class="icon-check"></i>';
        } else {
          status = '<i style="color: #E08F95;" class="icon-remove"></i>';
        }
        let clone = $('.inventory_products_lines.inventory_products_lines_values').last().clone();
        clone.find('.id_product_attribute').html('<input name="id_product_attribute[]" id="inventory_id_product_attribute" type="hidden" value="0" />');
        clone.find('.now_quantity').html('<input type="hidden" name="now_quantity[]" id="now_quantity" value="' + val.quantity + '"><span>' + val.quantity + '</span>').attr('data-quantity', val.quantity);
        clone.find(".brand").html('<span>' + val.manufacturer_name + '</span>');
        clone.find(".name").html('<span>' + val.name + '</span>');
        clone.find(".status").html(status);
        clone.find(".wholesale_price").html('<input name="wholesale_price[]" id="wholesale_price" type="hidden" value="' + val.wholesale_price + '" /><span>' + val.formatted_wholesale_price + '</span>');
        clone.find(".price").html('<input name="price[]" id="price" type="hidden" value="' + val.price + '" /><span>' + val.formatted_price + '</span>');
        clone.find('.id_product').html('<input name="id_product[]" id="inventory_id_product" type="hidden" value="' + val.id_product + '" />');
        clone.removeClass('hidden').insertAfter($injectAfter);
        $('.inventory_products_lines_values').closest('div').after(clone);

      });
      let inventory_products = $('#default_inventory_products .inventory_products_block').clone(true);
      inventory_products.find('.search_category_name').append('<h2>' + data.category_name + '</h2>');
      $('#inventory_products').closest('div').after(inventory_products);
      $('.inventory_products_lines_result').each(function(i) {
        var number = i + 1;
        $(this).find('div:first').text(number + ".");
      });
    }
  });
});

   <div class="form-group inventory_products" id="inventory_products"></div>
<div class="hidden" id="default_inventory_products">
    <div class="inventory_products_block">
            <div class="inventory_products_lines inventory_products_lines_head">
                <div></div>
                <div class="hidden"></div>
                <div class="hidden"></div>
                <div class="search_category_name head"></div>
                <div class="combinations_head head">Comb</div>
                <div class="brand_head head">Brand</div>
                <div class="wholesale_price_head head">wholesale price</div>
                <div class="price_head head">retail price</div>
                <div class="now_quantity_head head">now q-ty</div>
                <div class="quantity_head head">new q-ty</div>
                <div class="discrepancy_head head">discrepancy</div>
                <div class="status_head head">status</div>
            </div>
    </div>
</div>
<div class="inventory_products_lines inventory_products_lines_values hidden inventory_products_lines_result">
    <div class="product_number"></div>
    <div class="id_product hidden"></div>
    <div class="id_product_attribute hidden"></div>
    <div class="name"></div>
    <div class="combinations"></div>
    <div class="brand"></div>
    <div class="wholesale_price"></div>
    <div class="price"></div>
    <div class="now_quantity"></div>
    <div class="inventory_quantity"><input name="inventory_quantity[]" id="inventory_quantity" type="text" value="" /></div>
    <div class="discrepancy_block"><input name="discrepancy[]" id="discrepancy" type="hidden" value="" /><span class="discrepancy"></span></div>
    <div class="category_inventory_wholesale_deficit_block"><input name="category_inventory_wholesale_deficit_price[]" id="category_inventory_wholesale_deficit_price" type="hidden" value="" /></div>
    <div class="category_inventory_retail_deficit_price_block"><input name="category_inventory_retail_deficit_price[]" id="category_inventory_retail_deficit_price" type="hidden" value="" /></div>
    <div class="status"></div>
</div>    

I think the problem is clone and wrong html.


Solution

  • Let's create a working example from your code. We will fake ajax using timeout.

    categories_id = [1, 2];
    
    categories_id.forEach(function(category_id) {
      setTimeout(function(data) {
        let $injectAfter = $('.inventory_products_lines_head');
        data = {
          category_name: 'category ' + category_id,
          products: [{
            manufacturer_name: 'nike',
            name: 'alfred',
            quantity: 50,
            id_product: 12,
    
          }, {
            manufacturer_name: 'adidas',
            name: 'berko',
            quantity: 50,
            id_product: 13,
          }]
    
        }
        $.each(data.products, function(key, val) {
          status = '';
          if (val.active == 1) {
            status = '<i style="color: #72C279;" class="icon-check"></i>';
          } else {
            status = '<i style="color: #E08F95;" class="icon-remove"></i>';
          }
          let clone = $('.inventory_products_lines.inventory_products_lines_values').last().clone();
          clone.find(".brand").html('<span>' + val.manufacturer_name + '</span>');
          clone.find(".name").html('<span>' + val.name + '</span>');
          clone.find(".status").html(status);
          clone.find('.id_product').html('<input name="id_product[]" id="inventory_id_product" type="hidden" value="' + val.id_product + '" />');
          clone.removeClass('hidden').insertAfter($injectAfter);
          $('.inventory_products_lines_values').closest('div').after(clone);
    
        });
        let inventory_products = $('#default_inventory_products .inventory_products_block').clone(true);
        inventory_products.find('.search_category_name').append('<h2>' + data.category_name + '</h2>');
        $('#inventory_products').closest('div').after(inventory_products);
        $('.inventory_products_lines_result').each(function(i) {
          var number = i + 1;
          $(this).find('div:first').text(number + ".");
        });
      }, 200 * category_id)
    })
    .hidden {
      display: none
    }
    
    .inventory_products_lines_values,
    .inventory_products_block {
      border: 1px solid green;
      margin: 10px;
      padding: 10px;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
    <div class="form-group inventory_products" id="inventory_products"></div>
    
    <div class="hidden" id="default_inventory_products">
      <div class="inventory_products_block">
        <div class="inventory_products_lines inventory_products_lines_head">
          <div class="search_category_name head"></div>
          <div class="combinations_head head">Comb</div>
          <div class="brand_head head">Brand</div>
          <div class="wholesale_price_head head">wholesale price</div>
          <div class="price_head head">retail price</div>
          <div class="now_quantity_head head">now q-ty</div>
          <div class="quantity_head head">new q-ty</div>
          <div class="discrepancy_head head">discrepancy</div>
          <div class="status_head head">status</div>
        </div>
      </div>
    </div>
    
    
    <div class="inventory_products_lines inventory_products_lines_values hidden inventory_products_lines_result">
      <div class="product_number"></div>
      <div class="id_product hidden"></div>
      <div class="id_product_attribute hidden"></div>
      <div class="name"></div>
      <div class="combinations"></div>
      <div class="brand"></div>
      <div class="wholesale_price"></div>
      <div class="price"></div>
      <div class="now_quantity"></div>
      <div class="inventory_quantity"><input name="inventory_quantity[]" id="inventory_quantity" type="text" value="" /></div>
      <div class="discrepancy_block"><input name="discrepancy[]" id="discrepancy" type="hidden" value="" /><span class="discrepancy"></span></div>
      <div class="category_inventory_wholesale_deficit_block"><input name="category_inventory_wholesale_deficit_price[]" id="category_inventory_wholesale_deficit_price" type="hidden" value="" /></div>
      <div class="category_inventory_retail_deficit_price_block"><input name="category_inventory_retail_deficit_price[]" id="category_inventory_retail_deficit_price" type="hidden" value="" /></div>
      <div class="status"></div>
    </div>

    The result is wrong!

    Now let's build an example that works. For each category we will add a container (a-block) which has a products container within it. Then it's a matter of adding the correct div into correct container. I don't like clone so I've used an HTML template (as string).

    categories_id = [1, 2];
    
    var container = $(".container")
    var template_block = $(".a-block")[0].outerHTML
    var template_product = $(".a-product")[0].outerHTML
    
    categories_id.forEach(function(category_id) {
      setTimeout(function(data) {
    
        data = {
          category_name: 'category ' + category_id,
          products: [{
            manufacturer_name: 'nike',
            name: 'alfred',
            quantity: 50,
            id_product: 12 + category_id * 3,
    
          }, {
            manufacturer_name: 'adidas',
            name: 'berko',
            quantity: 50,
            id_product: 13 + category_id * 3,
          }]
        }
    
        let inventory_products = $(template_block)
        inventory_products.find('h2').html(data.category_name);
        inventory_products.removeClass("hidden")
        container.append(inventory_products);
    
        var products_container = inventory_products.find(".products")
    
        $.each(data.products, function(key, val) {
          let clone = $(template_product)
          clone.removeClass("hidden")
          clone.find(".brand").html('<span>' + val.manufacturer_name + '</span>');
          clone.find(".name").html('<span>' + val.name + '</span>');
          clone.find('.id_product').html('<input name="id_product[]" id="inventory_id_product" type="text" value="' + val.id_product + '" />');
          products_container.append(clone)
        });
    
    
    
      }, 200 * category_id)
    })
    .hidden {
      display: none
    }
    
    .a-block,
    .a-product {
      border: 1px solid green;
      margin: 10px;
      padding: 10px;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
    
    <div class="container">
    </div>
    
    
    <div class="hidden a-block">
      <h2></h2>
      <div class="products">
      </div>
    </div>
    
    <div class="hidden a-product">
      <div class="id_product"></div>
      <div class="brand"></div>
      <div class="name"></div>
      <div class="quantity"></div>
    
    
    
    </div>

    Please note: adding products like that from ajax requests will not guarantee that order of blocks will be as you expect. One option is to wait for all ajax requests to finish before appending them.