Saturday, August 20, 2016

Mapping default value when using the datetimepicker with knockout

If you are using the bootstrap datetimepicker from http://eonasdan.github.io/bootstrap-datetimepicker/Installing/ with knockout and facing issues for mapping the default value this post may help you.

Below is the code I was using for the custom binding.

ko.bindingHandlers.datepicker = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        //initialize datepicker with some optional options
        var options = {
            format:'DD/MM/YYYY hh:mm A',
            defaultDate : valueAccessor()(),
            sideBySide:true
        };
        if (allBindingsAccessor() !== undefined) {
            if (allBindingsAccessor().datepickerOptions !== undefined) {
                options.format = allBindingsAccessor().datepickerOptions.format !== undefined ? allBindingsAccessor().datepickerOptions.format : options.format;
            }
        }
        $(element).datetimepicker(options);
        //when a user changes the date, update the view model
        ko.utils.registerEventHandler(element, "dp.change", function (event) {
            var value = valueAccessor();
            if (ko.isObservable(value)) {
                value(event.date);
            }
        });
        var defaultVal = $(element).val();
        var value = valueAccessor();
        value(moment(defaultVal, options.format));
    },
    update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var thisFormat = 'DD/MM/YYYY hh:mm A';

        if (allBindingsAccessor() !== undefined) {
            if (allBindingsAccessor().datepickerOptions !== undefined) {
                thisFormat = allBindingsAccessor().datepickerOptions.format !== undefined ? allBindingsAccessor().datepickerOptions.format : thisFormat;
            }
        }

        var value = valueAccessor();
        var unwrapped = ko.utils.unwrapObservable(value());

        if (unwrapped === undefined || unwrapped === null) {
            element.value = new moment(new Date());
        } else {
            element.value = unwrapped.format(thisFormat);
        }
    }
};

When initiating the model I was passing the values to match the format I have set, but the datetimepicker was returning NaN exception when the date value is more than 12 and then I got to know option parameter format passed when initializing the datetimepicker was only used for formatting the input text and when reading from the input box the defaultDate option should have a valid date which is understandable by moment.js. So for resolving the issue the other option I found is when initializing the observable can pass the moment object and moment object can be initialized with the any given format when format is passed. So here below is the code to initialize the observable which is used with the datetimepicker binding.

 function TestModel(data){
  self.test_date = ko.observable(moment().add('1','week'));
    if(data.test_date != undefined){
        isNumeric = /^(\d+)$/;
        //if data comes from mySQL then use unix timestamp
        //or if data comes from posted value when reloading use custom format
        if(isNumeric.test(data.test_date )){
            self.whencantake_date(moment.unix(data.test_date )); 
        } else{
            self.whencantake_date(moment(data.test_date ,'DD/MM/YYYY hh:mm A'));     
        }
    }

Hope this helped you.

Knockout observable array options binding issue with AJAX loading

I had an issue where when I loaded all the element using jQuery AJAX synchronous option the binding worked perfectly but it didn't work when I loaded the array using asynchronously. Here is my finding in case if any one else come up with the same issue.

    function ViewModel()
    {
        var self = this;
        self.AllSubjects = ko.observableArray();
        $.ajax({
            url: all_subject_url,
            type: 'post',
            data: '',
            async: false,
            success: function (result) {
                var response = JSON.parse(result);
                if (response.status == 'success')
                {
                    var mapped = ko.mapping.fromJS(response);
                    if (mapped.Subjects()) {
                        self.AllSubjects = mapped.Subjects;
                    }
                }
            }
        });

        <select class="form-control" name="subject" data-bind="options:$root.AllSubjects,optionsText:'name',optionsCaption:'Select Subject',optionsValue:'id',value:subject">
        </select>

The above code work fine when loading values synchronusly, but won't work if you remove the async=false option in the $.ajax option. The issue with one the AJAX data is loaded we are replacing the AllSubjects observable array with new observable array. The correct way to map it as below.

self.AllSubjects(mapped.Subjects());

Sunday, July 31, 2016

How to display new fonts in CK Editor?

You may want to support different fonts in different language in Ck Editor. This is how I have done that for one of my recent website.

1. First of all I have downloaded the font to be used in my CKEditor.
2. Then I have created css file named font.css with following content in it. URL refers to the font file name from website home. Font family is used to display the font in font dropdown.

@font-face {
   font-family: "Tamil";
   src: url(/exam/assets/js/ckeditor/fonts/tamil.ttf); /* IE */
   src: local("Tamil/Tamil"), url("/exam/assets/js/ckeditor/fonts/tamil.ttf") format("truetype"); /*non-IE*/
}

3. After that I have modified the config.js file in the CKEditor folder with following changes.
CKEDITOR.editorConfig = function( config ) {
//Exiting code goes here
//..........................
//my code is as follow below existing code
config.contentsCss = '/exam/assets/js/ckeditor/fonts/fonts.css';
config.font_names = 'Tamil/Tamil;' + config.font_names;
}
Here the first line the reference to the css file I created and second line is append the new font with the font list.

Hope this works for you if not please put a comment.