BANano 4.50 released and Anywhere Software drops a bomb!

“A very exiting week it is” would Yoda say.

The latest version of BANano introduces the BANanoServer.b4xlib library, which is an easy to use wrapper around jServer, the same B4J lib ABMaterial uses. One can now use BANano to make stand-alone Web sites, but also interfaces for e.g. IoT and Raspberry Pi (when using the BANanoServer lib: as this is a normal B4J app, it can use all B4J libs on the server side) and build complex Web Apps.

NOTE: BANano still works without the BANAnoServer Library as it did before in case you want to use another backend like PHP, but only having to program in one language has huge advantages and is what RAD is al about. :)

When you will look at the code of BANanoServer, ABM users will recognize the powerful Caching system, Root, Websocket support and HTTP/2 filters. In BANano, all that code is removed from your own projects view (but still accessible as it is an open source .b4xlib).

Some ‘easy-to-use’ methods to communicate between the browser and the server are added (to call methods from each other and to exchange files).

More info and download BANano for free here: BANano Download

But the biggest news this week must have been the BOMB Erel from Anywhere Software dropped: B4A, the tool to make native Android apps, is from now on free to use for everyone!

From his announcement a couple of weeks ago:

I’m very proud in the continuing progress of B4X tools. I’m also very proud to be part of this amazing developers community.

In the last couple of years, I have a growing feeling that B4X tools do not realize their potential. While there are all kinds of development tools, B4X offers a unique set of features: simple, powerful, mature, great community, RAD, native, cross platform and more.
B4X tools are an excellent choice for a wide range of real-world use cases.

So, the question is how to make B4X more popular? Obviously, it is not a simple nor a short-term task.
A clear growth barrier is the fact that unlike most development tools today, B4A and B4i are not free. This wasn’t the case 10 years ago.

The big announcement today is that B4A will become free in a few weeks. The framework – set of internal libraries, will be open sourced.

We will accept contributions for B4A like currently done with B4J.
We’ve also secured funds from a US investor who shares my vision of making B4X a popular development tool. These resources will allow us to further expand.

Two huge points were raised here: for one, the tool that started it all (B4A) to make native Android apps, is now 100% free (download it here)! It is clear that nowadays paying for a development platform is so 2009. Some development vendors may exist for 20+ years, but haven’t grown accordingly and seem to be stuck in overprotecting their (outdated?) technology.  Clearly, Erel is not one of them and is thinking on how to expand his tools into the next decade.

But the second part may be even more exciting: a private US investor has shown interest in the potential of B4X and shares Erel view of the future.  This opens a whole new world to the development tool and allows to think a lot bigger. He can expand his team not only with new developers, but even more important, surround himself with professionals to market his brand and let the world know of the big impact his technology can have on any software developer.

Just read up on the forum (100K+ members) and one quickly realizes the brought spectrum of apps that can be written with B4X. It is used by big corporations like NASA, IBM and Bosch to name a few, but also by small companies and citizen developers. I for one use it daily in my day job at OneTwo and we feel so secure with the tools we use it as the foundation of all our software.  Very rarely one comes across a tool where the developers are so close to its users and actually listens to their concerns and wishes.  I remember having found a bug (yeah, you really have to go deep to find one) and after reporting it to the Anywhere Software team, 10 minutes(!) later, an update was released.  This has never been seen and blocked me zero time in my own development.

I am a big fan of the B4X suite. It allowed me to write ABMaterial and BANano, two libraries to make modern Websites and Webapps.  Something I can’t see me do in any other tool, at least not with such ease and speed. And it is fun, really fun to work with too. 🙂

So I think Yoda would concur: ‘The future, bright it is!’


[B4X] BANano 2.25 released



1. GetViewFromLayoutArray() and GetAllViewsFromLayoutArray() has now an extra first parameter to allow adding the B4J class where the layout was loaded.

2. New params to config the paths for a build:


3. BANanoSkeletonCSS added some properties.  You will need to copy the new Library to your Additional Libraries folder if you have already used it.   The CSS file was also changed.

4. New example Website with multiple pages.  I made two versions, one using a Router, one without a Router.

IMPORTANT: they need to run on a real server (or e.g. the Chrome Server plugin!)

It is a real world example I wrote for a friend of mine who owns a B&B.


  •  multi-pages
  •  multilingual supports
  •  browsers Back/Forward buttons support.
  •  floating navigation bar

It is ridiculous how little programming I had to do to make his site. Finished the whole thing in a morning. 🙂

Both examples are heavily commented.

5. The following JavaScript DOM objects have been wrapped:

  • BANanoWindow
  • BANanoHistory
  • BANanoLocation
  • BANanoGeoLocation/BANanoGeoPosition (https only)
  • BANanoConsole
  • BANanoNavigator
  • BANanoScreen

Some are used in the examples in (4)

6. BANanoObject has been extended with:

  • AddEventListener
  • ClientLeft/Top/Width/Height
  • OffsetLeft/Top/Width/Height
  • ScrollLeft/Top/Width/Height

7. Other transpiler fixes.




Click here to Donation and support BANano & ABMaterial

[B4X] BANano 2.19 released


1. Fix for bug in release mode for #if CSS commenting out all generated Javascript after it.

2. Support for #ExcludeFromLibrary. Modules having this tag set to true will not be compiled into the BANano library.

3. Fix for bug ‘OR’ between two String functions:

e.g. previously failed:

Dim myString As String = "Foo"
If myString.ToLowerCase.Contains("foo") Or myString.ToLowerCase.Contains("bar") Then
End If

4. Fix for List AddAll(), AddAllTo()

5. BANanoObject.Initialize2() second param can now also be an Array. This makes it possible to do this:

In Javascript:

var myGraph = new Dygraph(div, data);


DygraphObject.Initialize2("Dygraph", Array(div, data))

6. Support for BANanoPromise. See this tutorial:

7. 3 shortcut methods on the BANanoElement:


All three are BANanoObject methods, now also available on BANanoElement as shortcuts.

So you can now do:

Dim UploadedFiles() As String = BANano.GetElement("#fu").GetField("files").Result

instead of having to do (both still work):

Dim UploadedFiles() As String = BANano.GetElement("#fu").ToObject.GetField("files").Result

I hope this will help confused users who do not always understand when to switch from BANanoElement to BANanoObject.



Click here to Donation and support BANano & ABMaterial


[B4X] BANano 2.15 released


1. Support for B4J Type e.g. Type Person(Name As String, Properties As Map, Scores(10) As Int)

2. Support for NumberFormat and NumberFormat2

3. BANano.Sleep() is now just B4Js normal Sleep(). BANano.Sleep() will be depreciated in the future.

4. BANano.GetType() and BANano.TypeOf are now just B4Js normal GetType(). Both BAnano functions will be depreciated in the future.

5. BANanoSQL new method .ExecuteCallBack(). See for more info:

6. BANanoSkeleton added SKRange, SKRadio, SKSwitch and SKNaviagationBar.
All views now have a Visibility property.

– contains new files. If you use it already in a project, you must copy the new files in your projects Files folder.
– Open all your layouts and save them so the new property Visible will be saved in the layout.

7. Bug fixes in the Transpiler




Click here to Donation and support BANano & ABMaterial


[B4X] BANano 1.21 Christmas gift!


BANano is a new B4J library to create one-page websites/webapps with (offline) Progressive Web App support. Unlike its big brother ABMaterial, BANano does not rely on any particular framework like Materialize CSS. You will have to write that part yourself, but on the other hand, you have the choice to pick which one.

Why a second framework? Well, from the start ABMaterial was build with a back server in mind. B4J has great support to setup a very powerful jServer to handle web requests. Which does mean all intelligence is at the server side and you have the full power of B4J to do whatever you want (secure database access, serial communication, cache control etc). With B4JS, some of this intelligence could be transferred to the browser side, but the app still needs internet/intranet access so this is as far as it could go.

BANano is a different animal. It can use a Service Worker to ‘install’ the web app in the browser, so it can also work when the user is offline. While ABMaterial builds the page from the server side, BANano builds it from the browser side. This means EVERYTHING you write in B4J is transpiled to JavaScript, HTML and CSS.

But with great power comes great responsibility! Unlike ABMaterial, knowledge of HTML, CSS and to some extend JavaScript is needed to build BANano apps. It makes excellent use of B4X’s SmartStrings to create the HTML part of the app. BANano gives you a set of tools to write your own wrapper around any framework (MiniCSS, Skeleton, Spectre, Bootstrap, …), which then can be easily used to quickly build webapps/websites.

v1.21+: Abstract Designer support:

It is a rather simple and elegant system I think:

1. You set BANanoDesignElement elements in the designer (Add View, Custom view).

Only these properties apply:

2. Each has a Build event where you can do your specific html stuff

3. You load a layout with BANano.LoadLayout. At this time, the bjl file is parsed (as B4J custom components do not support a different parent than main yet, I had to write an alogorithm to find the parent myself) and translated to _Build commands (you will see it in the DemoUI library file).

Sub Class_Globals
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize

End Sub

Sub Div_Build (el As BANanoDesignElement)
<div id="${el.Name.ToLowerCase}" class="${el.Classes}"></div>

End Sub

Sub Row_Build (el As BANanoDesignElement)$"
<div id="${el.Name.ToLowerCase}" class="${el.Classes}"></div>

End Sub

Sub Column_Build (el As BANanoDesignElement)$"
<div id="${el.Name.ToLowerCase}" class="${el.Classes}"></div>

End Sub

' extra: the level of h;the text
Sub H_Build (el As BANanoDesignElement)$"<h${el.Extra(0)} id="${el.Name.ToLowerCase}" class="${el.Classes}">${el.Extra(1)}</h${el.Extra(0)}>"$)
End Sub

Sub P_Build (el As BANanoDesignElement)$"

End Sub

Sub Button_Build (el As BANanoDesignElement)
   Dim btn() As BANanoElement =$"<button id="${el.Name.ToLowerCase}" class="${el.Classes}">${el.Extra(0)}</button>"$,"").Children("#" & el.Name.ToLowerCase)
   ' defining events is very simple. Note that it has to be run AFTER adding it to the HTML DOM!
   ' eventName must be lowercase!
   btn(0).HandleEvents("click", el.ModuleEventHandler, el.Name.ToLowerCase & "_clicked")
End Sub

' extra: image source;alt text
Sub Image_Build (el As BANanoDesignElement)$"<img id="${el.Name.ToLowerCase}" src="${el.Extra(0)}" alt="${el.Extra(1)}"/>"$)
End Sub

Using this in a real app is now very simple:

Sub Process_Globals
   Private BANano As BANano 'ignore
   Private Builder As BANanoMiniCSSBuilder '<-------- defining our BANano Library
End Sub

Sub AppStart (Form1 As Form, Args() As String)

   ' you can change some output params here
   BANano.Initialize("BANano", "BANanoMiniCSS",12)
   BANano.HTML_NAME = "banano.html"

   BANano.Header.Title="BANano MiniCSS"
   BANano.ExternalTestConnectionServer = ""

   ' start the build

End Sub

'Return true to allow the default exceptions handler to handle the uncaught exception.
Sub Application_Error (Error As Exception, StackTrace As String) As Boolean
   Return True
End Sub

Sub BANano_Ready()
   Builder.Initialize '<------- initialize the library

   BANano.LoadLayout("#body", "Layout1", Builder, Me) '<--- load the layout
End Sub

Sub MyButton_Clicked(event As BANanoEvent)
   Log("Clicked on " & event.ID)    
   BANano.LoadLayout("#r3c2", "SubLayout1", Builder, Me) ' <---- load a sub layout
End Sub

Builder is the library (the one who will handle the Build() events)
Me (the one who will handle e.g. the MyButton_Clicked event: so like all other events except Build)

An example project using the MiniCSS javascript library is included in the zip (both the library and a demo project using it) in the DemoUi folder.

Couple of important notes:
1. Both the library and App must be a B4J UI project, not a console project
2. When you now run BuildAsLibrary, the assets in the Files folder will be zipped to, next to the .js and .dependsOn files.

I think this is cool! Very happy with it…

DOWNLOAD: FREE library and some basic demo projects

I wish you all a fantastic year-end end and a very productive and happy new 2019!


Click here to Donation and support BANano & ABMaterial

B4X: ABMaterial and using a CDN


Using a CDN (Content Delivery Network) for your WebApp can be very beneficial:

The Benefits of using a CDN:

  • Different domains. Browsers limit the number of concurrent connections (file downloads) to a single domain. …
  • Files may be pre-cached. …
  • High-capacity infrastructures. … (1200 world-wide)
  • Distributed data centers. …
  • Built-in version control. …
  • Usage analytics. …
  • Boosts performance and saves money.

ABM can already use a CDN for its own framework using the ABM.ActivateUseCDN(“DONATOR_KEY”, ““), but from version 4.35 on, ABM will support Cloudinary as a build-in CDN system for your own WebApps.

Cloudinary has a great free plan which can cover a great deal of the webapps you make without any additional cost:

  • Total images and videos: 300,000
  • Managed storage: 10 GB
  • Monthly net viewing bandwidth: 20 GB

In ABM, it can be used for two things:

1. Automatic upload/update of the generated CSS/JS files (the ones you find next to each index.html).
2. Your own assets, like images, videos or outer ‘RAW’ files (xls, doc, …)

Creating a free account on Cloudinary is pretty straight forward. You make a login and give your ‘cloud’ a name. You will then be able to get your API_KEY and API_SECRET from the management console:


1. Automatic upload/update when starting the .jar file.

Activate it in the main class:

' just some clear consts to explain the parameters
Dim SHOW_DEBUG As Boolean = True
Dim DO_NOT_SHOW_DEBUG As Boolean = False
Dim CLEAR As Boolean = True
Dim DO_NOT_CLEAR As Boolean = False
#If RELEASE      
#end if
' this is for 2, your own assets

What will this code do?
It will create a folder structure in you Cloudinary cloud:


Every time you start your jar, both these folders will be emptied (so do not put anything else in it!) and the most recent generated .js/.css files will be uploaded. Note: depending on the size of your app, this can take quite some time. e.g. for the demo app it takes about 3 minutes.

There is nothing more you have to do, ABM will have changed all the calls in the HTML from your local drive to the CDN.

2. Your own Assests:

As you can see in (1), we pre-load all the urls of the images/videos and other files. That way it is pretty simple to use the e.g. if we want to use an image using the ABM.CloudinaryGet() command:

Dim img4 As ABMImage
img4.Initialize(page, "img4", ABM.CloudinaryGet("demo/images/sample-1"), 1)

As parameter it takes the ‘publicId’ of the asset. In my case my APPNAME was demo, I created a folder images in it in the Cloudinary console and uploaded a file called sample-1.jpg so the public id becomes:


Note: I’ve noticed when uploading images via the cloudinary console, the public id cuts of the file extension.

The result of the ABM.CloudinaryGet command will be something like:


It is very easy to upload and organize you assets through the Cloudinary console app:


but sometimes you will want to upload it through B4X code.

So the following API methods do exist:

' using these 3 methods will automatically add them the the internal Cloudinary map
' so you can use them with the ABM.CloudinaryGet() method without reloading.
' The next time you restart your .jar, they will also be loaded.
ABM.CloudinaryUploadImage("publicId", fullFilePath)
ABM.CloudinaryUploadVideo("publicId", fullFilePath)
ABM.CloudinaryUploadRawFile("publicId", fullFilePath)



For the last 3 methods, you use a ‘prefix’ to delete files in bulk:

e.g. if I want to delete all images from my images folder, I would do:


Cloudinary has a wide range of API methods (e.g. for image manipulation etc), and maybe I will expand the B4X API to support it too, but for the moment using it as a CDN only, it suffices.

Note that this is a Donator only feature. ABM 4.35 will be available in a couple of weeks.

This concludes the tutorial.


Click here to Donation and support ABMaterial

B4X: ABMaterials B4JS – 07 The UI Connection (part 2)


This is the second part of the UI (ABMaterial) Connection. I wanted to do this one before releasing ABM 4.25 because it contains some important concepts on defining the events for B4JS in an ABMPage.

I’ve created a B4JS Demo that includes the source code for all the tutorials (so far). It will be included in the download zip.

One of the major advantages of B4JS is that you can check a lot of the users input before heading to the server. And because we have some events (like KeyDown, KeyUp) that we do not have on the server side, we can even do some ‘masking’.

This example makes such a mask for a credit card (every 4 chars a space is inserted and a check is done). It is not finished (e.g. it does not take into account if the user puts its cursor in the middle), but it shows some major concepts of B4JS.


Lets dive into the B4X code:

The B4JS class B4JSInputChecker:

'Class module
Sub Class_Globals
   Private Page As ABMPage 'ignore, just to be able to run ABMPage functions
   Private ToastID As Int
   Private ABM As ABMaterial 'ignore, just to be able to use ABM constants
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub InitializeB4JS

End Sub

public Sub InputDown(Key As String) As Boolean
   'Log("Down key: " &amp; Key)
   Dim cardInp As ABMInput 'ignore that it is not initialized, you can't initialize one in B4JS anyway
   cardInp.B4JSUniqueKey = "cardInp001"
   Dim CardNumber As String = cardInp.B4JSText
   If Key = "Backspace" Then
       If CardNumber.SubString2(0,CardNumber.Length-1).EndsWith(" ") Then
           CardNumber = CardNumber.SubString2(0,CardNumber.Length - 2)
           cardInp.B4JSText = CardNumber
           CheckCard(CardNumber.Replace(" ", ""))
           ' consume the event
           Return True
       End If
       ' let the Keydown do its thing
       Return False
   End If

   If CardNumber.Length = 19 Then
       CheckCard(CardNumber.Replace(" ", ""))
       Return True
   End If

   If Not(IsNumber(Key)) Then
       ToastID = ToastID + 1
       Page.B4JSShowToast("InputToast" &amp; ToastID, "red", "Please only enter numbers!", 3000, False)
       ' consume the event
       Return True
   End If

   ' let the Keydown do its thing
   Return False
End Sub

public Sub InputUp(KeyCode As Int) As Boolean
   'Log("Up keycode: " &amp; KeyCode)

   Dim cardInp As ABMInput 'ignore that it is not initialized, you can't initialize one in B4JS anyway
   cardInp.B4JSUniqueKey = "cardInp001"
   Dim CardNumber As String = cardInp.B4JSText

   ' we down't want to raise it for the combination keys
   ' AltGraph, Shift, Ctrl, Alt
   Select Case KeyCode
       Case 16, 17, 18
           ' consume the event
           Return True
       Case 8 ' and for the backspace, jump out too: we do not want to re-append the space
           ' consume the event
           CheckCard(CardNumber.Replace(" ", ""))
           Return True
   End Select

   Select Case CardNumber.Length
       Case 4,9,14
           cardInp.B4JSText = CardNumber &amp; " "
   End Select

   'a valid 4012 8888 8888 1881
   CheckCard(CardNumber.Replace(" ", ""))

   ' consume the event
   Return True
End Sub

public Sub CheckCard(CardNumber As String)
   'a valid 4012 8888 8888 1881
   Dim isValid As Boolean = Page.B4JSRunInlineJavascriptMethod("validateCCNum", Array As Object(CardNumber.Replace(" ", "")))
   Dim btnCheck As ABMButton 'ignore
   btnCheck.B4JSUniqueKey = "btnCheck"
   ' for our setButtonCSS method we do need the real ID of the button.  We can get this with the Page.B4JSGetComponentIDFromUniqueID() method!
   Dim ID As String = Page.B4JSGetComponentIDFromUniqueID("btnCheck")
   If isValid Then
       btnCheck.B4JSEnabled = True
       Page.B4JSRunInlineJavascriptMethod("setButtonCSS", Array As String(ID, "background-color: #4caf50 !important"))
       btnCheck.B4JSEnabled = False
       Page.B4JSRunInlineJavascriptMethod("setButtonCSS", Array As String(ID, "background-color: #F44336 !important"))
   End If
End Sub

' every method you want to call with a B4JSOn... call MUST return a boolean
public Sub InputGotFocus() As Boolean
   ToastID = ToastID + 1
   Page.B4JSShowToast("InputToast" &amp; ToastID, "red", "You are entering the Credit Card", 3000, False)
   ' consume the event
   Return True
End Sub

' every method you want to call with a B4JSOn... call MUST return a boolean
public Sub InputLostFocus() As Boolean
   ToastID = ToastID + 1
   Page.B4JSShowToast("InputToast" &amp; ToastID, "red", "Leaving the Credit Card field...", 3000, False)
   ' consume the event
   Return True
End Sub

' every method you want to call with a B4JSOn... call MUST return a boolean
public Sub CheckInput() As Boolean
   Dim switch1 As ABMSwitch  'ignore that it is not initialized, you can't initialize one in B4JS anyway
   switch1.B4JSUniqueKey = "switch001"

   Dim HasError As Boolean = False

   ToastID = ToastID + 1
   If Not(switch1.B4JSState) Then
       Page.B4JSShowToast("SwitchToast" &amp; ToastID, "green", "Please put the Switch to ON!", 3000, False)
       HasError = True
   End If

   ToastID = ToastID + 1
   Dim CheckBox1 As ABMCheckbox 'ignore that it is not initialized, you can't initialize one in B4JS anyway
   CheckBox1.B4JSUniqueKey = "CheckBox001"
   If Not(CheckBox1.B4JSState) Then
       Page.B4JSShowToast("CheckboxToast" &amp; ToastID, "green", "Please check the Checkbox!", 3000, False)
       HasError = True
   End If

   If HasError = False Then
       ToastID = ToastID + 1
       Page.B4JSShowToast("CheckboxInput" &amp; ToastID, "green", "All looks OK, well done!", 3000, False)
   End If
   ' consume the event
   Return True
End Sub

' every method you want to call with a B4JSOn... call MUST return a boolean
public Sub SwitchClick() As Boolean
   Dim switch1 As ABMSwitch  'ignore that it is not initialized, you can't initialize one in B4JS anyway
   switch1.B4JSUniqueKey = "switch001"

   ToastID = ToastID + 1
   If switch1.B4JSState Then
       Page.B4JSShowToast("SwitchToast" &amp; ToastID, "green", "Switch is ON", 3000, False)
       Page.B4JSShowToast("SwitchToast" &amp; ToastID, "green", "Switch is OFF", 3000, False)
   End If
   ' consume the event
   Return True
End Sub

' every method you want to call with a B4JSOn... call MUST return a boolean
public Sub CheckBoxClick() As Boolean
   Dim CheckBox1 As ABMCheckbox 'ignore that it is not initialized, you can't initialize one in B4JS anyway
   CheckBox1.B4JSUniqueKey = "CheckBox001"
   ToastID = ToastID + 1
   If CheckBox1.B4JSState Then
       Page.B4JSShowToast("CheckboxToast" &amp; ToastID, "green", "Checkbox ON", 3000, False)
       Page.B4JSShowToast("CheckboxToast" &amp; ToastID, "red", "Checkbox OFF", 3000, False)
   End If
   ' consume the event
   Return True
End Sub

' every method you want to call with a B4JSOn... call MUST return a boolean
public Sub RadioClick() As Boolean
   Dim Radio1 As ABMRadioGroup 'ignore that it is not initialized, you can't initialize one in B4JS anyway
   Radio1.B4JSUniqueKey = "Radio001"
   ToastID = ToastID + 1
   Page.B4JSShowToast("RadioToast" &amp; ToastID, "green", "Active Radio = " &amp;  Radio1.B4JSGetActive, 3000, False)
   ' example of setting the active radio
   'If Radio1.B4JSGetActive = 0 Then
   '   Radio1.B4JSSetActive(2)
   'End If
   Return True
End Sub

public Sub RangeOnChange(start As String, Stop As String, ConsumeEvent As Boolean) As Boolean
   Log("B4JS Start: " &amp; start)
   Log("B4JS Stop: " &amp; Stop)
   Return ConsumeEvent
End Sub

function setButtonCSS(id, val) {
   $('#' + id).attr('style', val);
#End If

And creating the components in ConnectPage():

public Sub ConnectPage()
   '   connecting the navigation bar

   page.Cell(1,1).AddComponent(ABMShared.BuildParagraph(page,"par1","This demo is practical example of B4JS. It uses the B4JSInputChecker B4JS class.") )

   ' input
   Dim cardInp As ABMInput
   cardInp.Initialize(page, "inp1", ABM.INPUT_TEXT, "Credit Card", False, "")
   cardInp.B4JSUniqueKey = "cardInp001"
   ' special case, it has to pass at least the pressed key (ABM.B4JS_PARAM_INPUTKEY or ABM.B4JS_PARAM_INPUTKEYCODE) to it!
   cardInp.B4JSOnKeyDown("B4JSInputChecker", "InputDown", Array As Object(ABM.B4JS_PARAM_INPUTKEY))
   cardInp.B4JSOnKeyUp("B4JSInputChecker", "InputUp", Array As Object(ABM.B4JS_PARAM_INPUTKEYCODE))
   ' some focus stuff
   cardInp.B4JSOnGotFocus("B4JSInputChecker", "InputGotFocus", Null)
   cardInp.B4JSOnLostFocus("B4JSInputChecker", "InputLostFocus", Null)

   ' switch
   Dim switch1 As ABMSwitch
   switch1.Initialize(page, "switch1", "ON", "OFF", False, "")
   switch1.B4JSUniqueKey = "switch001"
   switch1.B4JSOnClick("B4JSInputChecker", "SwitchClick", Null)

   ' checkbox
   Dim CheckBox1 As ABMCheckbox
   CheckBox1.Initialize(page, "CheckBox1", "B4JS Checkbox", False, "")
   CheckBox1.B4JSUniqueKey = "CheckBox001"
   CheckBox1.B4JSOnClick("B4JSInputChecker", "CheckBoxClick", Null)

   ' radiogroup
   Dim Radio1 As ABMRadioGroup
   Radio1.Initialize(page, "Radio1", "")
   Radio1.B4JSUniqueKey = "Radio001"
   Radio1.AddRadioButton("radio 0", True)
   Radio1.AddRadioButton("radio 1", True)
   Radio1.AddRadioButton("radio 2", True)
   Radio1.B4JSOnClick("B4JSInputChecker", "RadioClick", Null)

   ' range
   Dim range As ABMRange
   range.Initialize(page, "range", 10, 20, 0, 100, 1, "")
   range.B4JSUniqueKey = "range001"
   ' special case, it has to pass at least ABM.B4JS_PARAM_RANGESTART & ABM.B4JS_PARAM_RANGESTOP!

   range.B4JSOnChange("B4JSInputChecker", "RangeOnChange", Array As Object(ABM.B4JS_PARAM_RANGESTART, ABM.B4JS_PARAM_RANGESTOP, True))

   ' button
   Dim btnCheck As ABMButton
   btnCheck.InitializeFlat(page, "btnCheck", "", "", "Check", "red")
   btnCheck.Enabled = False
   btnCheck.B4JSUniqueKey = "btnCheck"
   btnCheck.B4JSOnClick("B4JSInputChecker", "CheckInput", Null)

   ' refresh the page
   ' Tell the browser we finished loading
   ' restoring the navigation bar position
End Sub

As you see, for every component we want to use in a B4JS class, we have to set the B4JSUniqueKey property.

We also define some events (B4JSOn…). You notice some of them have special parameters, e.g. the ABMInputs B4JSOnKeyDown event. This event MUST have at least the parameter ABM.B4JS_PARAM_INPUTKEY or ABM.B4JS_PARAM_INPUTKEYCODE to be able to work.

For the B4JSOnKeyDown event for example, we pass the ABM.B4JS_PARAM_INPUTKEY param. The order of the params is very important!

In our B4JS class, this is our method definition:

public Sub InputDown(Key As String) As Boolean<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;">&#65279;</span>

Here ABM.B4JS_PARAM_INPUTKEY’s value will be put into the Key param.

You can add additional params (if needed) as for example is done in the range.B4JSOnChange event declaration.

This concludes the tutorial. ABM 4.25 is now available for download for all donators in the feedback app!


Click here to Donation and support ABMaterial