2
votes

Here’s what I want to do:

On an XPage I want to have an edit box control that calls some server-side code (Java bean or SSJS) when the user presses enter. The main use case for this is that this page will be run from an an iPad and a bluetooth barcode scanner is attached. This scanner emulates a keyboard. The user will tap into the edit box. They will then scan with the scanner and the barcode will be entered along with an “enter” key. The edit box needs to be immediately cleared for the next scan and the value of the scan needs to be processed. The processing will do various backend things to a java bean and then return a message to be displayed on the page. Basically one of these: 1. Item not found, 2. Item is available and assigned, 3. Item is already assigned but you can have it anyway 4. Item Unavailable

I have code for this and overall the core code is working fine. I’ve normally used the onchange event but since I had this problem I moved to the onkeypress event. That was as much an attempt to get it to run in IE. Again the goal is only for the iPad/Safari, but I’d love to get it working on desktop browsers as well. I’m currently testing only with Chome.

In the CSJS piece of the edit box I have this code:

var e = dojo.fixEvent(thisEvent);
if (e.keyCode == dojo.keys.ENTER) {
return true;
} else {
return false;
}

The thinking is that I only want to call the SSJS on Enter.

Here’s the SSJS code that clears the edit box, processes the value and then sets a couple viewScope variables.

// Get the value off the page and put it in to the key variable
var key:String = getComponent("scanBox").getValue();
// clear the scanbox so it's ready for the next scan.
getComponent("scanBox").setValue("");
//Managed Bean to hold the last scan value - since the scanbox is cleared we do want to show the value
Scan.setLastScan(key);

// Managed Bean to process the item.
AddToJob.processItem(key);

viewScope.put("vsShowAssignPanel", true);
viewScope.put("vsShowMessagePanel", false);

The SSJS code is set to partially update the main content of the page. I’m using bootstrap and am trying to refresh everything on the page except a top and bottom nav bar. The editbox itself IS in the partial refresh zone since I’m clearing the value after the scan.

Finally here’s the problem:

The SSJS code of the button is being run TWICE. Not all for some reason but maybe 90% of the time. I’m not clicking twice. The way I test is by using Chrome on my desktop and I paste a value into the field and press enter once on the keyboard.

If I’m trying to scan in and assign an Item with a barcode. It first runs fine. the it updates the objects and saves a field to the backend NotesDocument to mark it assigned. Just want I want. But the second run through hits and it’s deterring that the item is ALREADY assigned and giving me back the wrong message.

After a lot of trial and error I’m fairly certain that something in the JSF lifecycle is causing this to run twice. Why I have no idea. I have no idea how to get it once.

The only good workaround that I’ve found so far is in the “AddToJob.processItem” method. “AddToJob” is a managed bean in the viewScope. In there I store and keep the last barCode. Then I do a check to see of the value is the same. If so then I assume it’s on the second run and stop processing.

I’ve used this basic concept of calling code in the on change event of an edit control for years. But that was inside XPages Mobile Controls and sometimes things do behave a little differently in there. I’m trying to re-write this app to use Bootstrap rather then XPages Mobile controls.

Below is the full XML markup of the edit control if that helps.

<xp:inputText id="scanBox" styleClass="newTarget">
<xp:this.attrs>
    <xp:attr name="placeholder" value="Tap to Scan..." />
    <xp:attr name="autocorrect" value="off" />
</xp:this.attrs>
<xp:this.dojoAttributes>
    <xp:dojoAttribute name="autocorrect" value="off" />
</xp:this.dojoAttributes>
<xp:eventHandler event="onkeypress" submit="true" refreshMode="partial" refreshId="mainPanel">
<xp:this.script><![CDATA[var e = dojo.fixEvent(thisEvent);
if (e.keyCode == dojo.keys.ENTER) {
return true;
} else {
return false;
}]]></xp:this.script>
<xp:this.action><![CDATA[#{javascript:// Get the value off the page and put it in to the key variable
var key:String = getComponent("scanBox").getValue();
// clear the scanbox so it's ready for the next scan.
getComponent("scanBox").setValue("");

//Managed Bean to hold the last scan value - since the scanbox is cleared we do want to show the value
Scan.setLastScan(key);

// Managed Bean to process the item.
AddToJob.processItem(key);

viewScope.put("vsShowAssignPanel", true);
viewScope.put("vsShowMessagePanel", false);}]]></xp:this.action>
</xp:eventHandler></xp:inputText>

ANY information is appreciated. Thank you very much!!!

3

3 Answers

7
votes

The Enter key is a special one because pressing enter can also submit the form you are currently in. In this case you actually have two POST requests hitting the server, one for the partial request and then another when the form is submitted.

What you need to do it prevent the enter key from submitting the form using an event.preventDefaults() call. Here is the updated CSJS

var e = dojo.fixEvent(thisEvent);
if (e.keyCode == dojo.keys.ENTER) {
    e.preventDefault();
    return true;
} else {
    return false;
}

Now the form won't get submitted but the partial refresh will fire correctly.

0
votes

David,

Not really a specific XPages solution but I had to do this in the Notes client - where a barcode was scanned in - it was processed and then the field was cleared and the next one could then be scanned in. The operator was not at the client (its a bluetooth scanner).

My solution and I admit was a bit nasty was to use a NotesTimer - whereby the field contents were checked to see if it was empty or if it had the same value as previously. To make sure I didn't catch a partial scan I did the check twice and if the value did not change then I processed it.

So after all that - couldn't you use a Javascript timer and follow the same logic?

0
votes

Please forgive me if I'm missing something here, but it seems to me you have set up an unnecessarily complex design.

Why is CSJS or SSJS even needed?

Meaning, have you tried just some simple markup and a bean to do the processing?

Example Markup:

<xp:panel
id="panelbarcode">
<xp:inputText
    id="barcode1"
    value="#{MyBean.barcode}">
    <xp:eventHandler
        event="onchange"
        submit="true"
        refreshMode="partial"
        refreshId="panelbarcode" />
</xp:inputText>
</xp:panel>

Example Bean Code:

public class MyBean implements Serializable {

private static final long serialVersionUID = 8541L;

// Zero-Argument Constructor
public MyBean() {}

public String getBarcode() {
    // give out a barcode here, 
    // or return "" for an empty barcode
    return "";
} 

public void setBarCode(String barcode) { 
    // process the scanned in barcode 
} 
}

(edited to get rid of stupid "onClick" event)