B4J Question [ABMaterial] Creating An Autocomplet/Typeahead search control?

keirS

Well-Known Member
Licensed User
I am newcomer to ABMaterial. I have previously used ASP.NET for Web App's. What I am looking to achieve is an autocomplete component that loads results from a database on the server side. For a small data set I could do this with ABMInput and use the AddAutoComplete. However i am not dealing with a small data set (100K + records)

The functionality I am looking to achieve is the user enters the first letter of the search and a function is called on the ABMPage that gets the value of the control, runs a query on the DB and returns JSON to the calling Javascript to generate the list for the autocomplete. This JQuery control: http://www.runningcoder.org/jquerytypeahead/demo/ is the sort of functionality I am looking to achieve. In particular the User V1 demo.

I pretty much understand how this sort of thing works using ASP.NET and AJAX but I can't really work out how this would work in ABM. Is there a JavaScript function in core.4.30.min.js that can call a B4J function in an ABMPage? If not how would be best to approach this?
 

Harris

Expert
Licensed User
If the search is expected to be used often on the page, load the contents of the table in Connect() into list, map, whatever - and use it from there. That way you won't have to go back to the server on every new character.
My 2 pesos...
 

OliverA

Expert
Licensed User
b4j_raiseEvent(methodNameToCallInB4J, JSONStructureThatBecomesAMapParameterForTheMethodCalled)

Two ways calling your method from JavaScript
1) Just call your method directly
b4j_raiseEvent('call_myownmethod', {"parameter1": "Hello World", "parameter2" : 42})
2) Go through ABM's Page_ParseEvent method
b4j_raiseEvent('Page_ParseEvent', {"eventname" : "call_myownmethod", "eventparams" : "parameter1,parameter2", "parameter1": "Hello World", "parameter2" : 42})

In ABM
B4X:
Public Sub call_myownmethod(Params as Map)
   Log(Params.Get("parameter1")
   Log(Params.Get("parameter2")
End Sub
Edit: Typo in JavaScript (missing ending parenthesis)
 

keirS

Well-Known Member
Licensed User
I am struggling with this. I have a BJS class with inline Javascript in it.
B4X:
'Class module
Sub Class_Globals
    ' use Public or Dim if you want to share this variable over ALL B4JS classes
    ' use Private if only within this class
    Public ABM As ABMaterial 'ignore, just to access the constants
    Public Page As ABMPage 'ignore, just to be able to run ABMPage B4JS functions
 
 
End Sub
Public Sub Initalize
    InitializeB4JS
End Sub
'Initializes the object. You can NOT add parameters to this method.
'MUST be called InitializeB4JS is automatically called when using this class
Public Sub InitializeB4JS
 
End Sub
#if JAVASCRIPT
function bindkeyupToB4J(htmlElement, b4jSub) {
      $(htmlElement).keyup(function() {
          var elementValue = $(htmlElement).val();
          b4j_raiseEvent(b4jSub, {"inputValue": elementValue})
          });
 }
        
#End If

Then in my page I have.

B4X:
Sub mykepress(M As Map)
    Log("M")
End Sub

public Sub ConnectPage()        
     Dim kp As testkeypress ' B4JS Class
     testkeypress.Initialize
  
    '    connecting the navigation bar
    ' ABMShared.ConnectNavigationBar(page)
    Dim testinput As ABMInput
    testinput.Initialize(page, "testinput", ABM.INPUT_TEXT, "test", False, "input")
    page.Cell(1,2).AddComponent(testinput)
  
 
    page.B4JSRunInlineJavascriptMethod("bindkeyupToB4J",Array("#testinput","mykeypress")) ' run JScript
 
 
    ' refresh the page
    page.Refresh
 
    ' Tell the browser we finished loading
    page.FinishedLoading
    ' restoring the navigation bar position
    page.RestoreNavigationBarPosition
End Sub
Which doesn't work because my JS function is not included in the Javascript generated for the page or in the page itself. So how do I get ABMaterial to include the Javascript?
 
Last edited:

Anser

Well-Known Member
Licensed User
doesn't work because my JS function is not included in the Javascript generated for the page or in the page itself. . So how do I get ABMaterial to include the Javascript?
How about adding the follwing line in the Sub BuildPage()
B4X:
page.AddExtraJavaScriptFile()
Its just an idea
 

alwaysbusy

Expert
Licensed User
my JS function is not included in the Javascript generated
When I first tried your example, I had the same. I then created a new ABM B4JS class (Project - Add New Module - Class Module - ABM B4JS, copied your code in it at then it did generate. I honestly have no idea why it didn't the first time.

Couple of notes:
1. First, do not add an initialize method in the B4JS class. My B4JSBinder class looks like this:
B4X:
'Class module
Sub Class_Globals
   ' use Public or Dim if you want to share this variable over ALL B4JS classes
   ' use Private if only within this class
   Public ABM As ABMaterial 'ignore, just to access the constants
   Public Page As ABMPage 'ignore, just to be able to run ABMPage B4JS functions

End Sub

'Initializes the object. You can NOT add parameters to this method.
'MUST be called InitializeB4JS is automatically called when using this class
Public Sub InitializeB4JS
End Sub

#if JAVASCRIPT
function bindkeyupToB4J(htmlElement, b4jSub) {
      $(htmlElement).keyup(function() {
          var elementValue = $(htmlElement).val();
          b4j_raiseEvent(b4jSub, {"inputValue": elementValue})
          });
 }       
#End If
2. You must run the page.B4JSRunInlineJavascriptMethod AFTER page.Refresh. If you run it before, the Input is not yet added to the page so the javascript method does nothing. Also use .ToLowerCase as the generated html/js IDs will be lowercased too.
B4X:
   ...
   Dim InpLat1 As ABMInput
   InpLat1.Initialize(page, "inpLat1", ABM.INPUT_TEXT, "From Latitude", False , "input")
   page.Cell(3,1).AddComponent(InpLat1)

   ' refresh the page
   page.Refresh
   ' Tell the browser we finished loading
   page.FinishedLoading
   ' restoring the navigation bar position
   page.RestoreNavigationBarPosition
   
   ' now you can run this method as the InpLat1 component is added   
   page.B4JSRunInlineJavascriptMethod("bindkeyupToB4J", Array("#inpLat1".ToLowerCase, "inpLat1_MyKeyUp".ToLowerCase))
End Sub
Now I receive on my page in Sub InpLat1_MyKeyUp(m As Map):
B4X:
inputValue: 1
inputValue: 12
inputValue: 123
inputValue: 1234
inputValue: 12345
inputValue: 123456
inputValue: 1234567
inputValue: 12345678
inputValue: 123456789
Note this is just to demonstrate how B4JS works, but with just ABM, you can archive the same using the Changed event. By default this event is off (because going back and forth to the server for each key press on the browser side is bad: it will stress your server traffic enormously). But if you really, really, really want it, you can activate it with:
B4X:
Dim InpLat1 As ABMInput
InpLat1.Initialize(page, "inpLat1", ABM.INPUT_TEXT, "From Latitude", False , "input")
InpLat1.RaiseChangedEvent = True ' <-------------------
page.Cell(3,1).AddComponent(InpLat1)
...
Sub InpLat1_Changed(value As String)
   Log(value)
End Sub
Alain
 

keirS

Well-Known Member
Licensed User
Note this is just to demonstrate how B4JS works, but with just ABM, you can archive the same using the Changed event. By default this event is off (because going back and forth to the server for each key press on the browser side is bad: it will stress your server traffic enormously). But if you really, really, really want it, you can activate it with:
B4X:
Dim InpLat1 As ABMInput
InpLat1.Initialize(page, "inpLat1", ABM.INPUT_TEXT, "From Latitude", False , "input")
InpLat1.RaiseChangedEvent = True ' <-------------------
page.Cell(3,1).AddComponent(InpLat1)
...
Sub InpLat1_Changed(value As String)
   Log(value)
End Sub
Alain
I only need to capture the first key press. What I am trying to do is limit the size of the data downloaded by the client. If I have DB table with 100,000 records in and I have an even distribution of entries starting with A-Z then I only need to push 4000 records to client instead of pushing the whole lot. What I eventually hope to do is cache the data on the client although that's rather getting ahead of myself.
 
Top