Debugging Ajax’ed JavaScript and jQuery val() calls

I develop a web application which displays data in a read-only form, and loads the edit form upon pressing a button

$(function () {
  $(".btnEdit").click(function () {
    $.ajax({
      url: '@Url.Action("Form", new { id = Model.ID })',
      type: 'post'
    });
  });
});

Now I needed to debug the JavaScript code loaded by the call to $.ajax(), but Chrome does not seem to display the loaded response in its tree of Sources.

An answer on SO provided me with the solution: Simply add the line

//# sourceURL=@Url.Action("Form", new { id = Model.ID })

inside a <script> block in the AJAX-loaded HTML. This will add the requested URL under the (no domain) node

chrome ajax js

Now that I have access to the source file, I needed to find all invocations of the jQuery val() function, since I was tracking down a wrong value in an <input>.

Again SO provided a solution, which I added to my code

(function($){
  var originalVal = $.fn.val;
  $.fn.val = function(){
    var selectorPath = $(this).parents().map(function () {return this.tagName;}).get().reverse().join("->");
    console.log("val( #" + $(this).attr("id") + " " + selectorPath + " , " + JSON.stringify(arguments) + ")");
    var result =originalVal.apply(this,arguments);
    return result;
  };
})(jQuery);

Now that the calls to val() were logged to the Console, it was easy to find where the wrong value was set.

A jQuery Alternative for ACT CalendarExtender

As I extend an ASP.Net application with MVC functionality, I also need to replace some of the components that the AjaxControlToolkit provides. Case in point, the CalendarExtender.

The CE is declared in markup like this:

<ajax:CalendarExtender id=”ce” runat=”server” TargetControlID=”edDate” Format=”dd.MM.yyyy”>

edDate being the ASP.Net TextBox control containing the selected date.

The simplest replacement is, of course, the Datepicker widget found in the jQuery UI library.

Integration the Datepicker is a bit more effort as compared to the other controls in this series, but not much.

First we need to reference the required .js and .css files of jQuery and jQuery UI:

<script type="text/javascript" src="@Url.Content("~/script/jquery-1.8.3.js")"></script>
<script type="text/javascript" src="@Url.Content("~/script/jquery-ui-1.9.2.custom.min.js")"></script>
<link href="@Url.Content("~/css/jquery-ui-1.9.2.custom.css")" rel="stylesheet" type="text/css" />

Depending on the options you used for the CalendarExtender, the initialization for the Datepicker may look like this:

$("#edDate").datepicker({
  changeMonth: true,
  changeYear: true,
  showButtonPanel: true
});
$("#edDate").datepicker("option", "dateFormat", "dd.mm.yy");
$("#edDate").datepicker("option", $.datepicker.regional["de"]);

For more options, see the sample page and press View Source, or browse the documentation.

Note that while the CE requires “yyyy” for 4-digit years, the Datepicker interprets “yy” as 4-digit years, and will insert the year twice if you use “yyyy”.

I’m not sure whether the localization files are part of the default jQuery UI downloads – if not, you can find the localized strings for Datepicker in the GitHub repository.

Lastly, the CE provides a “Today” button which sets the current date and closes the calendar, whereas Datepicker just navigates to the current date in the calendar control, but does not select it.

To change the Datepicker behavior, I use the code found in an answers on SO:

$.datepicker._gotoToday = function (id) {
    var target = jQuery(id);
    var inst = this._getInst(target[0]);
    if (this._get(inst, 'gotoCurrent') && inst.currentDay) {
        inst.selectedDay = inst.currentDay;
        inst.drawMonth = inst.selectedMonth = inst.currentMonth;
        inst.drawYear = inst.selectedYear = inst.currentYear;
    }
    else {
        var date = new Date();
        inst.selectedDay = date.getDate();
        inst.drawMonth = inst.selectedMonth = date.getMonth();
        inst.drawYear = inst.selectedYear = date.getFullYear();
        this._setDateDatepicker(target, date);
        this._selectDate(id, this._getDateDatepicker(target));
    }
    this._notifyChange(inst);
    this._adjustDate(target);
};

See also here for further discussion.

Finally, if the jQuery UI skins do not fit in the design of your application, open the developer tools of your browser (F12) and find out which jQuery UI CSS classes define the look of the Datepicker control. In my case, I added a bit of CSS to adjust the control:

.ui-datepicker table.ui-datepicker-calendar {
    font-size: 11px;    
}
.ui-datepicker table.ui-datepicker-calendar td
{
    width: 17px;
}
.ui-datepicker table.ui-datepicker-calendar td a 
{
    padding: 0px;
}
.ui-datepicker .ui-datepicker-title select {
    font-size: 0.9em;
    margin: 1px 0;
}
.ui-widget button {
    font-size: 0.8em;
}

Note that the overriding style definition selectors must be more specific than the original jQuery UI declarations.

Aaaand we are done! 😉

A jQuery Alternative for ACT HoverMenuExtender

As I extend an ASP.Net application with MVC functionality, I also need to replace some of the components that the AjaxControlToolkit provides. Case in point, the HoverMenuExtender.

The HME is declared in markup like this:

<ajax:HoverMenuExtender ID=”hme” runat=”server” TargetControlID=”tdTable” PopupControlID=”paTable” PopupPosition=”Bottom” OffsetX=”0″ OffsetY=”2″>
</ajax:HoverMenuExtender>

tdTable being, in my case, a TD table cell in a menu bar, and paTable the submenu that is meant to dropdown when hovering over the table cell.

As replacement, I found the Simple jQuery Dropdowns library on CSS-Tricks, and the sample looked similar to the solution I wanted to replace.

Adding the Simple Dropdowns was straight forward, as it consists only of a couple of Javascript lines and some CSS classes. Integration was easy and quickly done.

But then…

As I mentioned, the menu bar was defined as single-row HTML table, and the cells are fixed-width for all drop-down menus, except the one displaying a record identifier – this cell uses a table’s (built-in!) capability to expand so that the table extends over the defined width, no matter how many other fixed-width cells there are and how wide the browser window is.

So I added some display:table* styles like this

<ul class="dropdown" style="width: 99%; display:table">
    <li style="display:table-cell; ">
        <a href="#">Menu 1</a>
        <ul class="sub_menu">
            <li><a href="#">Command 1</a></li>
            <li><a href="#">Command 2</a></li>
        </ul>
    </li>
    <li style="display:table-cell; width: auto" >
        <a href="#">ID</a>
        <ul class="sub_menu">
            <li><a href="#">Command 1</a></li>
            <li><a href="#">Command 2</a></li>
        </ul>
    </li>
      <li style="display:table-cell; ">
        <a href="#">Menu 2</a>
        <ul class="sub_menu">
            <li><a href="#">Command 1</a></li>
            <li><a href="#">Command 2</a></li>
        </ul>
    </li>
    <li style="display:table-cell">&nbsp;</li>
</ul>

Chrome and IE9 displayed the menu as expected, but Firefox insisted on displaying the dropdown left-aligned below to <ul> table, no matter how I changed the individual styles and classes.

Fortunately I found this answer on SO that illustrated a solution that works for Firefox (and the other browsers!): add a <div style=”position:relative”> inside the <td>.

So now my menu bar looks like this:

<ul class="dropdown" style="width: 99%; display:table">
    <li style="display:table-cell; " >
        <div style="position: relative">
            <a href="#">Menu 1</a>
            <ul class="sub_menu">
                <li><a href="#">Command 1</a></li>
                <li><a href="#">Command 2</a></li>
            </ul>
        </div>
    </li>
    <li style="display:table-cell; width: auto" >
        <div style="position: relative">
            <a href="#">ID</a>
            <ul class="sub_menu">
                <li><a href="#">Command 1</a></li>
                <li><a href="#">Command 2</a></li>
            </ul>
        </div>
    </li>
    <li style="display:table-cell; " >
        <div style="position: relative">
            <a href="#">Menu 2</a>
            <ul class="sub_menu">
                <li><a href="#">Command 1</a></li>
                <li><a href="#">Command 2</a></li>
            </ul>
        </div>
    </li>
    <li style="display:table-cell" class="command">&nbsp;</li>
</ul>

A jQuery Alternative for ACT CollapsiblePanelExtender

As I extend an ASP.Net application with MVC functionality, I also need to replace some of the components that the AjaxControlToolkit provides. Case in point, the CollapsiblePanelExtender.

The CPE is declared in markup like this:

<ajax:CollapsiblePanelExtender ID=”cpe” runat=”server” CollapseControlID=”paHeader” ExpandControlID=”paHeader” AutoCollapse=”false” AutoExpand=”false” Collapsed=”true” TargetControlID=”paDetail” ExpandedImage=”~/images/collapse_blue.jpg” CollapsedImage=”~/images/expand_blue.jpg” SuppressPostBack=”true” ImageControlID=”imgHeader”>
</ajax:CollapsiblePanelExtender>

paHeader and paDetail are both asp:Panels – clicking on the header toggles the display of the detail panel. The image control is toggled accordingly.

My goal was to create a JavaScript function which uses parameters similar to the markup, omitting those that do not matter in my application.

In MVC, of course, the equivalent of an asp:Panel is a simple HTML <div>.

I started out with a function declaration like this (please forgive non-standard casing):

function CollapsiblePanelExtender(ID, CollapseControlID,
    TargetControlID, ImageControlID) { }

but then noticed that I needed to distinguish between initial states expanded and collapsed, as well as an additional callback after expanding/collapsing.

var CollapsiblePanelExtenders = {};
function CollapsiblePanelExtender(ID, CollapseControlID, 
    TargetControlID, ImageControlID, expanded, fn) {
  var ExpandedImage = '@Url.Content("~/images/collapse_blue.jpg")';
  var CollapsedImage = '@Url.Content("~/images/expand_blue.jpg")';

CollapsiblePanelExtenders is our array of all CPEs on the page. Parameters expanded and fn are optional.

First, we register the CPE in the array, and initialize the image and the detail’s overview setting:

  CollapsiblePanelExtenders[ID] = { collapsed: !(expanded || false) };
  $("#" + ImageControlID).attr("src", (expanded || false) ? ExpandedImage : CollapsedImage);
  $("#" + TargetControlID).attr("overflow", "hidden");

Finally, we add a click handler on the header panel:

  $("#" + CollapseControlID).click(function (pa) {
    if (CollapsiblePanelExtenders[ID].collapsed) {
      $("#" + TargetControlID).css("height", "");
      $("#" + ImageControlID).attr("src", ExpandedImage);
      CollapsiblePanelExtenders[ID].collapsed = false;
    } else {
      $("#" + TargetControlID).css("height", "0px");
      $("#" + ImageControlID).attr("src", CollapsedImage);
      CollapsiblePanelExtenders[ID].collapsed = true;
    }
    if (fn) {
      fn(CollapsiblePanelExtenders[ID].collapsed);
    }
  });
};

The click handler adjusts the detail’s height and sets the expand/collapse image. If a callback function is defined, the function is executed with the collapsed status as parameter.

I was surprised that it only takes a couple of JavaScript lines to implement an ACT control. Note, however, that the presented solution does not invoke any animations – have a look at jQuery Effects, if you need them…

Workaround for “Could not load control AjaxControlToolkit”

I wanted to update an ASP.Net application I maintain to the latest version of Ajax Control Toolkit, only to get an exception raised by the toolkit

Could not load control AjaxControlToolkit.[ControlName]. The script reference(s) of this control was not loaded correctly. If AjaxControlToolkit.config is used, probably this control is not registered properly.

See also posts on SO such as here and here.

The internets suggest not to use the latest version, as not versions are created equal, and point to the January 2013 edition as reliable.

Fixing the AjaxControlToolkit ColorPicker

The Ajax Control Toolkit ColorPicker adds a dynamic color palette to an asp:TextBox and sets the textbox’s value to the hex value of the selected color.

There are two problems with the control (.Net 3.5 build 51116 of Nov 2011):

  • First, the OnClientColorSelectionChanged JS event is not fired if the user edits the textbox manually.

To fix this problem, you need to add two properties to the asp:TextBox declaration

onchange="javascript: colorTextChanged(this);" 
onkeyup="javascript: colorTextChanged(this);"

and implement a JavaScript function

function colorTextChanged(sender) {
    // sender is the textbox, sender.value is the edited color
}
  • Second, the color palette does not show if you set the SelectedColor property from code

The bug report I found on this topic is quite dated (Sep 2009), so nobody seems to care. The solution provided certainly worked for older versions of ACT, but needs to be adjusted (AjaxControlToolkit vs. Sys.Extended.UI) to look like this:

Sys.Application.add_init(function() {
    // Store the color validation Regex in a "static" object off of   
    // Sys.Extended.UI.ColorPickerBehavior.  If this _colorRegex object hasn't been   
    // created yet, initialize it for the first time.   
    if (!Sys.Extended.UI.ColorPickerBehavior._colorRegex) {
        Sys.Extended.UI.ColorPickerBehavior._colorRegex = 
            new RegExp('^[A-Fa-f0-9]{6}$');
    }
});  

 

Dual Listboxes in ASP.Net using Ajax Extenders

I wrote an article about a Javascript Dual Listbox some time ago. Recently I needed this functionality again, searched, and found the Dual List Box Extender.

What’s the difference?

The MetaBuilders control library generates the list boxes and buttons using ASP.Net controls, and adds selection logic in Javascript.

The (support) downside is that the last update of the project is from September 2007, and the latest answer to a “discussion” entry is from July 2009, a year ago.

The Dual List Box Extender is just that: it uses the MS Ajax Extender mechanism (MSDN library, magazine, blog) to add dual listbox functionality to a single listbox.

What the extender’s Javascript does is to hide the original listbox, dynamically create two new listboxes and necessary buttons, and copy the listbox contents to the newly created listboxes according to the items’ Selected flag.

Moving the item between the two lists automatically sets the Selected flag accordingly, thus supporting compatible behavior in postbacks.

The layout of the dual listbox controls can be freely defined using CSS.

JavaScript pageLoad() for MasterPage and ContentPage

I was experimenting with the DragPanelExtender recently, and almost immediately ran into the question of how to position it upon opening.

There are a lot of articles to be found on this question, and they always deal with it by means of a JavaScript function called pageLoad(), which is called by the ASP.Net Ajax framework sometime while the page is loading.

Now my application already has a pageLoad() function in the MasterPage, so adding a function of the same name in the ContentPage just did not work, as it would not be called by the framework.

My knowledge of JavaScript is not too profound, and I found that a function definition in JavaScript is actually a property of the window object. The hint was a line in the MicrosoftAjax.js file:

if (window.pageLoad) window.pageLoad(this,a);

meaning: if there is a defined function named pageLoad(), it is a property of the window object, and the value of the property is not null (== not false), and I can call it.

The solution was clear to me soon: if I need a pageLoad() method in the ContentPage, I need to name it differently and check for its definition in the MasterPage.

The pageLoad() in the MasterPage looks like this:

function pageLoad(sender, args)
{
    ...
    // ... do my MasterPage JavaScript stuff...
    ...
    if (window.contentPageLoad) {
        window.contentPageLoad(sender, args);
    }
}

and the ContentPage has its equivalent contentPageLoad() function:

function contentPageLoad(sender, args)
{
    // do something
}