简体   繁体   中英

jQuery datepicker - Knockout binding set initial date

I'm using the jQuery datepicker with a custom binding I've made (after combining several examples from all around). To be clear; everything works just fine when changing the date, my problems lies with the scenario that the user does not change the date. This means the datepicker value should be the initial date fed into the datepicker and the textbox element.

This is MVC so I'm using a regular C# DateTime object. Be aware that this DateTime is nullable, so a DateTime? object.

Take this scenario:

A user loads the webpage, and my MVC model is sent to that page. My model looks like this (JSON format):

{
    "User": "Christian Haase",
    "Username": "ChristianH",
    "Date": "/Date({som-random-millisecond-number})/"
}

NOTE: this is not copy-pasted, so some things might look a bit funny like this Date value.

Lets make a simple webpage for the user to change this value:

<p data-bind="text: User" ></p>
<input data-bind="value: Username" />
<input data-bind="datepicker: Date, datepickerOptions: { dateFormat: 'dd/mm/yy' }" />

Very simple, but this should be enough from the markup side of things. Let's hook up knockout and the datepicker binding.

<script type="text/javascript">
    $(document).ready(function(){

        // Loading the viemodel from MVC (the JSON object)
        var vm = ko.mapping.fromJSON('@Model');

        // Adding the datepicker binding!
        ko.bindingHandlers.datepicker = {
            init: function (element, valueAccessor, allBindingsAccessor) {
                // Getting the datepickerOptions value applied to the binding, if any. Else just an empty object.
                var options = allBindingsAccessor().datepickerOptions || {};

                // Instantiating the datepicker with the options object
                $(element).datepicker(options);

                // Listen for any changes in the element
                ko.utils.registerEventHandler(element, "change", function(){
                    var observable = valueAccessor();
                    var value = $(element).val();

                    // Check to see whether the element.val() is empty or not
                    if(!value){
                        // If empty, I want the observable to hold value 'null'
                        observable(null);
                    } else{
                        // Converting the string '01/09/2016' to an actual JS Date object and settings the observable to that value
                        var date = convertStringToDate(value);
                        observable(date);
                    }
                });
            },
            update: function(element, valueAccessor){
                // Get the value from the viewmodel
                var value = ko.unwrap(valueAccessor());
                // Get the observable
                var observable = valueAccessor();

                // Check whether the value is null or not
                if(value === null){
                    // If the value is null, set the value of the element equal to an empty string
                    $(element).val('');
                }
                else{
                    // If the value is not null (is it something) set the new date on the datepicker.

                    // Parse the JSON date string ('/Date({some-random-millisecond})')
                    var date = parsejsonDateString(value);

                    // Set the new JS Date object to the datepickers 'setDate' function
                    $(element).datepicker('setDate', value);
                }
            }
        }

        vm.save = function(){
            // Function to save the changes made to the object
            // Loggign the date to see the value that'll be sent to the MVC controller
            console.log(vm.Date());
            ....
            // The ajax request is irrelevant
        }

        ko.applyBindings(vm);
    });
</script>

now, you might notice that I'm doing some calls to manipulate the Date object, the JSON Date string ( '/Date({some-random-millisecond})/' ), and the date in raw string ( '01/09/2016' ), and I suspect that one of these might be messing with the dates. here are those functions:

var jsonDateRE = /^\/Date\((-?\d+)(\+|-)?(\d+)?\)\/$/;
var parseJsonDateString = function (value) {
    var arr = value && jsonDateRE.exec(value);
    if (arr) {
        return new Date(parseInt(arr[1]));
    }
    return value;
};

var convertStringToDate = function (stringDate) {
    if (stringDate != null || stringdate || string != "") {
        var from = stringDate.split("/");
        return new Date(from[2], from[1] - 1, from[0]);
    }
    return "";
}

I'm quite lost on what actually happens when the datepicker initializes, but if the user hits 'Save' WITHOUT changing the initial date, my console says; 'Invalid date'. On the other hand, if the user DOES change the date from the datepicker, everything works as expected.

I'm not sure how to actually make this datepicker binding work, and all possible guidance is highly appreciated!

Please let me know if you need further explanation or code (thoguh I don't really see other relevant code than this). I will be happy to apply all the needed information.

I've put together a little example. Notice that I added a span that shows the value of Date . That's an easy way to see what it is at startup time, and what it becomes when you choose a different value. In this example, there doesn't seem to be anything non-date-like about it, but you may have different conditions in your system.

Also note that I corrected capitalization on parsejsonDateString to parseJsonDateString . If that was how it was in your code, that would be a problem (but you should see errors in the console).

 var jsonDateRE = /^\\/Date\\((-?\\d+)(\\+|-)?(\\d+)?\\)\\/$/; var parseJsonDateString = function (value) { var arr = value && jsonDateRE.exec(value); if (arr) { return new Date(parseInt(arr[1])); } return value; }; var convertStringToDate = function (stringDate) { if (stringDate != null || stringdate || string != "") { var from = stringDate.split("/"); return new Date(from[2], from[1] - 1, from[0]); } return ""; }; ko.bindingHandlers.datepicker = { init: function(element, valueAccessor, allBindingsAccessor) { // Getting the datepickerOptions value applied to the binding, if any. Else just an empty object. var options = allBindingsAccessor().datepickerOptions || {}; // Instantiating the datepicker with the options object $(element).datepicker(options); // Listen for any changes in the element ko.utils.registerEventHandler(element, "change", function() { var observable = valueAccessor(); var value = $(element).val(); // Check to see whether the element.val() is empty or not if (!value) { // If empty, I want the observable to hold value 'null' observable(null); } else { // Converting the string '01/09/2016' to an actual JS Date object and settings the observable to that value var date = convertStringToDate(value); observable(date); } }); }, update: function(element, valueAccessor) { // Get the value from the viewmodel var value = ko.unwrap(valueAccessor()); // Get the observable var observable = valueAccessor(); // Check whether the value is null or not if (value === null) { // If the value is null, set the value of the element equal to an empty string $(element).val(''); } else { // If the value is not null (is it something) set the new date on the datepicker. // Parse the JSON date string ('/Date({some-random-millisecond})') var date = parseJsonDateString(value); // Set the new JS Date object to the datepickers 'setDate' function $(element).datepicker('setDate', value); } } } ko.applyBindings({ User: ko.observable('1'), Username: ko.observable('The guy'), Date: ko.observable('?') }); 
 <link href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.6.4/css/bootstrap-datepicker.min.css" rel="stylesheet"/> <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.6.4/js/bootstrap-datepicker.min.js"></script> <p data-bind="text: User" ></p> <input data-bind="value: Username" /> <input data-bind="datepicker: Date, datepickerOptions: { dateFormat: 'dd/mm/yy' }" /> <div>Date as text: <span data-bind="text:Date"></span></div> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM