Id of Dice changes before JS time

Asked

Viewed 63 times

1

Hello guys I’m having a huge problem, next I need to create an element and send via ajax wait for the return (done/fail) and perform an action on this element, but sending and creating elements can be done several times in a row and the return of ajax takes different times according to the size of the sent element. The error occurs when performing the action on the element after the return of ajax, as the element identifier may already have changed if another element has been sent then I have this problem within 3 locations, inside the done and fail that the solution will be the same for both and I have this error inside a setTimeOut that is inside Always.

Following is code/plugin link: (Obs.: is equal to the code I put here below in the post) http://jsfiddle.net/mateusfmello/uog1k69p/

Code: (Obs.: error simulation)

// elemento é um elemento do DOM

(function($){
  $.fn.plug = function(optionsPlugin) {
    var plugin = this;

    var defaultsPlugin = {
      debug	: false
      elementos : true
    };

    confPlugin = $.extend(true, defaultsPlugin, optionsPlugin);

    var defaultsEnvio = {
      ajax : {
        type		: 'post',
        url			: '',
        data		: {}				
      }
    };


    if (confPlugin.elementos) {

      var idElement = 0;
      var elementos = new Array();
      // elemento é um elemento do DOM
      var criaElemento = function() {
        elementos.push('novo elemento com id: '+ idElement );
      };
      // elemento é um elemento do DOM
      var removeElemento = function(elemento){
        elemento.fadeOut(1000, function() {
          elemento.remove();
        });
      };
      // elemento é um elemento do DOM
      var statusElemento = function(elemento, status){

        if (status)
          elemento.addClass('ele-sucesso');
        else
          elemento.addClass('ele-erro');
      };
    };

    var enviaAjax = function(){

      $.ajax(
        confEnvio.ajax
      )
      .done(function(res) {
        if (confPlugin.elementos)
          statusElemento(elementos[idElement], true);
      })
      .fail(function(res) {
        if (confPlugin.elementos)
          statusElemento(elementos[idElement], false);
      })
      .always(function() {
        if (confPlugin.elementos)
          setTimeout(
            function(){
              removeElemento(elementos[idElement]);
            }
            , 2000
          );
      });
    };

    var confEnvio = {};

    plugin.enviar = function(optionsEnvio){

      confEnvio = $.extend(true, defaultsEnvio, optionsEnvio);

      if (confPlugin.elementos)
        criaElemento();

      enviaAjax();

      idElement++;
    };

    return this.each(function() {
      return plugin;
    }); 
  }; 
})(jQuery);

var enviaLa = $.fn.plug({debug:true});

enviaLa.enviar();
enviaLa.enviar();

I intend to release this plugin on my Github, when I do this I will put the link here in this post.

  • What’s the idea of the last line return this.each(function () {?

  • @Sergio line this.each I saw in a tutorial on how to create jquery plugins, from there it said that this line serves so that it can be done element.addClass('Lala'). meuPlugin(). removeClass('Lala'); in case if it didn’t it would have to go every function call on a different line.

  • Matthew, as I pointed out in my reply, just pass the idElement as the sending argument to Ajax and assign it to a local variable.

  • @Tobymosque yes I did it came to work, however at the time I run I have to pass a message and each message is different in each execution, but always picks up the last message passed, I tried to do the same scheme, but it does not work with the message.

  • I don’t see why it wouldn’t work with a message, at what point in the code you’re adding it?

3 answers

2

Although the answer from @Sergio points out the problem, I believe it does not solve it, after all the id used in the internal scope is the same as the external scope.

I tried to simulate your problem using a setTimeout to simulate the AJAX request.

var btAdicionar = document.getElementById("btAdicionar");
var tmplLinha = document.getElementById("tmplLinha");
var tabContent = document.getElementById("tabContent");
var indice = 1;

btAdicionar.addEventListener("click", function (event) {
  var linha = tmplLinha.content.cloneNode(true);
  var input = linha.querySelector(".input");
  var output = linha.querySelector(".output");

  input.parentNode.dataset.indice = indice;
  input.textContent = indice;
  window.setTimeout(function () {
    output.textContent = indice;
  }, 2000);   

  tabContent.appendChild(linha);
  indice++;
});
<input id="btAdicionar" type="button" value="adicionarLinha" />
<table>
  <thead>
    <tr>
      <td>Input</td>
      <td>Output</td>
    </tr>
  </thead>
  <tbody id="tabContent">

  </tbody>
</table>

<template id="tmplLinha">
  <tr>
    <td class="input"></td>
    <td class="output"></td>
  </tr>
</template>

The result that we expect is that the input value is equal to the output, but as the index is belongs to a wider scope, so it ends up being updated before the output can be set.

In this case then we create a context for each request:

var btAdicionar = document.getElementById("btAdicionar");
var tmplLinha = document.getElementById("tmplLinha");
var tabContent = document.getElementById("tabContent");
var indice = 1;

var adicionarLinha = function (indice) {
  var indice = indice;
  var linha = tmplLinha.content.cloneNode(true);
  var input = linha.querySelector(".input");
  var output = linha.querySelector(".output");

  input.parentNode.dataset.indice = indice;
  input.textContent = indice;

  window.setTimeout(function () {
    output.textContent = indice;
  }, 2000);   

  tabContent.appendChild(linha);
}

btAdicionar.addEventListener("click", function (event) {
  adicionarLinha(indice);
  indice++;
});
<input id="btAdicionar" type="button" value="adicionarLinha" />
<table>
  <thead>
    <tr>
      <td>Input</td>
      <td>Output</td>
    </tr>
  </thead>
  <tbody id="tabContent">

  </tbody>
</table>

<template id="tmplLinha">
  <tr>
    <td class="input"></td>
    <td class="output"></td>
  </tr>
</template>

note that within the add methodLine, I am declaring again the index variable, this way it happens to have a different value the external index variable.

(function($){ 
  var enviarElemento = function (id) {
    var id = id;
    $.ajax(
      // configurações do ajax
    )
    .done(function(res) {
      fazAlgo(elementos[id], true);
    })
    .fail(function(res) {
      fazAlgo(elementos[id], false);
    })
    .always(function() {
      setTimeout(
        function(){
          removeElemento(elementos[id]);
        }, xTempo);
    });
  }
  
  $.fn.plug = function() {

    var elementos = new Array();
    var id = 0;
    var xTempo = 2000;

    elementos.push('novo elemento e id: ' + id);
    enviarElemento(id);
    id++; // sou obrigado a encrementar pois cada elemento tem que ter um identificador diferente

    var plugin = this;
    return this.each(function() {
      return plugin;
    }); 
  }; 
})(jQuery);

  • +1 different approach from mine, both protect the id to be over-written.

1

I suggest you create an object that stores this information and is in a protected scope to store the right element with the right ID. Something like this:

(function ($) {

    $.fn.plug = (function () {
        var worker = {}; // aqui fica em memória e vai recebendo IDs
        var id = 0;      // aqui e não dentro da função para não ser reiniciado como 0
        var elementos = new Array(); // acho que até isto devia estar fora, mas nõ sei como é o resto do teu código
        return function () {

            var xTempo = 2000;

            elementos.push('novo elemento e id: ' + id);
            worker[id] = elementos[id];
            id++;
            $.ajax( /* confi.*/ ).done(function (res) {
                fazAlgo(worker[id], true);
            }).fail(function (res) {
                fazAlgo(worker[id], false);
            }).always(function () {
                setTimeout(function () {
                    removeElemento(worker[id]);
                }, xTempo);
            });

            // sou obrigado a encrementar pois cada elemento tem que ter um identificador diferente

            var plugin = this;
            return this.each(function () {
                return plugin; // o que faz esta linha?
            });
        }
    })();
})(jQuery);

jsFiddle: http://jsfiddle.net/La5hpu6b/1/

  • I believe he has to create a different scope for each request

  • @Tobymosque in my reply id is never the same. Increments without restarting.

  • all right, maybe I’m not being able to visualize the scopes, and just like you, I have no idea what this this.each in the end realizes.

  • the line this.each I saw in a tutorial on how to create jquery plugins, from there it said that this line serves so that it can be done element.addClass('Lala'). meuPlugin(). removeClass('Lala'); in case if it didn’t it would have to go every function call on a different line.

  • @Mateusfmello I understand the intention but return plugin; doesn’t make much sense. return this without the loop seems ideal because you are not modifying anything. However, my answer helped?

0

EDIT 1

You can instead of incrementing the id, generate a random number to identify the item. See:

$.fn.plug = function() {

    /* Aqui gera o id aleatório */
    var id = String(Math.random() + 1);
    var elementos = new Array();
    elementos.push(id);

    $.ajax(
        // configurações do ajax
    ).done(function(res) {

        fazAlgo(elementos[id], true);
    }).fail(function(res) {

        fazAlgo(elementos[id], false);
    }).always(function() {
        setTimeout(
            function(){
                removeElemento(elementos[id]);
            }, xTempo);
        });

        var plugin = this;
        return this.each(function() {
            return plugin;
        }); 
    }; 
})(jQuery);

EDIT 2

Do the following then:

var ajaxManager = (function() {
     var requests = []; /* Array de requisicoes */

     return {
        addReq:  function(opt) { /* Adiciona requisicao */
            requests.push(opt);
        },
        removeReq:  function(opt) { /* Remove requisicao */
            if( $.inArray(opt, requests) > -1 )
                requests.splice($.inArray(opt, requests), 1);
        },
        run: function() {
            var self = this,
                oriSuc;

            if(requests.length) {
                oriSuc = requests[0].complete;

                requests[0].complete = function() {
                     if( typeof(oriSuc) === 'function' ) oriSuc();
                     requests.shift();
                     self.run.apply(self, []);
                };   

                $.ajax(requests[0]); /* Processa requisicao */
            } else {
              self.tid = setTimeout(function() {
                 self.run.apply(self, []);
              }, 1000);
            }
        },
        stop:  function() { /* Para requisicao */
            requests = [];
            clearTimeout(this.tid);
        }
     };
}());

On the call:

$(function() {
    ajaxManager.run(); 

    $("a.button").click(function(){ /* A cada click adiciona uma requisicao */
       ajaxManager.addReq({
           type: 'POST',
           url: 'whatever.html',
           data: params,
           success: function(data){
              // do stuff
           }
       });
    });
});

You can adapt to your needs.

Source: https://stackoverflow.com/questions/24821064/stacking-multiple-ajax-requests-on-success-jquery

EDIT 3

This example I believe is what you need:

var ajaxManager = {
    arr : [],
    add : function(param, fn){
        var id = String(Math.random() + 1);
        arr.push({run : run(param, fn, id), id : id});
    },

    run : function(param, fn, id){
        $.ajax(param).done(function (res) {
            fn(res); 
            rem(id);
        }).fail(function (res) {
            fn(res); 
            rem(id);
        });
    }

    rem : function(id){
        $.each(arr, function(index, item) {
            if(id === item.id)
                delete arr[index];
        });
    }
};

Pass the query data parameter and last a callback function.

  • here besides not avoiding id reset, you are still entering a second problem, a possible (though unlikely) collision of Ids.

  • @Tobymosque Collision of ids not neh? We are talking about 0.0001 or 0.0000001% of repeating the same id? I find it easier for lightning to fall in the same place. Frameworks with jquery, for example, use this Random generation approach. I don’t think that’s the problem.

  • Unfortunately this does not solve the problem, only changes it, but vlw by trying...

  • @Mateusfernandesdemello see if the second example meets

  • Emirmarques and @Tobymosque follow the most complete code http://goo.gl/e0lGd0 What I need is when the codes inside done, fail or setTimeOut identify the right element

  • @Emirmarques I will try this second solution

  • @Troubleshoot this code does not solve because when it removes requests it always identifies in the code requests.shift();, requests[0].complete, removeReq: function(opt) { etc, in my case it is dynamic, it is the request that will finish earlier will remove the element that she sent

  • @Mateusfmello see this last

Show 3 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.