Solving SignalR $.connection.HubName is null or undefined

I added support for SignalR to one of the ASP.Net MVC applications I develop, mainly following this Microsoft tutorial.

I was surprised by the fact that you cannot install the SignalR package from the NuGet GUI (in VS 2013 at least), but need to switch to the Package Manager Console and type

install-package Microsoft.AspNet.SignalR

It worked nevertheless.

I also copied the JS initialization code from there

 $(function () { 
    // Declare a proxy to reference the hub. 
    var chat = $.connection.chatHub;
    chat.client.broadcastMessage = function (name, message) { ... };
}

(adjusted, of course, to the hubs defined in my project), only to find that the JavaScript console in Chrome Developer Tools displays the error message

Uncaught TypeError: Cannot read property 'client' of undefined
 at HTMLDocument. (38024:10393)
 at fire (jquery-1.12.4.js:3232)
 at Object.fireWith [as resolveWith] (jquery-1.12.4.js:3362)
 at Function.ready (jquery-1.12.4.js:3582)
 at HTMLDocument.completed (jquery-1.12.4.js:3617)

The error is raised by the line

chatHub.client.broadcastMessage = function (name, message) {

with chatHub being undefined.

I stepped through the JavaScript code and found that the (automagically generated) JavaScript file

< script src="@Url.Content("~/signalr/hubs")" >< /script>

defines the hubs correctly, but somehow the hub definitions are lost when the

$(function() {

is being executed.

It turned out to be a question of JavaScript scopes, and I moved the client callback definition out of the jQuery $(function(){}) directly into the

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.

Adding Dropdown Arrow for Chosen Plugin in Multiselect Mode

I use the jQuery Chosen plugin in a web application, and recently got the request to display a dropdown arrow in multiselect mode.

I found an answer on SO regarding styling of the dropdown arrow, but that covered only singleselect mode. At least a starting point 😉

Within minutes, I had a CSS-only solution for this particular problem:

<style type="text/css">
.chosen-container-multi.chosen-container .chosen-choices::after {
    background: url(@Url.Content("~/Content/chosen-sprite.png")) no-repeat 3px 4px;
    width: 16px;
    height: 100%;
    content: " ";
    position: absolute;
    right: 0;
    background-color: lightgray;
}
</style>

(Note that this is an ASP.Net MVC application, and @Url.Content() computes the URL relative to application root.)

Only after I solved this, I found that there is a closed (and unresolved) issue on github from 2011, with comments up to 2016, but no solution to a valid feature request…

jQuery Autocomplete Inconsistencies

I guess I found an inconsistency in jQuery’s autocomplete() widget, which does not cause a selected value to be reset when clearing the input field.

Steps to reproduce (checked with 1.8.3 and 1.11.0):

  • Type the beginning of a string that is provided by the autocomplete source
  • Tab away from the input
  • “autocompletechange” fires, but the value is not valid, so the input field value is reset
  • Type the beginning of string (as above)
  • Select an entry using cursor keys
  • “select” fires (and sets the corresponding key value for the selected text value in a callback function)
  • Double-click the input to select the full text of the input field
  • Delete selected text (backspace or right-click Cut)
  • Neither “autocompletechange” nor “select” fires
  • Tab away from input field
  • Neither “autocompletechange” nor “select” fires

From the console.log:

blur ” 5:81
mouseup ” 5:90
keyup ‘r’ 5:87
keyup ‘re’ 5:87
keyup ‘rei’ 5:87
keyup ‘re’ 5:87
change ‘re’ 5:84
autocompletechange null 5:78
Nothing selected, input was re common.js:114
blur ” 5:81
mouseup ” 5:90
keyup ‘re’ 5:87
keyup ‘re’ 5:87
keyup ‘rei’ 5:87
keyup ‘6749’ [this is the key value!] 5:87
Selected: [some text] (6749) common.js:114
keyup ‘[some text] 5:87
keyup ‘[some text] 5:87
keyup ‘[some text] 5:87
keyup ” 5:87
change ” 5:84
blur ”

The value displayed in single quotes is the .value property of the input field, “rei” being the string that corresponds to the beginning of a valid value in the source.

The only workaround that comes to mind is to use the keyup and mouseup events to check whether the input’s value property is empty, and then reset the key field accordingly.

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…

Googlebot POSTS – using jQuery

After I came up with the idea to log web application hits using jQuery, to my great surprise I found that Googlebot actually performs POSTs implemented as jQuery $.ajax() calls:

2014-01-15 09:46:04 POST /Log - - 66.249.64.45 
  Mozilla/5.0+(compatible;+Googlebot/2.1;++http://www.google.com/bot.html) - 200 0 0 255

Wow!

Searching the Interwebs I found other people who observed this behavior, too:

Most importantly, the links in Wikipedia’s Googlebot article analyze the bot’s behavior in more detail:

The articles are about 2 years old, so the bot may now be even more capable than then.

Of course, the simplest solution to prevent bots from POSTing is to add the logger’s URL to robots.txt:

User-agent: *
Disallow: /Log

 

Web Application Logging using jQuery

If you have an Internet-facing web application, you want to log the hits of your visitors to a database.

Typically, the log table will also include hits by web crawlers, and, depending on the number of users of your site, the crawlers will create far more log entries than your human visitors.

One way to cope with this scenario is to also log the ASP.Net SessionID (i.e. retrieving Request.Cookies[“ASP.NET_SessionId”]), and later delete the log entries with a unique SessionID (robots typically do not reuse cookie data set by a web server).

Rather than cleaning up huge log tables, we should just log the visits that we are interested in. Since a crawler might fetch anything referenced by an HTML page, such as counting pixels, and even request referenced files a long time after fetching the original HTML, I thought about logging via AJAX requests.

In this solution, the master pages contains a logging function, such as

<script type="text/javascript">
    function Log(logData) {
        $.ajax('<%: Url.Action("Logger", "Log") %>', 
            { type: 'POST', data: logData });
    }
</script>

which passes a JavaScript object to an MVC Logger action.

Each view with logging functionality thus include the following script:

<script type="text/javascript">
  $(function () {
    Log({ 
      data: [whatever you want to log],
      referrer: '<%: (Page.Request.UrlReferrer != null) ? 
        this.Request.UrlReferrer.OriginalString : null %>',
      aspnetSessionId: '<%: (Request.Cookies["ASP.NET_SessionId"] != null) ? 
        (object)Request.Cookies["ASP.NET_SessionId"].Value : null%>', 
    });
  });
</script>

This way, logging requires that the browser issues a jQuery POST request, and simple crawlers do not end up in your logging database.

Retrieving the data() Value of Multiple JQuery Objects

I have a web page with a (generated) list of checkboxes, each representing a database record

                <input type="checkbox" data-oid="<%= record.OID %>" />

After checking the desired check boxes, the concatenated values of the data- attribute should be posted to the server.

Since this selector

$("input[type='checkbox']:checked")

would return all the checked checkboxes, I expected the expression

$("input[type='checkbox']:checked").data("oid")

to return all data- values as an array. But NO:

Store arbitrary data associated with the matched elements or return the value at the named data store for the first element in the set of matched elements.

says the jQuery documentation quite unexpectedly.

I need a map(), I thought, and after a couple of tries convinced the JS interpreter to let me have my map():

$("input[type='checkbox']:checked")
    .map(function(i,el) { return $(this).data("oid"); })

Great. This returns something that looks like an integer array, e.g.

[82, 86]

but it is not, since I cannot join() it:

TypeError: Object [object Object] has no method ‘join’

and the Chrome Developer Tool claims it looks more like a stack than an array. Make it so!

$("input[type='checkbox']:checked")
    .map(function() { return $(this).data("oid"); })
    .toArray()

The result not only looks like an array, it really seems to be an array, and

$("input[type='checkbox']:checked")
    .map(function() { return $(this).data("oid"); })
    .toArray()
    .join(",")

finally returns a string of concatenated integer values:

"82,86"