2009-05-19 9 views
1

J'ai un problème étrange avec du code JavaScript (encore une fois, je déteste le débogage du code JS). Je travaille sur une table normale - que je remplis à partir d'un appel JSON, et j'ai ajouté à la prise en charge de certains pagination (sorte de pagination 2x je suppose que vous pourriez l'appeler), de tri et de sélection de lignes. Tout fonctionne bien - MAIS quand une ligne est DESELECTED (et désélectionnée seulement) mon événement add_navigate est déclenché deux fois, ce qui entraîne un peu de rechargement de données qui n'est pas nécessaire - et une indication de chargement qui n'est même plus nécessaire.Double problème de chargement dans l'hybride javascript/jQuery/Microsoft-ajax

premier est ici mon code JS:

var customerType; 
var selYear; 
var selMonth; 
var sdir; 
var sort; 
var page; 
var noteId; 
var hasDoneCall; 
var customerId; 

var customerIdChanged = false; 


function initValues() { 
    customerType = "Publisher"; 
    selYear = new Date().getFullYear(); 
    selMonth = new Date().getMonth()+1; 
    sdir = false; 
    sort = "CustomerName"; 
    page = 1; 
    noteId = false; 
    customerId = 0; 

    hasDoneCall = location.href.indexOf('#') > 0; 

} 
function flash(elm, color, duration) { 

    var current = elm.css('backgroundColor'); 

    elm.animate({ backgroundColor: 'rgb(' + color + ')' }, duration/2).animate({ backgroundColor: current }, duration/2); 

} 

function createNotes(elm) { 
    var btn = jQuery(elm); 
    btn.attr('disabled', 'disabled'); 
    bulkCreditOption('true', '', function(changeSet) { 
     var i = 0; 
     while (i < changeSet.length) { 
      var selector = "input[type=checkbox][value=" + changeSet[i] + "].check:checked"; 
      var row = jQuery(selector).parent().parent(); 
      var cell = row.find("td:nth-child(2)"); 

      cell.html("<a href=\"javascript:showNotes('" + changeSet[i] + "')\">" + cell.html() + "</a>"); 
      flash(row, '60, 130, 200', 500); 
      i++; 
     } 
     btn.removeAttr('disabled'); 
    }); 
} 

function deleteNotes(elm) { 
    var btn = jQuery(elm); 
    btn.attr('disabled', 'disabled'); 
    bulkCreditOption('', 'true', function(changeSet) { 
     var i = 0; 
     while (i < changeSet.length) { 
      var selector = "input[type=checkbox][value=" + changeSet[i] + "].check:checked"; 
      var row = jQuery(selector).parent().parent(); 
      var cell = row.find("td:nth-child(2)"); 

      cell.html(cell.text()); 
      flash(row, '60, 130, 200', 500); 
      i++; 
     } 
     btn.removeAttr('disabled'); 
    }); 
} 

function bulkCreditOption(createNotes, deleteNotes, callback) { 
    var path = "/BulkCredit"; 
    var data = ""; 
    var checked = jQuery("input[type=checkbox].check:checked"); 
    checked.each(function(chk) { 
     data += "&ids=" + urlencode(jQuery(this).val()); 

    }); 
    jQuery.ajax({ 
     type: 'POST', 
     url: path, 
     dataType: 'json', 
     data: "createNotes=" + urlencode(createNotes) + data + "&deleteNotes=" + urlencode(deleteNotes), 
     success: function(msg) { 
      callback(msg); 
     } 
    }); 
} 

initValues(); 

Sys.Application.add_init(function() { 
    Sys.Application.add_navigate(function(sender, e) { 
     var reinstate = e.get_state(); 
     if (typeof (reinstate) != 'undefined' && typeof (reinstate.customerType) != 'undefined') { 
      customerType = reinstate.customerType; 
      selYear = reinstate.selYear; 
      selMonth = reinstate.selMonth; 
      sdir = reinstate.sdir; 
      sort = reinstate.sort; 
      page = reinstate.page; 
      noteId = reinstate.noteId; 
      customerId = reinstate.customerId; 

     } else { 
      initValues(); 
     } 

     if (!customerIdChanged) { 

      jQuery("#customerTypeChanger").val(customerType); 

      jQuery("#customerFilter").val(customerId); 

      jQuery("#monthPicker").empty(); 

      makeMonthPicker(); 

      if (noteId != false && noteId != 'false') { 
       doShowNotes(); 
      } else { 
       jQuery("#notesContent").hide(); 
       jQuery("#tableContent").show(); 
       doAjaxCall(); 
      } 
     } else { 
      //logic to fetch customer specific stuff here, TODO 
      customerIdChanged = false; 
     } 
    }); 
    Sys.Application.set_enableHistory(true); 


    jQuery(document).ready(function() { 
     origColor = jQuery("#dataTable > thead > tr > th").css('backgroundColor'); 

     makeMonthPicker(); 

     jQuery("#customerTypeChanger").val(customerType); 
     jQuery("#customerTypeChanger").change(function() { 
      customerType = jQuery(this).val(); 
      iqSetHistory(); 
     }); 

     jQuery("#customerFilter").change(function() { 
      customerId = jQuery(this).val(); 
      var tableBody = jQuery("#dataTable > tbody"); 
      tableBody.find("tr").removeClass("selected"); 
      tableBody.find("tr[rel=" + customerId + "]").addClass("selected"); 
      customerIdChanged = true; 
      iqSetHistory(); 
     }); 

     jQuery(".checkAll").click(function() { 
      var elm = jQuery(this); 
      if (elm.is(':checked')) { 
       jQuery(".check").attr('checked', 'checked'); 
      } else { 
       jQuery(".check").removeAttr('checked'); 
      } 
     }); 

     if (!hasDoneCall) { 
      if (noteId == false) { 
       doAjaxCall(); 
      } else { 
       doShowNotes(); 
      } 
     } 

    }); 
}); 

function makeMonthPicker() { 
    var selDate = new Date(); 
    selDate.setFullYear(selYear); 
    selDate.setMonth(selMonth-1); 
    jQuery("#monthPicker").monthPicker(function(year, month) { 
     selYear = year; 
     selMonth = month; 
     iqSetHistory(); 
    }, selDate); 
} 

var origColor; 
var notesPath = "/ShowNotes"; 


function fadeOut(elm) { 
    elm.animate({ backgroundColor: 'rgb(180, 180, 180)' }, 250); 
} 
function fadeIn(elm) { 
    elm.animate({ backgroundColor: origColor }, 250); 
} 

function iqSetHistory() { 
    var state = { 'customerType': customerType, 'selYear': selYear, 'selMonth': selMonth, 'sdir': sdir, 'sort': sort, 'page': page, 'noteId': noteId, 'customerId':customerId }; 

    Sys.Application.addHistoryPoint(state); 
} 
var ajaxPath = "/GetCreditListMonth"; 
function doAjaxCall() { 

    fadeOut(jQuery("#dataTable > thead > tr > th")); 
    jQuery.ajax({ 
     type: "POST", 
     url: ajaxPath, 
     dataType: "json", 
     data: "month=" + selMonth + "&year=" + selYear + "&custType=" + customerType + "&sort=" + sort + "&sdir=" + sdir + "&page=" + page + "&asCsv=false", 
     success: function(msg) { 
      var table = jQuery("#dataTable"); 

      var tableBody = table.find("tbody"); 


      tableBody.empty(); 

      var i = 0; 
      while (i < msg.Rows.length) { 
       var data = msg.Rows[i]; 
       var row = jQuery("<tr rel=\"" + data.CustomerId + "\"></tr>"); 

       if (data.CustomerId == customerId) { 
        row.addClass("selected"); 
       } 

       if (i % 2 == 1) { 
        row.addClass("alternatetablerow"); 
       } 

       var custName = data.CustomerName; 
       if (data.PaymentCreated) { 
        custName = "<a href=\"javascript:showNotes('" + getCreditId(data.CustomerId) + "')\">" + custName + "</a>"; 
       } 
       row.append("<td><input type=\"checkbox\" class=\"check\" name=\"ids\" value=\"" + getCreditId(data.CustomerId) + "\" /></td>"); 
       row.append("<td>" + custName + "</td>"); 
       row.append("<td>" + data.AmountExcludingTaxes + "</td>"); 
       row.append("<td>" + data.BonusAmount + "</td>"); 
       row.append("<td>" + data.Amount + "</td>"); 

       row.appendTo(tableBody); 
       i++; 
      } 


      tableBody.find("input, a").click(function(event){ //Stop clicks from falling through to the table row event 
       event.stopPropagation(); 
       return true; 
      }); 
      tableBody.find("tr").click(function(event){ 
       var row = jQuery(this); 
       if (row.hasClass("selected")) { //Deselect 
        jQuery("#customerFilter").val(0); 
       } else { 
        jQuery("#customerFilter").val(jQuery(this).attr('rel')); 
       } 
       jQuery("#customerFilter").triggerHandler("change"); 
      }); 



      createPager(msg.Pages, jQuery("#pager")); 
      jQuery(".checkAll").triggerHandler('click'); 

      fadeIn(table.find('thead > tr > th')); 
     } 
    }); 
} 

function downloadListAsCsv() { 
    window.location.href = ajaxPath + "?month=" + selMonth + "&year=" + selYear + "&custType=" + customerType + "&sort=" + sort + "&sdir=" + sdir + "&page=0&asCsv=true"; 
} 

function doShowNotes(){ 
    jQuery.ajax({ 
     type: "GET", 
     url: notesPath + "/" + noteId, 
     success: function(msg) { 
      jQuery("#tableContent").hide(); 
      jQuery("#notesContent").html(msg).show(); 

     } 

    }); 
} 

function showNotes(id) { 
    noteId = id; 
    iqSetHistory(); 
} 

function showTable() { 
    noteId = false; 
    iqSetHistory(); 
} 

function getCreditId(custId) { 
    return selYear + "-" + selMonth + "-" + custId; 
} 

function sortDataTable(col) { 
    if (col == sort) { 
     sdir = !sdir; 
    } else { 
     sdir = false; 
    } 

    page = 1 

    sort = col; 
    iqSetHistory(); 
} 

function createPager(totalPages, elm) { 
    elm.empty(); 
    if (totalPages > 1) 
     { 
      var builder = ""; 


      var numDirections = 2; 

      if (page > 1) 
      { 
       if (page - numDirections - 1 > 0) 
       { 
        builder += CreatePageLinkStatic(1, "&laquo;"); 
        builder += " "; 
       } 

       builder += CreatePageLinkStatic(page - 1, "&lt;"); 
       builder += " "; 
      } 

      var n = page - numDirections; 
      while (n < page) 
      { 
       if (n > 0) 
       { 
        builder += CreatePageLinkStatic(n, n); 
        builder += " "; 
       } 
       n++; 
      } 

      builder += page; 
      builder += " "; 

      n = page + 1; 

      while (n <= page + numDirections && n <= totalPages) 
      { 
       builder += CreatePageLinkStatic(n, n); 
       builder +=" "; 
       n++; 
      } 

      if (page < totalPages) 
      { 
       builder += CreatePageLinkStatic(page + 1, "&gt;"); 
       builder += " "; 
       if (page + numDirections < totalPages) 
       { 
        builder += CreatePageLinkStatic(totalPages, "&raquo;"); 
       } 
      } 

      builder; 

      elm.append(builder); 
     } 

} 
function CreatePageLinkStatic(page, str){ 
    return "<a href=\"javascript:pageDataTable(" + page + ")\">" + str + "</a>"; 
} 

function pageDataTable(newPage){ 
    page = newPage; 
    iqSetHistory(); 
} 

Et le balisage:

<div id="tableContent"> 
<select id="customerTypeChanger"> 
    <option selected="selected" value="Publisher">Publisher</option> 
    <option value="Advertiser">Advertiser</option> 
</select> 
<select id="customerFilter"><option value="0">Choose Customer</option><option value="1">Customer 1</option><option value="1">Customer 2</option>...</select> 
<div id="monthPicker"></div> 
<div><a href="javascript:downloadListAsCsv()">DownloadAsCSV</a></div> 
    <table id="dataTable" class="grid"> 
     <thead> 
      <tr> 
       <th style="text-align: left"><input type="checkbox" name="toggleCheckBox" class="checkAll" value="dummy" /></th> 
       <th><a href="javascript:sortDataTable('CustomerName')">Customer name</a></th> 
       <th><a href="javascript:sortDataTable('AmountExcludingTaxes')">Amount</th> 
       <th><a href="javascript:sortDataTable('BonusAmount')">Bonus amount</a></th> 
       <th><a href="javascript:sortDataTable('Amount')">Amount including VAT</a></th> 
      </tr> 
     </thead> 
     <tbody></tbody> 
    </table> 
    <div class="pagination" id="pager"></div> 
    <div>With the selected rows</div> 
<input id="createNotes" type="button" value="Create notes" onclick="javascript:createNotes(this)" /> <input id="deleteNotes" value="Delete notes" type="submit" onclick="javascript:deleteNotes(this)" /> 

</div> 
<div id="notesContent"></div> 

Si nécessaire aussi bien, voici le code que je l'ai fait pour le monthpicker (il est une chose datepicker très basique qui vous permet juste de feuilleter dans les deux sens entre les mois et donne une sortie comme

< Avril 2009mai 2009Juin 2009>

(où est des liens cliquables gras vous emmenant voir juste cette période de mois, et en italique est le déjà sélectionné une, de toute évidence le balisage HTML réel est différent)

Il utilise le datepicker de jQuery UI pour obtenir les noms localisés des mois

(function($) { 


    var selDate; 

    $.fn.monthPicker = function(callback, selectedDate) { 

     selDate = selectedDate; 

     var elm = this; 

     this.html("<span class=\"prevMonthButton\"><a href=\"\">&lt;</a></span><span class=\"prevMonth\"><a href=\"\"></a></span><span class=\"curMonth\"></span><span class=\"nextMonth\"><a href=\"\"></a></span><span class=\"nextMonthButton\"><a href=\"\">&gt;</a></span>"); 

     populateDates(this); 

     var prevMonthFunc = function() { 

      var month = selDate.getMonth() - 1; 
      if (month < 0) { 
       month = 11; 
       selDate.setFullYear(selDate.getFullYear() - 1); 
      } 
      selDate.setMonth(month); 

      populateDates(elm); 
      callback(selDate.getFullYear(), selDate.getMonth() + 1); 
      return false; 
     } 
     var nextMonthFunc = function() { 
      var month = selDate.getMonth() + 1; 
      if (month > 11) { 
       month = 0; 
       selDate.setFullYear(selDate.getFullYear() + 1); 
      } 
      selDate.setMonth(month); 

      populateDates(elm); 
      callback(selDate.getFullYear(), selDate.getMonth() + 1); 
      return false; 
     }; 

     this.find(".prevMonth > a").click(prevMonthFunc); 
     this.find(".prevMonthButton > a").click(prevMonthFunc); 
     this.find(".nextMonth > a").click(nextMonthFunc); 
     this.find(".nextMonthButton > a").click(nextMonthFunc); 
    } 

    function populateDates(elm) { 

     var months = jQuery.datepicker._defaults.monthNames; 
     var selYear = selDate.getFullYear(); 
     var selMonth = selDate.getMonth(); 


     elm.find(".curMonth").text(months[selMonth] + " " + selYear); 

     var prevMonth = selMonth - 1; 
     var prevYear = selYear; 
     if (prevMonth < 0) { 
      prevMonth = 11; 
      prevYear = prevYear - 1; 
     } 

     elm.find(".prevMonth > a").text(months[prevMonth] + " " + prevYear); 

     var nextMonth = selMonth + 1; 
     var nextYear = selYear; 
     if (nextMonth > 11) { 
      nextMonth = 0; 
      nextYear = nextYear + 1; 
     } 

     elm.find(".nextMonth > a").text(months[nextMonth] + " " + nextYear); 
    } 
})(jQuery); 

Je sais que la plupart de ce code JavaScript Sucks - mais pour la partie principale il semble faire le travail tout à fait bien, mais comme je l'ai dit cliquer sur une ligne pour sélectionner puis cliquez dessus pour désélectionner boom, double appel à add_navigate ce qui entraîne un appel supplémentaire à mon service JSON et un scintillement visuel côté client - et je n'arrive pas à comprendre pourquoi cela arrive (et encore plus étrange, pourquoi ça arrive quand il est désélectionné et pas sur un sélectionné aussi).

Répondre

5

Je voudrais essayer de faire

.unbind('click').click(function() 

au lieu de

.click(function() 

Juste pour événements sûr clic ne sont pas liés se deux fois.

0

Oh désolé j'avais complètement oublié cette question.

J'ai restructuré un peu de mon code un peu après cela et le problème était alors disparu. Je ne suis pas vraiment sûr de ce qui a causé ça, vu que cela m'a toujours surpris de voir que je n'ai jamais eu, semble-t-il, que la moitié du temps.

Anyhoow, le problème est résolu, seulement je ne sais pas comment ou pourquoi, ce qui me dérange toujours légèrement, mais d'autre part - cela fonctionne.

1

Je pense que le problème est avec la double liaison des événements au même élément inconsciemment.

Cela est généralement le scénario,

(function($){ 
    var MyDocument = new Object({ 
     prepareBody : function(){ 

      //addClick Event 
      $('div#updatedElement').click(MyDocument.ajaxCall()); 

      //adjusting the height of an updatedElement to an-otherElement 
      $('div#updatedElement').css('height', $('div#otherElement').height()); 
     }, 
     ajaxCall : function(){ 

      //do your ajax Call 
      $.getJSON('index.php',{param:1, param:2},function(response){ 

       //do something with your response 
       $('div#updatedElement').html(response) 

       //say if after your call you decide to update the body again 
       MyDocument.prepareBody(); 

       //what that does is you will double bind click to the updateElement div. 
       //The next time that it is click, the AjaxCall function will run twice 
       //The next time it is clicked the MyDocument.ajaxCall function will be run four times 

       //8 - 16 - 32 and by now, firefox would have crashed! 

      },'json'); 
     } 
    }); 
    $(document).ready(function(){ 
     MyDocument.prepareBody() 
    }); 
})(jQuery); 

Alors comme conseillé par KClough, UNBIND des événements avant de les lier afin qu'ils fonctionnent qu'une seule fois! Sad jQuery ne les écrase pas comme les autres frameworks!

Espérons que cela aide quelqu'un d'autre

Questions connexes