How to use input type='date' for date column in jqGrid
Asked Answered
M

1

2

jqGrid date column for inline editing is defined using colmodel and javascript below.

It uses jquery ui-date picker. This is lot of code to maintain and result is ugly.

How to use html5 native input type='date' for inline date editing if this is supported by browser instead of this code ?

colmodel:

{"template":DateTemplate
,"label":"Invoice date",
"name":"Invoicedate",
"index":"Invoicedate",
"editoptions":{
  "dataInit":initDateWithButton
  ,"size":10
  },

"searchoptions":{"dataInit":initDateWithButton
,"size":10,"attr":{"size":10}},"width":50
}

javascript:

var DateTemplate = {
    sorttype: 'date', formatter: 'date',
    formatoptions: {
        srcformat: "Y-m-d"
    },

    editoptions: { maxlength: 10, size: 10, dataInit: initDateWithButton },
    editable: true,
    searchoptions: {
        sopt: ['eq', 'ne', 'lt', 'le', 'gt', 'ge'],
        dataInit: initDateWithButton,
        size: 11,          // for the advanced searching dialog 
        attr: { size: 11 }   // for the searching toolbar 
    }
};

var initDateWithButton = function (elem) {
    if (/^\d+%$/.test(elem.style.width)) {
        // remove % from the searching toolbar 
        elem.style.width = '';
    }
    // to be able to use 'showOn' option of datepicker in advance searching dialog 
    // or in the editing we have to use setTimeout 
    setTimeout(function () {
        $(elem).css({ "box-sizing": "border-box", width: "5.7em" }).datepicker({
            // dateFormat: 'dd.mm.yy',
            showOn: 'button',
            changeYear: true,
            changeMonth: true,
            showWeek: true,
            showButtonPanel: true,
            onClose: function (dateText, inst) {
                inst.input.focus();
            }
        })
            .removeClass("ui-corner-all").addClass("ui-corner-left");

        $(elem).next('button.ui-datepicker-trigger').button({
            text: false,
            icons: { primary: 'ui-icon-calendar' }
        }).css({ width: '1em', height: '1.09em' })
            .removeClass("ui-corner-all").addClass("ui-corner-right")
        .find('span.ui-button-text')
        .css({ padding: '0.1em' })
        .siblings('span.ui-button-icon-primary')
        .css({ marginLeft: "-8.5px", marginTop: "-8.5px" });
        $(elem).next('button.ui-datepicker-trigger').andSelf().css("verticalAlign", "middle");
    }, 100);
};

This is ASP.NET MVC4 application.

Update

I tried answer but got issues.

  1. Date template in question does not contain newformat so this is still not defined. I replaced date parsing line with line

    $(elem).val($.jgrid.parseDate($.jgrid.formatter.date.newformat, orgValue, "Y-m-d"));
    

as recommended in comment.

  1. Line str = $.jgrid.parseDate("Y-m-d", $this.val(), cm.formatoptions.newformat);

convets valid date which is already converted to iso, like 1973-02-15 to long format like Thu Feb 15 1973 00:00:00 GMT+0200 (FLE Standard Time)

Required result is 1973-02-15 so conversion is not needed.

I solved this by replacing line

$this.val(str);

with

$this.val($this.val());

  1. After date inline edit is finished, date is shown in column in iso format. Localized date is shown only after grid is refreshed.

** Update **

Date does not fit to column width. In IE button is visible:

iebutton

but in Chrome for same column width big empty space appears and only part of first button is visible:

chrome

How to fix this so that buttons are visible for same column width ?

Menefee answered 25/9, 2014 at 14:8 Comment(0)
W
3

I find your question interesting and created the demo which works in Google Chrome without jQuery UI Datepicker and display during date editing the results like

enter image description here

The demo have column invdate defined as below

{ name: "invdate", width: 120, align: "center", sorttype: "date",
    formatter: "date", formatoptions: { newformat: "m/d/Y"}, editable: true,
    editoptions: { dataInit: initDateEdit } }

The callback function initDateEdit I defined as

var initDateEdit = function (elem, options) {
    // we need get the value before changing the type
    var orgValue = $(elem).val(),
        cm = $(this).jqGrid("getColProp", options.name);

    $(elem).attr("type", "date");
    if ((Modernizr && !Modernizr.inputtypes.date) || $(elem).prop("type") !== "date") {
        // if type="date" is not supported call jQuery UI datepicker
        $(elem).datepicker({
            dateFormat: "mm/dd/yy",
            autoSize: true,
            changeYear: true,
            changeMonth: true,
            showButtonPanel: true,
            showWeek: true
        });
    } else {
        // convert date to ISO
        $(elem).val($.jgrid.parseDate.call(this, cm.formatoptions.newformat, orgValue, "Y-m-d"));
    }
};

I don't know <input type="date"/> good enough. It uses input format of date as ISO. So I converted in the code above the original text to ISO to display correct date during editing. In the same way one have to convert the results of editing back to the formatoptions.newformat. I used beforeSaveRow callback in the case:

onSelectRow: function (rowid) {
    var $self = $(this),
        savedRow = $self.jqGrid("getGridParam", "savedRow");
    if (savedRow.length > 0 && savedRow[0].id !== rowid) {
        $self.jqGrid("restoreRow", savedRow[0].id);
    }
    $self.jqGrid("editRow", rowid, {
        keys: true,
        beforeSaveRow: myBeforeSaveRow
    });
}

where myBeforeSaveRow are defined as the following:

var myBeforeSaveRow = function (options, rowid) {
    var $self = $(this), $dates = $("#" + $.jgrid.jqID(rowid)).find("input[type=date]");
    $dates.each(function () {
        var $this = $(this),
            id = $this.attr("id"),
            colName = id.substr(rowid.length + 1),
            cm = $self.jqGrid("getColProp", colName),
            str;
        if ((Modernizr && Modernizr.inputtypes.date) || $this.prop("type") === "date") {
            // convert from iso to newformat
            str = $.jgrid.parseDate.call($this[0], "Y-m-d", $this.val(), cm.formatoptions.newformat);
            $this.attr("type", "text");
            $this.val(str);
        }
    });
};

UPDATED: One more demo supports better Opera 24 and empty input dates.

UPDATED 2: The demo contains small modification (the setting of this for $.jgrid.parseDate) and it uses free jqGrid 4.8.

Watchdog answered 25/9, 2014 at 22:28 Comment(28)
I tried it but got error since options parameter is undefined. I updated question.Menefee
@Andrus: jqGrid 4.4.5 is more as 1.5 year old. The second options parameter of dataInit was added in 4.5.4 one year ago. So you should either update jqGrid which you use or to use something like dataInit: function (elem) { return initDateWithButtonHTML5.call(this, elem, {name: "columnName"}); } instead of dataInit: initDateWithButtonHTML5. Alternatively you can just use fixed date format which you use instead of cm.formatoptions.newformat. In the case you will don't need cm and options.name.Watchdog
As shown in code in question, source date values are in ISO format accepted by input type='date' . input element performs convertsion to/from iso format automatically from date shown in browser. Should we remove format conversion and myBeforeSaveRow from this code ?Menefee
@Andrus: If you read the code of initDateEdit from my answer you will see that thesecond options parameter will be used to get cm.formatoptions.newformat (see the first parameter of $.jgrid.parseDate). So if you know newformat then you can use it directly in the value of newformat property from formatoptions or $.jgrid.formatter.date.newformat. In the case you should remove additionally the second options parameter of initDateWithButtonHTML5 and the line cm = $(this).jqGrid("getColProp", options.name) too.Watchdog
@Andrus: Inside of myBeforeSaveRow you need just replace cm.formatoptions.newformat to cm.formatoptions.newformat || $.jgrid.formatter.date.newformatWatchdog
4.4.5 source code does not contain beforeSaveRow . How to use this in 4.4.5 or should I upgrade ? I have added changes to jqgrid source code from your answers, so I must apply those changes manually and test again. This is lot of work.Menefee
@Andrus: I recommend you to update to the latest version of jqGrid. The goal of stackoverflow is providing solutions which can have values for many people. Creating solutions for some old version have some value for you only. Only if you really can't update jqGrid because of some project specific reasons you can create the solution of your problem yourself. I think it's possible using serializeRowData or successfunc or aftersavefunc or by "subclassing" saveRow method (see the answer for an example of "subclassing").Watchdog
@Andrus: I tried to explain you in my previous comment that updating jqGrid to 4.6.0 (or higher which will be hopefully published soon) is the way to solve the problem. If you do want to create the implementation with jqGrid 4.4.5 I think it's possible based on the idea from my answer, but you have to write the implementation yourself. You can't use my code directly because a lot of changes in jqGrid between 4.4.5 and 4.6.0, you can just use the idea. Just an example parseDate have 2 options format, date in 4.4.5 and 4 options format, date, newformat, opts in 4.6.0.Watchdog
I upgraded to 4.6.0 . Those issues occur in 4.6.0 and one can reproduced in your sample as described.Menefee
@Andrus: Which test case in my demo to reproduce the problem? What is the current problem?Watchdog
This is described in issue 3: double click in date column to edit in Chrome. Press x in right side to clear date. Press Enter. Observed: year 1970 date appears. Expected: date column should represent empty date (null value)Menefee
@Andrus: It has no relation to the main part of my answer and can be fixed in less as one min. See the modified demo.Watchdog
Thank you. It worked. Now refresh 4th issue in question remains unresolved. Another issue: ff column width is small, buttons are not visible (if datepicker is used, calendar button appears in next row in this case). How to show buttons also if column width is small. Why demo code uses $(elem).css("width", "") which is not in answer?Menefee
@Andrus: The line $(elem).css("width", "") just removes width: 98%; which jqGrid set on standard <input> inline editing fields. It seems to me that the setting don't help in case of usage type="date". I makes some other small changes in the code of demo after I posted the answer. I changed for example width: 130 to width: 120. I'm not sure which behavior you expect in case of usage <input type="date" .../> with small width. Default behavior: one can use keyboard arrows (right & left) to move cursor or to edit (up & down). One can also press keys with numbers, Esc and Enter.Watchdog
$(elem).css("width", "9.6em") decreases width so that there is no empty space before date edit delete icon and whole date is visible in search toolbar and inline edit. How to replace 9.6em with something more generic ?Menefee
@Andrus: I don't know an universal value for width. The best value depend on how the web browser build the control. In the last demo which I posted in my answer I used $(elem).css("width", /OPR/.test(navigator.userAgent) ? "11em": "10em"); to set 10em for Chrome and 11em for Opera 24. You can test your cases and choose any other width value ("96%" for example) which more corresponds your requirements.Watchdog
If row is added or edited using form, entered date value is not saved if this code is used. In Chrome date entry control appers properly but changed value is not saved. How to save date from form also ?Menefee
@Andrus: I used inline editing in my demos. If you use form editing you should provide your demo. There are some common problems with editing of dates in jqGrid which I fixed in my fork. The usage of reformatAfterEdit: true can be important for old jqGrid versions. I recommend you to read the post where I described the problem in details with many demos and the exact test case.Watchdog
I'm using reformatAfterEdit: true. I read your post but havent found solution. I posted it as separate question in #28705682Menefee
In 4.8 line $(elem).val($.jgrid.parseDate($.jgrid.formatter.date.newformat, orgValue, "Y-m-d")) causes exception Cannot read property 'replace' of undefined It looks like $.jgrid.formatter.date.newformat is undefinedMenefee
@Andrus: it's correct. One have to use $.jgrid.parseDate.call($self[0], ...). It's required because of the line which get formatter.date depend on the grid locale.Watchdog
I tried but got exception that $self is not defined. It looks like $self is out of scope in this callMenefee
@Andrus: I appended the answer with the modified demo and included the corresponding information in the Readme of free jqGrid 4.8.Watchdog
This requires specifying date format in every column in colmodel. If date format is not specified cm.formatoptions.newformat is undefined and exception occurs. How to fall back to default locale date format if date format is not specified in colmodel so that same colmodel supports multiple locales automatically ?Menefee
I'm using always default locale date format and never set date format in colmodel. Using $.jgrid.formatter.date.newformat worked well in this case earlier.Menefee
@Andrus: You should forget $.jgrid.formatter.... which is not more exists in free jqGrid 4.8 (like in the current code of jqGrid too). There are locale between $.jgrid and formatter.date.newformat (like $.jgrid.locales["en-US"].formatter.date.newformat or $.jgrid.locales.de.formatter.date.newformat). So it's better to use getGridRes to get the strings: $grid.jqGrid("getGridRes", "formatter.date.newformat"). I updated the demo.Watchdog
@Andrus: Tony implemented the changes in his code based on my old. So the names of properties are a little different. He uses $.jgrid.regional["en-US"] and I use $.jgrid.locales["en-US"]. Moreover I added getGridRes and he uses $.jgrid.getRegional. The logic of getGridRes and $.jgrid.getRegional is a little different too. In any way you should not use localized stings like $.jgrid.formatter.date.newformat.Watchdog
@Andrus: I would recommend you to search your existing code for $.jgrid.formatter, $.jgrid.defaults, $.jgrid.search, $.jgrid.edit, $.jgrid.view, $.jgrid.del, $.jgrid.nav, $.jgrid.col, $.jgrid.errors. You can still set defaults options using the objects, but you should use deep version: $.extend(true, ...);. If you need get any localized value from above parts of settings you should always use getGridRes method now.Watchdog

© 2022 - 2024 — McMap. All rights reserved.