Archives for category: Javascript

So I had this strange issue today when I deployed an application that was designed in such a way that all of its javascript is stored in external .js files, as opposed to embedding it directly in the page.  I made some pretty heavy changes to the external .js files.

Everything worked great in both the local development environment and the staging environment.  But when I deployed to the production environment, the client-side functionality was not working properly.

I deployed and redeployed, but there was no change.  I looked at the javascript file on the production file system, and it reflected the changes.  But when I viewed the source of the javascript file in the web browser, I could see that none of my updates were reflected.

My first thought was to recycle the application pool, but that didn’t work.  Then I attempted to do an IISRESET.  That DIDN’T WORK either!!!

I went in and deleted my temporary internet files TWICE, and finally the updates were reflected.  I realize that javascript files are supposed to be cached, but I needed my changes to show up.

For the rest of the day, my users were complaining that the application was no longer working.  My instructions to delete the temporary internet files seemed to work in most cases, but I didn’t need my users to have to deal with that.

This is not an issue I typically deal with, because I usually embed my Javascript directly in the web page.  This is only an issue with script references to external .js files, and perhaps it is by design, because in most cases you want your external javascript references to be cached and load super fast!

I did some research and discovered that if you append the javascript file with a unique querystring, it will force the browser to retrieve the latest version of the file.  Some folks on the forums recommend appending the script reference with a date time value so that it is always unique, forcing the browser to always retrieve the latest version.

http://scripts/GMConsultantBench.js?version=%=System.DateTime.Now.ToString()%20%

I don’t fully agree with that approach, because for the most part, I want my .js files to cache. But I would like to control when an update occurs.

I recommend versioning your javascript so that for the most part it remains cache, but when you make an update, you can increment the version number to break through the cache just one time.

http://scripts/GMConsultantBench.js?version=1.0

Or, even better, you can set it to a unique GUID that you store in your web.config and just update the version in the web.config when you deploy an update.  Anyway, you get the idea!

Advertisements

I struggled a bit attempting to generate dynamic javascript in an MVC Razor page view.  I kept getting the following error:  Too many characters in character literal.

I did a little research and found the answer.  You will need to include
@using System.Text; at the top of your page view.

To build my javascript, I included the following code block within my <script> tags:

@{
StringBuilder sb = new StringBuilder();
foreach (GetBillingCodes_Result billingCode in Model.BillingCodes)
{
sb.Append(“AddBillingCode(‘” + billingCode.billing_code + “’”);
sb.Append(“, ‘” + billingCode.travel + “’”);
sb.Append(“, ‘” + billingCode.billing_code_type + “’”);
sb.Append(“, ‘” + billingCode.billing_rate + “’”);
sb.Append(“, ‘” + billingCode.taxable + “’”);
sb.Append(“, ‘” + billingCode.discount + “’”);
sb.Append(“, ‘” + billingCode.C100_club + “’”);
sb.Append(“, ‘” + billingCode.active + “’”);
sb.Append(“);”);
}
@MvcHtmlString.Create(sb.ToString());
}

In this new cyberworld saturated with abundant mouse clicks, it has become more necessary to try to get your user’s attention before allowing them to perform an action that may result in a critical error.  One of these actions is leaving the application without first saving changes.  In some cases, a request to leave a page without first saving changes may be on purposes, but in many cases it may have been the result of an accidental mouse click.

Browsers have provided a built-in prompt that warns users before leaving certain pages.  The user then has the option of leaving the page or staying on the page.  This built-in prompt is not at all flexible, by design, to prevent rogue sites from actually preventing users from leaving their web sites.

The following is an example of how you might implement this prompt using the window.onbeforeunload event.

STEP 1:  Mechanism to determine if there are unsaved changes
Because there is no reason to warn your users from leaving if they have not made any changes, the first thing you want to do is create a hidden field or client-side variable that specifies whether your page contains unsaved changes.

In this example, I have created a hidden input to store my change status:
@Html.Hidden(“UnsavedChanges”, “0”)

I then created a function to update the change status:
function HandleFieldChange()
{
$(“#UnsavedChanges”).val(“1”);
}

Finally, I used a little jQuery to easily add the field change event:
$(“input”).change(function(){
HandleFieldChange();
});
$(“select”).change(function(){
HandleFieldChange();
});
$(“textarea”).change(function(){
HandleFieldChange();
});
$(“input:checkbox”).click(function(){
HandleFieldChange();
});
$(“input:radio”).click(function(){
HandleFieldChange();
});

STEP 2:  Use the browser’s built-in warning prompt before leaving a page.

Again, this cannot be customized.  If it could, that would open the door for this functionality to be used maliciously.  You don’t have any control over how the warning looks, but you can insert a custom message into the prompt.

This function checks to see if there are unsaved changes.  If there are, it calls the browser’s built-in warning and inserts a custom message.

window.onbeforeunload = function() {
if ($(“#UnsavedChanges”).val() == “1”)
{
var _message = “You currently have unsaved changes!!!\n\nAre you sure you want to exit without saving.\n\nChoose ‘Leave this page’ to exit without saving changes.\nChoose ‘Stay on this page’ to return to the billing profile.”;
return _message;
}
}

RESULT

It’s not as pretty as I would like, but it works!

ieprompt.png

Even though most browsers are configured to suppress Javascript errors, you want to ensure that you handle these errors effectively.

The best way to do this is to put all of your Javascript code within try/catch statements:

function HandleBillingCodeFieldChange(idx)
    {
        try
        {
            $(“#bc_changed_” + idx.toString()).val(“1”);
        }
        catch(Exception){}
    }

In addition to that, it is always a good idea to have an overall Javascript error suppressor.  You probably only want to enable this in production, however, because it makes it more difficult to detect and fix Javascript errors in development:

function silentErrorHandler(){return true;}
window.onerror=silentErrorHandler;

I recently ran into an issue in which I was loading a SharePoint view into an iFrame in my application.  It was loading just fine, but it was throwing out a random JS conflict error, even though I had taken the above precautions.

I even tried adding an onerror event to my iFrame in an attempt to suppress the error, but it didn’t work.

I was, however, able to find the workaround for this in IE by adding a security=”restricted” attribute to my iframe:

http://”MySource.aspx”

I love using jquery.tablesorter to add quick sorting to my reports. It is fast and super easy and doesn’t require a postback or AJAX calls. But every once in a while, I run into a limitation. For example, it seems to be sorting my currency values as if they were text.
For example:
Instead of sorting them like this –
$ 50.00
$ 75.00
$ 300.00
$ 1,000.00
It is sorting them like this –
$ 1,000.00
$ 300.00
$ 50.00
$ 75.00
To resolve this issue, you can add a custom parser like so:
$.tablesorter.addParser({
// set a unique id
  id: ‘currency’,
is: function (s) {
    // return false so this parser is not auto detected
    return false;
  },
  format: function (s) {
    // format your data for normalization
return s.replace(‘$’, ”).replace(/,/g, ”);
  },
  // set type, either numeric or text
  type: ‘decimal’
});
$(“#MyReport”).tablesorter({
  // sort on the first column and third column, order asc
  sortList: [[1, 0], [2, 0]],
  // add custom sort (zero-based index) – this will sort the 7th column
  headers: {
    6: {
      sorter: ‘currency’
    } ,
    7: {
      sorter: ‘currency’
    }
  }
});
<table id=”MyReport” class=”tablesorter”>
. . .
</table>
I love using jQuery tablesorter for quick sorting of my report tables. But sometimes it is not able to sort some of my cells properly. For instance, when I use KPI images to reflect Green, Yellow, and Red states. But it is super easy to extend the tablesorter logic to add custom sort functionality by using the addParser method.
In this example, we will convert our KPI images of Green, Yellow and Red to the following values: 1, 2, 3. That way they will sort Green, Yellow, Red for ascending and Red, Yellow, Green for descending sort.
This defines the custom parser to handle KPI data and assigns it an ID of kpi.
$.tablesorter.addParser({
  // set a unique id
  id: ‘kpi’,
  is: function (s) {
    // return false so this parser is not auto detected
    return false;
    },
  format: function (s) {
    // format your data for normalization
    if (s.toLowerCase().indexOf(“green”) != -1) {
      return 1;
    }
    else if (s.toLowerCase().indexOf(“yellow”) != -1) {
      return 2;
    }
    else if (s.toLowerCase().indexOf(“red”) != -1) {
      return 3;
    }
    else {
      return -1;
    }
  },
  // set type, either numeric or text
  type: ‘numeric’
});
Once the custom parser has been defined, we can tell the tablesorter which column we want to use this logic on. My report table has an id of UnforecastedRevenueSummary.  Also, I have to add a text extraction item here.  Otherwise, the tablesorter tends to ignore markup and would not be able to sort on what is contained in an image.
$(“#UnforecastedRevenueSummary”).tablesorter({
  // text extraction to handle image, otherwise markup tends to be ignored
   textExtraction:function(s){
var $el = $(s),
$img = $e1.find(‘img’);
return $img.length ? $img[0].src : $e1.text();
},
 
  // here i am assigning the default sort
  sortList: [[1, 0], [2, 0], [3, 0]],
  // here i am stating that the very first column will use the custom kpi sort (columns contain a zero index)
  headers: {
    0: {
      sorter: ‘kpi’
    }
  }
});
<tableid=”UnforecastedRevenueSummary”class=”report tablesorter”style=”table-layout:fixed;”>
. . .
</table>

The following is a very simple example of using jQuery to remove HTML Tags from your content:

<!DOCTYPEhtmlPUBLIC”-//W3C//DTD XHTML 1.0 Strict//EN””http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”&gt;

<html>

<head>

<title>Strip HTML</title>

$(document).ready(function () {

var htmlString = $(“#textbox1”).val();

/* replace escaped brackets with real ones */

var htmlString2 = htmlString.replace(/&(lt|gt);/g, function (strMatch, p1) {

return (p1 == “lt”) ? “”;

});

$(“#textbox2”).val(htmlString2);

/* strip text */

var htmlString3 = htmlString2.replace(/]+(>|$)/g, “”);

$(“#textbox3”).val(htmlString3);

});

</head>

<body>

    <inputtype=”text”id=”textbox1″name=”textbox1″style=”width:350px;height:40px;”value=”<br><strong>This is a test</strong><p>”/><br/>

    <inputtype=”text”id=”textbox2″name=”textbox2″style=”width:350px;height:40px;”value=””/><br/>

    <inputtype=”text”id=”textbox3″name=”textbox3″style=”width:350px;height:40px;”value=””/>

</body>

</html>

And here is the result:

I recently ran into a puzzling issue with one of my MVC applications. I had a report link with querystring parameters that I was passing from the controller to the view. This link was being consumed by a client side procedure that was opening a new window and passing in the link to display the report.

function ViewReport() {

var reportLink = ‘@ViewData[“ReportLink”]‘;

window.location = reportLink;

}

Example Report Link: http://myreport.mycompany.com/default.aspx?report_id=12345&report_type=2

This worked great in Internet Explorer, but Chrome was replacing the querystring “&” character with “&amp;”, which was breaking the report view.

Report Link in Chrome: http://myreport.mycompany.com/default.aspx?report_id=12345&amp;report_type=2

After doing some research, I was able to resolve the issue using the Html.Raw html helper as follows:

function ViewReport() {

var reportLink = ‘@Html.Raw(@ViewData[“ReportLink”].ToString())‘;

window.location = reportLink;

}