by Njål

String replaceAll in Javascript

imageIn Javascript the string.replace(“target”, ”replacement”) function only replaces the first occurrence of the target…

Here’s how you can add a replaceAll() metod to all Strings (similar to Extension Methods in C#).

String.prototype.replaceAll = function (orig, replacement) {
    return this.split(orig).join(replacement);
}

 

You can then write (anywhere in your code):

"a a b c".replaceAll("a","X"); //==> "X X b c"
by Andreas

JQuery / KnockoutJS – define default button within a DIV

We’ve previously covered several aspects of using Default buttons in ASP.NET and Silverlight (the latter is in Norwegian, but should be semi-readable through an automagically translated version – sponsored by Google Translate).

This time I thought I’d show you how I did this without the need of server side controls. Why? First of all, adding server side controls adds unnecessary overhead by increasing the view state. Second, in my complex client side user interface I’ve stuck to clean HTML, javascript / jQuery and KnockoutJS. I didn’t want to clog it up, and since the solution was as simple as it turned out to be I am glad I stuck to my guns.

I had a look at what ASP.NET generates when a default button is defined for a Panel control. As you may or may not know, the server side Panel control is rendered as a DIV when the page is returned to the client browser. And as expected, Javascript is used to invoke the defined default button click event so I thought I’d just steal this javascript function and call it myself from within my pure HTML DIV element. Not surprisingly, this worked and why shouldn’t it? I just did the exact same thing as the .NET framework does, and there’s not much voodoo or black magic in that.

So, I wanted to invoke the click event for a “Save Changes” button displayed within a Fancybox dialog, simply by clicking ENTER. First, I added the javascript function that I stole from the .NET server control rendering engine (I am pretty sure that’s not what it’s called):

function WebForm_FireDefaultButton(event, target) {
    if (event.keyCode == 13) {
        // this variable has to be added
        var __nonMSDOMBrowser =
            (window.navigator.appName.toLowerCase().indexOf('explorer') == -1);

        var src = event.srcElement || event.target;
        if (!src || (src.tagName.toLowerCase() != "textarea")) {
            var defaultButton;
            if (__nonMSDOMBrowser) {
                defaultButton = document.getElementById(target);
            }
            else {
                defaultButton = document.all[target];
            }
            if (defaultButton && typeof (defaultButton.click) != "undefined") {
                defaultButton.click();
                event.cancelBubble = true;
                if (event.stopPropagation) event.stopPropagation();
                return false;
            }
        }
    }
    return true;
}

Note that I had to add the __nonMSDOMBrowser variable. This is usually added as a global variable on the page, but I saw no reason to do so because none of my other functions care about it.

Then, within my dialog box DIV element I make sure the onkeypress calls the new function with the ID of the button as a parameter:

<div id="addContactDialogBody" class="modal-body" onkeypress="javascript:return WebForm_FireDefaultButton(event, 'btnSaveContact')">
      <!-- This is my user input form, containing a couple of input text fields -->
</div>
<div id="addContactDialogFooter" class="modal-footer">
    <!-- dont be confused, the data-bind attribute is KnockoutJS related and has nothing to do with this example -->
    <input id="btnSaveContact" type="button" value="Save" data-bind="click: addNewContact, visible: true" />
    <input id="btnCancelContact" type="button" value="Cancel" onclick="javascript: $.fancybox.close();" />
</div>

 

When the focus is on an element within the first DIV (removed for clarty in the example code), pressing the enter Key will invoke the click event (or in my case, a KnockoutJS click binding) for the button with id btnSaveContact.

by Andreas

KnockoutJS – how to force binding refresh / re-evaluation

Observable bindings in KnockoutJS get re-evaluated when the focus is changed in a form, for instance when you click a button or use TAB to move from one input field to the next. When KnockoutJS is used in combination with an ASP.NET panel control where a DefaultButton is defined, re-evaluation of observable bindings is not performed when the user clicks ENTER to trigger the button click event. As a result of this, a value entered in an input field that is bound to the view model will not be registered if the user doesn’t navigate (move focus) away from the field first. 

A work-around for this is to use jQuery (or pure javascript) and programmatically set the focus to a different control, for instance the button itself when the event is fired.

self.addNewTimeRegEntry = function () {
    // set focus to button control to force re-evaluation
    $("#<%= btnAddRegEntry.ClientID %>").focus();

    ... // continue as normal

The button click event is bound to the addNewTimeRegEntry function above (through the KnockoutJS framework), and by moving the focus all variables will be refreshed before further processing is done.

by Andreas

Disable drag and drop in elFinder 2.x

The drag and drop functionality in elFinder 2.x can at be a bit of a problem. You might experience a bit of a lag, and sometimes there is a delay in the button-up event which causes you to drag folders and files around unintentionally.

Currently there is no property allowing drag and drop to be disabled, so you’ll have to make some minor changes in the .js files. My original solution to this was commenting out some delegates in tree.js and cwd.js but I will be the first to admit that this was a dirty hack with possible side effects – not pretty at all!

So here’s the real solution:

If you want to disable draggable animation and functionality, locate the Draggable options in elFinder.js (around line 465). Remove the appendTo parameter from ‘body’ to ‘’ and draggable plugin will be disabled completely.

If you want to keep the animated dragging functionality, but prevent the user from actually doing any move operations, locate the Droppable options in elFinder.js (around line 502), and change the accept parameter. Leave the types of droppable you want to support (file, folder or nav dir) and just remove the others. I didn’t want anything to be droppable to I changed it from this:

    /**
     * Base droppable options
     *
     * @type Object
     **/
    this.droppable = {
            tolerance  : 'pointer',
            accept     : '.elfinder-cwd-file-wrapper,.elfinder-navbar-dir,.elfinder-cwd-file',
            hoverClass : this.res('class', 'adroppable'),

to this:

    /**
     * Base droppable options
     *
     * @type Object
     **/
    this.droppable = {
            tolerance  : 'pointer',
            accept: '',
            hoverClass : this.res('class', 'adroppable'),
by Stian

Redirect page with jQuery / JavaScript

There are several different ways to do a clean redirect from one URL to another with some simple jQuery / javascript. I’ll start out here showing what in my opinion is the best way to simulate a HTTP redirect and then show a few other options that you have.

Pure javascript
// similar behavior as an HTTP redirect
window.location.replace("http://blog.degree.no");

This method is in my opinion the best, because the window.location.replace() is redirecting the page without putting the original page in the browser history.

// similar behavior as clicking on a link
window.location.href = "http://blog.degree.no";

This method works fine as well, but it will throw the user in to a back button loop if they would want to go back in history.

jQuery

My impression is that developers sometimes search for the “jQuery way” to do things and maybe you found this blog post searching for “redirect page with jQuery”? While there is a way to do this in jQuery, there is really no reason why you would want to do this as it could not be easier to with normal javascript. But since you searched for it, here it is.

$(location).attr("href",http://blog.degree.no);

 

image

by Andreas

Microsoft JScript error: jQuery.tmpl is too old

If you ever encounter the following error message when using string-based templates (or a component that uses it, like simpleGrid) for KnockoutJS:

Microsoft JScript runtime error: Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later.

you’d expect to just download the latest jQuery.tmpl plugin and get on with your life. This is not the case however, because from KnockoutJS version 2.0.0 jquery.templ are no longer supported. You’ve got two options:

1. Avoid using string-based templates (use native control flow bindings instead)

2. Download this version of jquery.tmpl: jquery-tmpl 1.0.0pre from github. And make sure you add the script reference before the knockout library:

<script src="../Scripts/jquery-ui-1.8.23.js"></script>
<script src="../Scripts/jquery.tmpl.js"></script>
<script src="../Scripts/knockout-2.1.0.js"></script>
<script src="../Scripts/knockout.simpleGrid.js"></script>
by Andreas

Jquery + JSON: IE8/IE9 treats response as downloadable file

We’ve recently been working with the pretty brilliant web based elFinder file manager. This component has a truckload of features, but for a very specific function (uploading files through the web browser) we came across a tricky problem. Upon receiving a JSON response from an HTTP handler (.asxh) Internet Explorer 8 triggered the following warning:

To Help Protect Your Security, Internet Explorer has blocked this website from displaying content with security certificate errors. Click here for options…

image

After opting for “Download file”, subsequent responses just triggered the “Save file” dialog box. The contents of this file was the perfectly fine JSON response, but this was not understood by Internet Explorer 8. In Internet Explorer 9 the warning was supressed and the response seemed to simply be ignored, leaving our jQuery component waiting for its JSON reply in vain. Chrome, Opera and Firefox had no issues, and processed the response as pure JSON.

After a fair bit of research trying to find the bug both server and client side, it turned out that Internet Explorer didn’t like that the Content-Type in the response header was "application/json”. Setting the Content-Type to “text/html” specifically for IE in the .asxh handler before returning the JSON response did the trick. It now works as expected across all browsers.

A simple method checks the browser and updates the content type if applicable (note: this is just an extract):

        private static string SetContentTypeBasedOnBrowserAndCommand(HttpContext context)
        {
            var contentType = "application/json"; // default
            // if browser is IE
            if (DegreeCore.Util.BrowserUtil.IsIE(context.Request.Browser))
            {
                // override response
                 contentType = "text/html"; 
            }

            return contentType;

        }

 

The non-reversible hair loss from occational problems like this has yet again been put on hold – for now.

Tags: ,
by Njål

console.log and Internet Explorer

imageWhen coding/debugging JS I usually work with Chrome and console.log works like a charm. But when you test the same page in Internet Explorer you’ll get a javascript error.

If you then open Developer Tools in IE and hit refresh – the page will work 100% – since IE now has a console object (the developer tools window) it can log to.

The solution is to either remove all console.log statements – or add this at the top of your js file:

 

if console === undefined {
    console = { log : function(){}};
}

 

 

by Joakim

Creating a simple form wizard in ASP.NET MVC 4

UPDATE: As of jquery.validation version 1.8+ hidden input fields are not validated by default, which means that the wizard form is submitted without client-side validation being performed. The solution is to set the default back to validating everything in the JavaScript section by including the following line; $.validator.setDefaults({ ignore: "" });
I’ve also added a sample project at the bottom of this post.
 

For small MVC sample application I’m fiddling with on my spare time, I wanted to be able to split my form up in smaller parts, much like the ASP.NET Wizard Control that is available to you when you are using Web Forms.

Now, there are quite a few jQuery plugins out there that does this for you (like the Smart Wizard by TechLaboratory), but I wanted to create my own (based on some sample code created by Nadeem Afana) just for fun (and to improve my jQuery skills).

Basically I wanted a pretty simple Wizard, where I break up the input fields in a form in two or more steps, and display a summary at the end. I wanted the users to be able to step through the wizard without filling in required fields (just so they can get a grasp of the amount of info they would need to fill in), but of course they should be stopped when trying to submit it if anything is missing. I also wanted to avoid going to the server to retrieve a partial view for the summary.

The model I will use is pretty straight forward. It contains some fields for the user to fill inn, that I will split up in “Personal Details”, “Address” and “Contact Details”:

public class SimpleWizardFormModel : IValidatableObject
{
    [Required]
    [Display(Name = "First Name")]
    public string FirstName { get; set; }

    [Required]
    [Display(Name = "Last Name")]
    public string LastName { get; set; }

    [Display(Name = "Street Address")]
    public string Address { get; set; }

    [Required]
    [Display(Name = "Postal Code")]
    public string PostalCode { get; set; }

    [Required]
    [Display(Name = "City")]
    public string City { get; set; }

    [Display(Name = "Home Phone")]
    public string Phone { get; set; }

    [Display(Name = "Mobile Phone")]
    public string Mobile { get; set; }

    [Display(Name = "I'm at least 18 years old?")]
    public bool HasTurned18 { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (!HasTurned18)
            yield return new ValidationResult("You must be 18 or older.", new[] { "HasTurned18" });
    }
}

The view isn’t very complicated either:

@model SimpleWizardFormModel
@section head
{
    <style type="text/css">
        .wizard-step { display: none; }
        .wizard-confirmation { display: none; }
        .wizard-nav {  }
        .wizard-nav input[type="button"] { width: 100px; }
    </style>
}
@section script
{
    <script type="text/javascript">
        //*SNIP*
    </script>
}
<h2>Simple Wizard Form</h2>
@using (Html.BeginForm())
{
    <fieldset>
        <legend></legend>
        <div class="wizard-step">
            <h4>Personal Details</h4>
            <ol>
                <li>
                    @Html.LabelFor(m => m.FirstName)
                    @Html.TextBoxFor(m => m.FirstName)
                    @Html.ValidationMessageFor(m => m.FirstName)
                </li>
                <li>
                    @Html.LabelFor(m => m.LastName)
                    @Html.TextBoxFor(m => m.LastName)
                    @Html.ValidationMessageFor(m => m.LastName)
                </li>
                <li>
                    @Html.LabelFor(m => m.HasTurned18)
                    @Html.CheckBoxFor(m => m.HasTurned18)
                    @Html.ValidationMessageFor(m => m.HasTurned18)
                </li>
            </ol>
        </div>
        <div class="wizard-step">
            @**SNIP**@
        </div>
        <div class="wizard-step wizard-confirmation">
            <h4>Confirm</h4>
            <div id="field-summary"></div>
            <div id="validation-summary">
                <span class="message-error">Please correct the following errors;</span>
                @Html.ValidationSummary(true)
            </div>
        </div>
        <div class="wizard-nav">
            <input type="button" id="wizard-prev" value="<< Previous" />
            <input type="button" id="wizard-next" value="Next >>" />
            <input type="button" id="wizard-submit" value="Submit" />
        </div>
    </fieldset>
}

I’ve cut out the javascript as I will get back to that later, as well as a couple of the wizard steps since they are look just like step 1 (just with other input fields). Inside my Layout.cshtml-file I’m importing jquery, jquery.validate, jquery.validate.unobtrusive, and rendering the “head”-section (in the head-tag) and “script”-section (just before body-close-tag) seen above.

The most important “feature” of the view are the divs which have been given the “wizard-step”-class. These contains the various input fields and will become the (as the class name suggests) steps in the wizard that is presented to the user. Initially all these divs are hidden from the user (note the display –> none in the css styles), and the javascript will take care of showing the div that represents the current wizard step to the user.

And now the stuff which actually performs some work, the javascript:

function DisplayStep() {
    var selectedStep = null;
    var firstInputError = $("input.input-validation-error:first"); // check for any invalid input fields
    if (firstInputError.length) {
        selectedStep = $(".wizard-confirmation");
        if (selectedStep && selectedStep.length) { // the confirmation step should be initialized and selected if it exists present
            UpdateConfirmation();
        }
        else {
            selectedStep = firstInputError.closest(".wizard-step"); // the first step with invalid fields should be displayed
        }
    }
    if (!selectedStep || !selectedStep.length) {
        selectedStep = $(".wizard-step:first"); // display first step if no step has invalid fields
    }

    $(".wizard-step:visible").hide(); // hide the step that currently is visible
    selectedStep.fadeIn(); // fade in the step that should become visible

    // enable/disable the prev/next/submit buttons
    if (selectedStep.prev().hasClass("wizard-step")) {
        $("#wizard-prev").show();
    }
    else {
        $("#wizard-prev").hide();
    }
    if (selectedStep.next().hasClass("wizard-step")) {
        $("#wizard-submit").hide();
        $("#wizard-next").show();
    }
    else {
        $("#wizard-next").hide();
        $("#wizard-submit").show();
    }
}

The first method in my javascript, called “DisplayStep”, takes care of displaying the correct wizard step (typically this means the first step) when the view is loaded. if the view is loaded after submitting it to the server and server validation errors are found however, it will show the confirmation step if there is one, and if not it will show the first step which contains erroneous input. Once the correct step to show is found, it will decide where this step is located in relation to the other steps and show or hide the “previous”, “next” and “submit” buttons.

function PrevStep() {
    var currentStep = $(".wizard-step:visible"); // get current step

    if (currentStep.prev().hasClass("wizard-step")) { // is there a previous step?

        currentStep.hide().prev().fadeIn();  // hide current step and display previous step

        $("#wizard-submit").hide(); // disable wizard-submit button
        $("#wizard-next").show(); // enable wizard-next button

        if (!currentStep.prev().prev().hasClass("wizard-step")) { // disable wizard-prev button?
            $("#wizard-prev").hide();
        }
    }
}

The “PrevStep” method is pretty straight forward. It just finds the current step, hides it, shows the previous one, and shows/hides the buttons. No validation is performed before navigation to the previous step, but if desired, this could be done just like in the “NextStep” shown below.

function NextStep() {
    var currentStep = $(".wizard-step:visible"); // get current step

    var validator = $("form").validate(); // get validator
    var valid = true;
    currentStep.find("input:not(:blank)").each(function () { // ignore empty fields, i.e. allow the user to step through without filling in required fields
        if (!validator.element(this)) { // validate every input element inside this step
            valid = false;
        }
    });
    if (!valid)
        return; // exit if invalid

    if (currentStep.next().hasClass("wizard-step")) { // is there a next step?

        if (currentStep.next().hasClass("wizard-confirmation")) { // is the next step the confirmation?
            UpdateConfirmation();
        }

        currentStep.hide().next().fadeIn();  // hide current step and display next step

        $("#wizard-prev").show(); // enable wizard-prev button

        if (!currentStep.next().next().hasClass("wizard-step")) { // disable wizard-next button and enable wizard-submit?
            $("#wizard-next").hide();
            $("#wizard-submit").show();
        }
    }
}

The “NextStep” is a little more complicated. In addition to performing pretty much the same tasks as the “PrevStep” (only the opposite), it validates all input fields in the current step, and if there are any errors, you won’t be allowed to go to the next step. It only validates none empty fields however, i.e. the required rule if applicable for a given field isn’t evaluated. This is done because I wanted the user to be able to step through the entire form to see how much needs to be filled in (you can easily change this by changing the part of the script where the input fields are found). If the next step has been given the “wizard-confirmation”-class a call is also made to setup/update the confirmation (the specifics of this function will be explained further down).

function Submit() {
    if ($("form").valid()) { // validate all fields, including blank required fields
        $("form").submit();
    }
    else {
        DisplayStep(); // validation failed, redisplay correct step
    }
}

The last function related to navigation is “Submit”. This function validates the entire form (including required fields), and submits the form if all is good, or calls “DisplayStep” to show the confirmation step (if there is one), or the first step with errors on it (in cases where there are no confirmation step).

function UpdateConfirmation() {
    UpdateValidationSummary();
    var fieldList = $("<ol/>");
    $(".wizard-step:not(.wizard-confirmation)").find("input").each(function () {
        var input = this;
        var value;
        switch (input.type) {
        case "hidden":
            return;
        case "checkbox":
            value = input.checked;
            break;
        default:
            value = input.value;
        }
        var name = $('label[for="' + input.name + '"]').text();
        fieldList.append("<li><label>" + name + "</label><span>" + value + "&nbsp;</span></li>");
    });
    $("#field-summary").children().remove();
    $("#field-summary").append(fieldList);
}

function UpdateValidationSummary() {
    var validationSummary = $("#validation-summary");
    if (!validationSummary.find(".validation-summary-errors").length) { // check if validation errors container already exists, and if not create it
        $('<div class="validation-summary-errors"><ul></ul></div>').appendTo(validationSummary);
    }
    var errorList = $(".validation-summary-errors ul");
    errorList.find("li.field-error").remove(); // remove any field errors that might have been added earlier, leaving any server errors intact
    $('.field-validation-error').each(function () {
        var element = this;
        $('<li class="field-error">' + element.innerText + '</li>').appendTo(errorList); // add the current field errors
    });
    if (errorList.find("li").length) {
        $("#validation-summary span").show();
    }
    else {
        $("#validation-summary span").hide();
    }
}

The “UpdateConfirmation” function (and the “UpdateValidationSummary”-function called by this function) takes care of setting up / displaying the confirmation step. The “UpdateValidationSummary” function finds all input errors (if any) and adds them to the server validation error list (creating this list if it doesn’t already exist). The “UpdateConfirmation” function, in addition to calling “UpdateValidationSummary”, finds all input fields and associated labels and created a list with them that is displayed to the user.

$(function () {
    // attach click handlers to the nav buttons
    $("#wizard-prev").click(function () { PrevStep(); });
    $("#wizard-next").click(function () { NextStep(); });
    $("#wizard-submit").click(function () { Submit(); });

    // display the first step (or the confirmation if returned from server with errors)
    DisplayStep();
});

Last part of the javascript is where we hook up handlers for the navigation buttons and calls the function to display the first (or correct) step when the view is first loaded.

That was all the code needed, not to bad if I say so myself.

A couple of screens to show how it looks in action (first picture shows one of the steps, while the second picture shows the confirmation step):

wizard1wizard2

As I said in the beginning, this wizard is pretty basic, but it works pretty good. In the future I might add a navbar that lists all the wizard steps at the top, highlights any steps that contain errors, and allows the user to navigate directly to given step without needing to use the “next” and “prev” buttons.

Download sample code: SimpleWizardForm.zip