Knockout + JavaScript + Validation useful custom bindings and functions

Hi,

Just to post some helpful bindings and JavaScript functions that I used multiple times throughout my projects. I have been using this in creating forms using jQuery and Knockout with Ajax web services.

DatePicker

Update: be careful if you need to deal with different timezone as client timezone and server timezone might not be the same. To avoid this issue altogether you can use knockout-jqueryui extension so that the value is written as string. This way we only need to deal with server timezone.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
ko.bindingHandlers.datepicker = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        //initialize datepicker with some optional options
        var options = allBindingsAccessor().datepickerOptions || {},
            $el = $(element);
 
        $el.datepicker(options);
 
        //handle the field changing
        ko.utils.registerEventHandler(element, "change", function () {
            var observable = valueAccessor();
            observable($el.datepicker("getDate"));
        });
 
        //handle disposal (if KO removes by the template binding)
        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            $el.datepicker("destroy");
        });
 
    },
    update: function (element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor()),
            $el = $(element);
 
        //handle date data coming via json from Microsoft
        if (String(value).indexOf('/Date(') == 0) {
            value = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1")));
        }
 
        var current = $el.datepicker("getDate");
 
        if (value - current !== 0) {
            value = new Date(Date.fromISO(value));
            $el.datepicker("setDate", value);
        }
    }
};
 
/* Date fix for JSON date IE8 */
(function () {
    var D = new Date('2011-06-02T09:34:29+02:00');
    if (!D || +D !== 1307000069000) {
        Date.fromISO = function (s) {
            var day, tz,
            rx = /^(\d{4}\-\d\d\-\d\d([tT ][\d:\.]*)?)([zZ]|([+\-])(\d\d):(\d\d))?$/,
            p = rx.exec(s) || [];
            if (p[1]) {
                day = p[1].split(/\D/);
                for (var i = 0, L = day.length; i < L; i++) {
                    day[i] = parseInt(day[i], 10) || 0;
                };
                day[1] -= 1;
                day = new Date(Date.UTC.apply(Date, day));
                if (!day.getDate()) return NaN;
                if (p[5]) {
                    tz = (parseInt(p[5], 10) * 60);
                    if (p[6]) tz += parseInt(p[6], 10);
                    if (p[4] == '+') tz *= -1;
                    if (tz) day.setUTCMinutes(day.getUTCMinutes() + tz);
                }
                return day;
            }
            return NaN;
        }
    }
    else {
        Date.fromISO = function (s) {
            return new Date(s);
        }
    }
})()
 
function isValidDate(value, format) {
    var isValid = true;
 
    try {
        jQuery.datepicker.parseDate(format, value, null);
    }
    catch (error) {
        isValid = false;
    }
 
    return isValid;
}
 
$.validator.addMethod(
    "australianDate",
    function (value, element) {
        return isValidDate(value, "dd/mm/yy");
    },
    "Invalid date. Please enter a date in the format dd/mm/yyyy."
);
 
jQuery.validator.addClassRules("DateAU", {
    australianDate: true
});

Radio Yes/No buttons

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
ko.bindingHandlers.radioTrueFalseBoolean = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        var updateHandler = function () {
            var valueToWrite;
            if ((element.type == "radio") && (element.checked)) {
 
                if (element.value != null) {
                    valueToWrite = element.value == "false" ? false : true;
                }
 
            } else {
                return; // "checked" binding only responds to checkboxes and selected radio buttons
            }
 
            var modelValue = valueAccessor(), unwrappedValue = ko.utils.unwrapObservable(modelValue);
            modelValue(valueToWrite);
        };
        ko.utils.registerEventHandler(element, "click", updateHandler);
 
        // IE 6 won't allow radio buttons to be selected unless they have a name
        if ((element.type == "radio") && !element.name)
            ko.bindingHandlers['uniqueName']['init'](element, function () { return true });
    }
        ,
    update: function (element, valueAccessor, allBindingsAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        var newValueAccessor = function () {
            if (value != null) {
                return value ? "true" : "false";
            }
            else {
                return null;
            }
        };
        ko.bindingHandlers.checked.update(element, newValueAccessor, allBindingsAccessor);
    }
};
 
jQuery.validator.addMethod("requiredRadioValue", function (value, element) {
    var selectedValue = $('input:radio[name=' + element.name + ']:checked').length;
    return selectedValue > 0;
}, "You must select one of the options.");
 
jQuery.validator.addClassRules("requiredRadio", {
    requiredRadioValue: true
});

Synchronous ajax call (normally used to populate dropdown values)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function GetSynchronousJSONResponse(url, postData) {
    var xmlhttp = null;
    if (window.XMLHttpRequest)
        xmlhttp = new XMLHttpRequest();
    else if (window.ActiveXObject) {
        if (new ActiveXObject("Microsoft.XMLHTTP"))
            xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
        else
            xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
    }
    // to be ensure non-cached version of response
    url = url + "?rnd=" + Math.random();
 
    xmlhttp.open("POST", url, false); //false means synchronous
    xmlhttp.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    xmlhttp.send(postData);
    var responseText = xmlhttp.responseText;
    return responseText;
}

Get QueryString

1
2
3
4
5
6
7
8
9
10
function getUrlVars() {
    var vars = [], hash;
    var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
    for (var i = 0; i < hashes.length; i++) {
        hash = hashes[i].split('=');
        vars.push(hash[0]);
        vars[hash[0]] = hash[1];
    }
    return vars;
}


IE9 Filter problem

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
if (!Array.prototype.filter) {
    Array.prototype.filter = function (fun /*, thisp */) {
        "use strict";
 
        if (this === void 0 || this === null)
            throw new TypeError();
 
        var t = Object(this);
        var len = t.length >>> 0;
        if (typeof fun !== "function")
            throw new TypeError();
 
        var res = [];
        var thisp = arguments[1];
        for (var i = 0; i < len; i++) {
            if (i in t) {
                var val = t[i]; // in case fun mutates this
                if (fun.call(thisp, val, i, t))
                    res.push(val);
            }
        }
 
        return res;
    };
}

That's all for now. Hope this helps.

Andreas

Comments

Popular posts from this blog

SharePoint 2013 anonymous access add attachments to list item

CRM Plugin - Parent and Child Pipeline