Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Eonasdan datetimepicker for bootstrap 3 in xPages

I'm having a problem with the bootstrap dateTimePicker control in my xpage application and I suspect this is to do with the way xPages generates a control id.

The following code works fine without an id on the inputText element.

<script type="text/javascript" src="/fPath/jquery-min.js"></script>
<script type="text/javascript" src="/fPath/bootstrap/js/bootstrap.min.js"></script>
<script type="text/javascript" src="/RfPath/bootstrap-datetimepicker-3.0.0/js/moment.min.js"></script>
<script type="text/javascript" src="/fPath/bootstrap-datetimepicker-3.0.0/js/bootstrap-datetimepicker.min.js"></script>

<div class='input-group date' data-datetimepicker="true">
    <xp:inputText styleClass="form-control timePicker">
        <xp:this.converter>
            <xp:convertDateTime type="time" timeStyle="short" />
        </xp:this.converter>
    </xp:inputText>
    <span class="input-group-addon">
        <span class="glyphicon glyphicon-time"></span>
    </span>
</div> 

As soon as I add an id back in I receive the following error when clicking away/out of the control:

Error: Syntax error, unrecognized expression: unsupported pseudo: _id1

the id is required to map the data back to the document but I am not actually using it to attach the datetimepicker - I am using a class for this:

$('.timePicker').each(function(i,el){SHARED.timePickerOpen(el)}) 

SHARED = {
    timePickerOpen: function(el){
        $(el).datetimepicker({
            pickDate: false,
            pickTime: true,
            useCurrent: true,
            minuteStepping:5  
        });
    }
}

UPDATE #1:

Sounds like there's a bit of misunderstanding going on so I'll try to explain further...

  • In testing, I wrote the code in pure html giving the field an ID and it works fine.
  • If I use an xpage field (where domino generates the ID using colons) the date picker fails with the error described above.
  • I do not use the ID to bind the event to the field, I use a class get the field then bind the datetimepicker control to it.

The problem appears to be the datetimepicker using the field id. Even though I obtain the field through the class name (Not the id) the datetimepicker code seems to want to use an id if one is found in the field. The datetimepicker binds to the field without any problems. When you attempt to select a time the model box displays and allows you to select a time, the error occurs when you try to click away from the field to close the model time control.

like image 844
Neil Cowley Avatar asked Dec 25 '22 07:12

Neil Cowley


1 Answers

Interesting plugin. I created a sample page to see if I can get it to integrate with an XPage and if I get the same error. After doing what I describe below I got it to run without the issues you reported.

As described in the docs, I've added the plugin and moment.js to a database, and used the sample code here to build a simple demo page. In the database I am using the Bootstrap4XPages plugin (March release), so Bootstrap 3.1.1 is already loaded.

<?xml version="1.0" encoding="UTF-8"?>
<xp:view
  xmlns:xp="http://www.ibm.com/xsp/core"
  xmlns:xc="http://www.ibm.com/xsp/custom">

<xp:this.resources>
    <xp:script
        src="momentjs-2.7.0/moment.min.js"
        clientSide="true">
    </xp:script>
    <xp:script
        src="eonasdan-datetimepicker/js/bootstrap-datetimepicker.min.js"
        clientSide="true">
    </xp:script>
    <xp:styleSheet
        href="eonasdan-datetimepicker/css/bootstrap-datetimepicker.min.css"></xp:styleSheet>
</xp:this.resources>

<xp:div
    xp:key="facetMiddle">

    <p>
        This page uses&#160;
        <a
            href="https://github.com/Eonasdan/bootstrap-datetimepicker">Eonasdan's Bootstrap DateTimePicker</a>
        .
    </p>

    <div
        class="col-sm-6">
        <div
            class="form-group">
            <div
                class='input-group date'
                id='datetimepicker1'>

                <xp:inputText
                    id="inputText1"
                    styleClass="form-control">
                    <xp:this.converter>
                        <xp:convertDateTime
                            type="both"
                            timeStyle="short" />
                    </xp:this.converter>
                </xp:inputText>
                <span
                    class="input-group-addon">
                    <span
                        class="glyphicon glyphicon-calendar"></span>
                </span>
            </div>
        </div>
    </div>

</xp:div>

<xp:scriptBlock
    id="scriptBlock1">

    <xp:this.value><![CDATA[$(function () {
    $('#datetimepicker1').datetimepicker();
    });]]></xp:this.value>
</xp:scriptBlock>

</xp:view>

Opening the XPage for the first time gave me a familiar error in Chrome's console:

Uncaught TypeError: undefined is not a function

I've seen that one before: newer jQuery plugins try to use its AMD loader, but that doesn't play well with the Dojo implementation in XPages. There are 2 workarounds that I know of:

  1. Use Sven Hasselbach's method to load the (JavaScript) resources before Dojo.
  2. Change the source code of the library you're using. This method is not perfect too, but the one I prefer. In the JavaScript file (in this case named bootstrap-datetimepicker.js you need to find the lines that determine if it can use the AMD loader. They can mostly be found at the beginning or end of a JavaScript file.

Here's the code you're looking for in bootstrap-datetimepicker.js:

; (function (factory) {
if (typeof define === 'function' && define.amd) {
    // AMD is used - Register as an anonymous module.
    define(['jquery', 'moment'], factory);
} else {
    // AMD is not used - Attempt to fetch dependencies from scope.
    if (!jQuery) {
        throw 'bootstrap-datetimepicker requires jQuery to be loaded first';
    } else if (!moment) {
        throw 'bootstrap-datetimepicker requires moment.js to be loaded first';
    } else {
        factory(jQuery, moment);
    }
}
}

We then disable (or remove; your choice) the AMD part:

; (function (factory) {
//if (typeof define === 'function' && define.amd) {
    // AMD is used - Register as an anonymous module.
//    define(['jquery', 'moment'], factory);
//} else {
    // AMD is not used - Attempt to fetch dependencies from scope.
    if (!jQuery) {
        throw 'bootstrap-datetimepicker requires jQuery to be loaded first';
    } else if (!moment) {
        throw 'bootstrap-datetimepicker requires moment.js to be loaded first';
    } else {
        factory(jQuery, moment);
    }
//}
}

A live demo can be found here.

like image 132
Mark Leusink Avatar answered Dec 28 '22 08:12

Mark Leusink