B4X: Writing JavaScript with ABMaterials B4JS


After a delay of more of a year working on other ABM stuff, I finally got back on track with B4JS. I will make a series of tutorials explaining how it works. B4JS is part of the ABMaterial library 4.25 which is going to be released next week.


One way to look at B4JS is as some kind of new platform in B4X, that uses a very similar syntax as B4J. At runtime, the B4J source code is transpiled to pure JavaScript. The generated source code can then be used in e.g. an ABMaterial Web App, or maybe even as a base for another JavaScript library.

A typical B4JS class could look like this:

Sub Process_Globals
   Dim myString As String
   Public pubMap As Map

   Dim timer As Timer
   Dim myGlobalName As String = "GlobalAlain"
End Sub
'Initializes the object. You can NOT add parameters.
Public Sub InitializeB4JS()

   Dim myName As String = "Alain"
   ' smartstrings do not support date, time or xml functions
   myString = $"Hello planet "B4X"! This is a test
    from ${myName} and ${myGlobalName}"$
   Log("myString.Contains('planet'): " & myString.Contains("planet"))
   Dim myLocalInt As Int = 15
   myLocalInt = myLocalInt * myString.Length

   LogMe("15 x the length of " & myString & " = " & myLocalInt)

   For i=0 To 50
       myLocalInt = myLocalInt + 2
       Select Case myLocalInt
           Case 20, 40
               pubMap.Put("key" & i, i)
           Case Else
               Log("less than 20")
       End Select

   timer.Initialize("timer", 1000)
   timer.Enabled = True

   Dim sb As StringBuilder
   sb.Append("lijn 1").Append(CRLF).Append("lijn 2")
End Sub
Private Sub Timer_Tick
   timer.Enabled = False
   Log("timer ticking")
   If timer.Enabled = False Then
       timer.Interval = timer.Interval + 1000
       Log("timer new interval: " & timer.Interval)
       timer.Enabled = True
   End If
End Sub

Looks very familiar, no? :)

Having the browser doing some stuff using JavaScript can have some big advantages by relieving some pressure from your server (checking if a form is filled in correctly, or changing the visibility of an ABMContainer if a user clicks a button).

But it also demands a great responsibility from the programmer not to expose to much information to the user. Never, ever write sensitive stuff like SQL, passwords etc in B4JS!

Another advantage is being able to expose some events (like the KeyUp/KeyDown events in an ABMInput field). They are deliberately omitted in ABM, because such events could potentially kill your server. But in Javascript, we could use them e.g. to check if the user could entry numbers, or if fits an email mask.

Having a Timer running on the browser side can also be handy.

First an overview of the syntax B4JS can handle:


Core library
Variable types:









Control Structures:


Smart String Literal:

JSON library


ABMaterial library (as of 2018/03/10, for the latest list check the B4X forum):

EVENTS: B4JSOnClick, B4JSOnMouseEnter, B4JSOnMouseLeave










EVENTS: B4JSOnClick, B4JSOnMouseEnter, B4JSOnMouseLeave



EVENTS: B4JSOnClick, B4JSOnMouseEnter, B4JSOnMouseLeave


In the next following days, I would like to cover the following topics:

01. Getting started
02. Core functions
03. Inline JavaScript
04. Running JavaScript on the server side (mini NodeJS?)
05. JSON & Ajax Calls
06. The UI (ABMaterial) connection


Click here to Donation and support ABMaterial


B4X: Responsive containers in ABMaterial 4.25


Next to the normal responsive page behaviour, in ABMaterial 4.25 for B4J, you will be able to do this with containers too!

Let’s watch it at work first:

This may not sound super special, but it’s a biggie! There are very few frameworks out there that can do this, so I’m very proud of this one.

Nice but that must ask for a lot of control code, no?“, I hear you ask.
Well, here it is:

gridContainer.IsResponsiveContainer = True

Done! 🙂

Just by setting gridContainer.IsResponsiveContainer = True, you just made the ABMContainer device responsive just like a page. This means you can from now on actually create controls that behave differently depending on the device you are on.

For example, setting this grid on a container, the last 5 cells will wrap differently on a phone, tablet or desktop:


I also added some powerful tools to adjust the layout of your WebApp according to the device you are on:

Returns the current page size (phone, tablet, desktop). You can use this method e.g. in ConnectPage to determine the state of the current page size and act accordingly.

public Sub ConnectPage()
   Log("Current page size: " & page.GetCurrentPageSize)
End Sub

An event Page_SizeChanged will return the same value in the ‘current’ parameter if the user changes the window size. (It also returns the ‘previous’ state).

Note that this event is NOT raised at load time! Use Page.GetCurrentPageSize to check that.

Sub Page_SizeChanged(previous As String, current As String)
   Log(previous & " -> " & current)
   Select Case current
       Case "phone"
           gridContainer.MarginLeft = "0px"
       Case "tablet"
           gridContainer.MarginLeft = "210px"
           gridContainer.SetFixedWidth("calc(100% - 210px)")
       Case "desktop"
           gridContainer.MarginLeft = "210px"
           gridContainer.SetFixedWidth("calc(100% - 610px)")
   End Select
End Sub

Containers on a fixed position:
Next to setting a fixed with and height of an ABMContainer, you can now also set a fixed position. This can be handy to create ‘floating’ sidebars for example.

compContainer.SetFixedPosition("0px", "", "56px", "")

Params: left, right, top,bottom

Set a parameter to empty string when you don not want to set it.

These 3 new features used together give you an immense power over how your WebApp should behave depending on the device it is running on, with very little effort.

Another year has passed:

2017 has been a great year for B4X and ABMaterial!

B4X has grown to be a very powerful player in develop land.  The already productive RAD IDE has received a couple of very innovative features to help you even more with your app development. Anywhere Software is always right on top of all the new stuff Android, iOS and all the other supported platforms throw at us.  And being stable and reliable makes this a unique environment. The growth of the forum and the quality of the messages/responses are the real proof of this.  Thanks a lot Erel for this beautiful tool.  It is without a doubt the best cross-platform tool one can use for the moment!

And ABMaterial is lucky to be able to stand on the shoulders of this giant.  ABM has surpassed my biggest expectations this year.  It is great to see a lot of you are using it to make amazing webapps and even can make a living out of it.  This was definitely the ‘year of speed‘.  Very few frameworks can match ABM when it comes to develop and execution time. And there is a lot more in the pipeline for the upcoming months!

Thank you all for following my blog!  You inspire me every day to push the limits of the tools and frameworks I make.

Rest me only to wish you all the very best and I hope to see you back in 2018!


Click here to Donation and support ABMaterial

B4X: Custom Icons in ABMaterial 4.25


A much asked feature has been to integrate custom icons in ABMaterial for B4J. In version 4.25, this will be possible!

Couple of notes first:
1. The already build-in font icons (font awesome, google icons) are a lot faster to load, so consider them first
2. Some basic CSS knowledge will be needed (margins, padding, fill)
3. Custom icons MUST be named starting with abm-

There are three ways to load custom icons in ABM:

1. SVG
These type of icons MUST be loaded in BuildPage. This is because, when used, they link to the SVG tags (contrary to embedded SVG, see further). If used multiple times, it will make your HTML code smaller as the SVG code is only included once in the HTML.

page.AddSVGIcon("abm-si-maki-basketball-11", $"<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" style="width:0;height:0;position:absolute;overflow:hidden;">
     <defs><symbol viewBox="0 0 11 10.54083251953125" aria-labelledby="basi-maki-basketball-11-title" id="abm-si-maki-basketball-11"><title id="basi-maki-basketball-11-title">icon basketball-11</title><path d="M11 1a1 1 0 1 1-2 0 1 1 0 0 1 2 0zM4.5 3a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zm3.89 6.69L6 6.59V4.5h.5a.49.49 0 0 0 .41-.23l1.5-2A.49.49 0 0 0 8.5 2a.5.5 0 0 0-.86-.34L6.25 3.5H2.5a.5.5 0 0 0-.36.16l-2 2.54a.49.49 0 0 0-. 0 0 0 . 0 0 0 .41-.23L2.74 4.5H3v2.09L.61 9.69a.5.5 0 0 0 . 0 0 0 .41-.23L3.94 7h1.12l2.52 3.27A.5.5 0 0 0 8.5 10a.49.49 0 0 0-.11-.3v-.01z"/></symbol> </defs>
   </svg>"$, 24,24,"margin-top: 12px;fill: black")

As you can see, in here we have also to declare a margin-top and a fill color in CSS. The name for our icon is ‘abm-si-maki-basketball-11′.

Icons like this typically have an SVG tag looking like this (hidden, width & height = 0, ALL properties are required!):

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" style="width:0;height:0;position:absolute;overflow:hidden;">...</svg>

10000+ icopns of this type can be found here: https://leungwensen.github.io/svg-icon/

2. Embedded SVG
These type of icons can be loaded at runtime (e.g. in ConnectPage) as the SVG tag code will be included in the html. This also means if you use the same icon multiple times, the full SVG code will be added multiple times too!

page.AddEmbeddedSVGIcon("abm-aardbei", $"<svg xmlns="http://www.w3.org/2000/svg" height="32" width="26" viewBox="0 0 116.76708 149.28999" style="margin-top: 8px;fill: red">
  <defs id="defs3749" />
  <g transform="translate(-129.2905,-77.659)" id="g3744">
  <g id="g3728">
  <path style="stroke:#000000;stroke-miterlimit:10" id="path3726" d="m 243.873,168.241 c -9.344,35.617 -43.232,58.208 -56.131,58.208 -13.155,0 -45.451,-28.357 -56.387,-63.353 -7.225,-23.113 12.081,-42.038 26.423,-42.112 21.474,-0.11 23.813,12.248 32.681,9.449 9.898,-3.125 8.874,-12.884 28.954,-12.606 12.542,0.171 31.997,21.688 24.46,50.414 z" stroke-miterlimit="10" />
  <path style="fill:#ffffff" id="path3730" d="m 156.34,148.723 c 0,5.688 -1.251,10.304 -2.801,10.304 -1.55,0 -2.801,-4.615 -2.801,-10.304 0,-5.689 1.251,-10.3 2.801,-10.3 1.55,0 2.801,4.611 2.801,10.3 z" />
  <path style="fill:#ffffff" id="path3732" d="m 170.818,175.338 c 0,5.688 -1.251,10.296 -2.802,10.296 -1.55,0 -2.802,-4.607 -2.802,-10.296 0,-5.689 1.252,-10.304 2.802,-10.304 1.55,0 2.802,4.615 2.802,10.304 z" />
  <path style="fill:#ffffff" id="path3734" d="m 190.6,203.347 c 0,5.695 -1.251,10.304 -2.801,10.304 -1.55,0 -2.802,-4.608 -2.802,-10.304 0,-5.688 1.251,-10.296 2.802,-10.296 1.549,0 2.801,4.607 2.801,10.296 z" />
  <path style="fill:#ffffff" id="path3736" d="m 211.903,175.338 c 0,5.688 -1.251,10.296 -2.801,10.296 -1.55,0 -2.801,-4.607 -2.801,-10.296 0,-5.689 1.251,-10.304 2.801,-10.304 1.55,0 2.801,4.615 2.801,10.304 z" />
  <path style="fill:#ffffff" id="path3738" d="m 192.462,160.498 c 0,5.688 -1.251,10.303 -2.794,10.303 -1.55,0 -2.802,-4.614 -2.802,-10.303 0,-5.692 1.251,-10.3 2.802,-10.3 1.543,0 2.794,4.608 2.794,10.3 z" />
  <path style="fill:#ffffff" id="path3740" d="m 226.324,148.723 c 0,5.688 -1.252,10.304 -2.802,10.304 -1.55,0 -2.802,-4.615 -2.802,-10.304 0,-5.689 1.251,-10.3 2.802,-10.3 1.55,0 2.802,4.611 2.802,10.3 z" />
  <path id="path3742" d="m 153.539,77.659 24.503,24.531 c 0,0 4.202,2.784 6.954,0.68 4.366,-3.328 32.268,-25.211 32.268,-25.211 l -12.842,21.712 c 0,0 -3.015,4.604 1.878,5.22 4.892,0.618 27.148,-6.854 32.126,19.917 0,0 -2.297,-15.409 -29.325,-11.513 0,0 -9.421,1.721 -14.719,6.841 0,0 -4.714,6.891 -12.138,1.244 0,0 -27.077,-15.366 -46.219,5.646 0,0 13.304,-25.724 34.792,-22.136 z" />

Here too, some CSS has to be used + setting the width & height too.

A typical svg tag looks like this:

<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 1024 1024" style="margin-top: 14px;fill: black">...</svg>

Such svg tags are e.g. created when exporting it from Illustrator.

3. Images
These type of icons are just plain png/jpg images. Make sure you use the appropriate size of the image to load. e.g. if your icon is going to be 32×32, do not load a 1024×1024 image!

page.AddImageIcon("abm-batman", "../images/batman.png", 32, 32, "margin-top: 8px", "Batman")

In this case, width, height and the CSS are part of the load method.

You can find 65000+ of these icons here: https://icons8.com/

Now you can use your loaded icons like you would load font based icons:

Dim lblTest As ABMLabel
lblTest.Initialize(page, "lblTest", "This is some test {IC:#000000}abm-si-maki-basketball-11{/IC} with {IC:#000000}abm-aardbei{/IC} special icons {IC:#000000}abm-gmail{/IC} or {IC:#000000}abm-gmail2{/IC}.", ABM.SIZE_H6, True, "")

Note that the color in the {IC} tag must be set for compatibility reasons, but is ignored.

Another example is in the sidebar menu:

page.NavigationBar.AddSideBarItem("MDL4", "Batmans layer", "abm-batman", "")
page.NavigationBar.AddSideBarItem("MDL5", "From icons8.com", "abm-facebook", "")

Or on the action button:

Dim actionB As ABMActionButton
actionB.Initialize(page, "ActionB", "abm-aardbei-action", "", "")
page.AddActionButton(actionB)<span 				data-mce-type="bookmark" 				id="mce_SELREST_start" 				data-mce-style="overflow:hidden;line-height:0" 				style="overflow:hidden;line-height:0" 			></span>


Click here to Donation and support ABMaterial

B4X: ABMaterial 3.75 Public/4.00 Donators released


ABMaterial Dragonfly (4.00) for B4X is available. Yes, it has a new name! What started as version 3.81 got upgraded to a new major version, especially since it uses a new cache control system.

ABMaterial has always been one of the fastest RADs from its start, now over 2 years ago.  But with 4.00, we’ll take it to a whole new level!

Mindful and I have spend 5 days (and nights) checking out how we could cache ABMaterial to the extreme without losing any of its functionality.  The results are just ridiculous…

Finish times are less than 10% of the time on the second and next loads. So on 3G, on the users next visit(s), the WebApp is almost just as fast as on high speed cable!

And this system not only works with the same page. Once one page has been loaded, all the other pages can take gain of this system. Even when the user revisits your app much later.

Furthermore there is the new debug feature to check how your apps work on different device sizes.  See it at work here:

Add this snippet to you main module, AFTER starting the server:

' in debug mode, start the browser and open the app on all devices (DOES NOT WORK IF EDGE IS YOUR DEFAULT BROWSER!)
ABM.ViewerOpenAllDevices("http://localhost:" & port & "/" & ABMShared.AppName & "/", 100)

' or open a specific device as default (DOES NOT WORK IF EDGE IS YOUR DEFAULT BROWSER!)
'ABM.ViewerOpenDevice("http://localhost:" & port & "/" & ABMShared.AppName & "/", 300, ABM.VIEWER_TABLET)

' or just open de browser, no multiple devices (should work in Edge)
'ABM.ViewerOpen("http://localhost:" & port & "/" & ABMShared.AppName & "/")
#End If

Lots of new theme properties! Check out the demo on how to use  them in the new Dragonfly theme.  As winter is coming and days are getting shorter (at least here in the northern hemisphere), I went for a ‘Night’ theme this time.

ABMaterial DragonFly is now available on 750+ locations worldwide on CDN!  If you use the CDN, you do not need to upload the css/js/fonts folders to your own server anymore!

This means only your own assets (images etc) from the www folder have to be available on your server. De demo running on abmaterial.com for example is working like this.


ABM.ActivateUseCDN("DONATORKEY", "https://cdn.jsdelivr.net/gh/RealAlwaysbusy/ABMaterial@v4.03/")

The over 20 new fixes and fulfilled wishes make ABMaterial 4.00 very stable, just like its ‘parent’ programming language: B4X.  It is a real joy knowing one can depend on a strong, bug free environment.  Erel from Anywhere Software rulez!

Happy programming!


Click here to Donation and support ABMaterial

B4J: Creating a Star Rating component in ABMaterial


Michał asked me in the feedback app to implement some kind of Star Rating component. As this is not so commonly needed, I thought this could be another good example of an ABMCustomComponent. One can ‘hover’ over the stars to set its value or it can be set and retrieved by code.

I show you how to do it in ABMaterial 3.75 first with the new features (released soon), and will then explain what needs to be changed for previous versions.

So let’s get started!

1. We create a new standard class CompStarRating:

'Class module
Sub Class_Globals
Public ABMComp As ABMCustomComponent
Dim myInternalName As String
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(InternalPage As ABMPage, ID As String, radioName As String)
myInternalName = radioName
Dim CSS As String = $"
#half-stars-example .rating-group {
display: inline-flex;
#half-stars-example .rating__icon {
pointer-events: none;
/* This is an important part: we MUST override the Materialize CSS implementation of the the radio button! */
#half-stars-example .rating__input + label:before,
#half-stars-example .rating__input + label:after {
position: absolute !important;
left: -9999px !important;
#half-stars-example .rating__label {
cursor: pointer;
padding: 0 0.1em;
font-size: 2rem;
margin-top: 1rem;
#half-stars-example .rating__label--half {
padding-right: 0;
margin-right: -0.6em;
z-index: 2;
#half-stars-example .rating__icon--star {
color: orange;
#half-stars-example .rating__icon--none {
color: #eee;
#half-stars-example .rating__input--none:checked + .rating__label .rating__icon--none {
color: red;
#half-stars-example .rating__input:checked ~ .rating__label .rating__icon--star {
color: #ddd;
#half-stars-example .rating-group:hover .rating__label .rating__icon--star,
#half-stars-example .rating-group:hover .rating__label--half .rating__icon--star {
color: orange;
#half-stars-example .rating__input:hover ~ .rating__label .rating__icon--star,
#half-stars-example .rating__input:hover ~ .rating__label--half .rating__icon--star {
color: #ddd;
#half-stars-example .rating-group:hover .rating__input--none:not(:hover) + .rating__label .rating__icon--none {
color: #eee;
#half-stars-example .rating__input--none:hover + .rating__label .rating__icon--none {
color: red;
ABMComp.Initialize("ABMComp", Me, InternalPage, ID, CSS)
End Sub

Sub ABMComp_Build(InternalPage As ABMPage, internalID As String) As String
Return $"
<div id="half-stars-example">
<div id="${internalID}" class="rating-group"><input id="rating-0" class="rating__input rating__input--none" checked="checked" name="${myInternalName}" type="radio" value="0" />
<label class="rating__label" title="0 stars" for="rating-0"> </label>
<input id="rating-00" class="rating__input" type="radio" value="-1" />
<label class="rating__label rating__label--half" title="0.5 stars" for="rating-05"><i class="rating__icon rating__icon--star fa fa-star-half"></i></label>
<input id="rating-05" class="rating__input" name="${myInternalName}" type="radio" value="0.5" />
<label class="rating__label" title="1 star" for="rating-10"><i class="rating__icon rating__icon--star fa fa-star"></i></label>
<input id="rating-10" class="rating__input" name="${myInternalName}" type="radio" value="1" />
<label class="rating__label rating__label--half" title="1.5 stars" for="rating-15"><i class="rating__icon rating__icon--star fa fa-star-half"></i></label>
<input id="rating-15" class="rating__input" name="${myInternalName}" type="radio" value="1.5" />
<label class="rating__label" title="2 stars" for="rating-20"><i class="rating__icon rating__icon--star fa fa-star"></i></label>
<input id="rating-20" class="rating__input" name="${myInternalName}" type="radio" value="2" />
<label class="rating__label rating__label--half" title="2.5 stars" for="rating-25"><i class="rating__icon rating__icon--star fa fa-star-half"></i></label>
<input id="rating-25" class="rating__input" name="${myInternalName}" type="radio" value="2.5" />
<label class="rating__label" title="3 stars" for="rating-30"><i class="rating__icon rating__icon--star fa fa-star"></i></label>
<input id="rating-30" class="rating__input" name="${myInternalName}" type="radio" value="3" />
<label class="rating__label rating__label--half" title="3.5 stars" for="rating-35"><i class="rating__icon rating__icon--star fa fa-star-half"></i></label>
<input id="rating-35" class="rating__input" name="${myInternalName}" type="radio" value="3.5" />
<label class="rating__label" title="4 stars" for="rating-40"><i class="rating__icon rating__icon--star fa fa-star"></i></label>
<input id="rating-40" class="rating__input" name="${myInternalName}" type="radio" value="4" />
<label class="rating__label rating__label--half" title="4.5 stars" for="rating-45"><i class="rating__icon rating__icon--star fa fa-star-half"></i></label>
<input id="rating-45" class="rating__input" name="${myInternalName}" type="radio" value="4.5" />
<label class="rating__label" title="5 stars" for="rating-50"><i class="rating__icon rating__icon--star fa fa-star"></i></label>
<input id="rating-50" class="rating__input" name="${myInternalName}" type="radio" value="5" /></div>
End Sub

public Sub GetCurrentRating(InternalPage As ABMPage) As Double
Dim script As String = $"return $('input[name=${myInternalName}]:checked').val();"$
Dim ret As Future = InternalPage.ws.EvalWithResult(script, Null)

Return ret.Value
End Sub

public Sub SetCurrentRating(InternalPage As ABMPage, value As Double) {
Dim script As String = $" $('input[name=${myInternalName}][value="${value}"]').prop('checked', 'checked');"$
InternalPage.ws.Eval(script, Null)
End Sub

' Is useful to run some initalisation script.
Sub ABMComp_FirstRun(InternalPage As ABMPage, internalID As String)
Dim script As String = $""$
InternalPage.ws.Eval(script, Array As Object(ABMComp.ID))
' flush not needed, it's done in the refresh method in the lib
End Sub
' runs when a refresh is called
Sub ABMComp_Refresh(InternalPage As ABMPage, internalID As String)
Dim script As String = $""$
InternalPage.ws.Eval(script, Array As Object(ABMComp.ID))
End Sub
' do the stuff needed when the object is removed
Sub ABMComp_CleanUp(InternalPage As ABMPage, internalID As String)
End Sub

ABMaterial users will notice I have added the CSS directly into the component. By doing so, we do not need to use the page.AddExtraCSSFile method in the Page_Build() method. (you can still do it this way, and in some cases it may be better as this part can be big. And in the final version, compress it!). In ABMaterial before version 3.75, using AddExtraCSSFile is the only way possible.

The only difficult part in the CSS was I had to override Materialize CSS’s implementation of a radio button (the ‘circle’). So we had to get rid of it.

2. Now we can make a variable for our StarComponent in the page Class_Globals, as we want to be able to Set and Get the value:

Dim myRating As CompStarRating

3. In Page_Connect() we add our component and a couple of buttons:

myRating.Initialize(page, "myRating", "rating")

Dim btn As ABMButton
btn.InitializeFlat(page, "btn", "", "", "Get value", "")

Dim btn2 As ABMButton
btn2.InitializeFlat(page, "btn2", "", "", "Set 2.5 stars value", "")

4. And our Get and Set code in the buttons:

Sub btn_Clicked(Target As String)
Dim value As Double = myRating.GetCurrentRating(page)
End Sub

Sub btn2_Clicked(Target As String)
myRating.SetCurrentRating(page, 2.5)
End Sub

That is all! :)

Changes for versions of ABMaterial before 3.75

1. Save the CSS string into a text file (e.g. star.rating.css) and copy it to the /css/custom folder.
2. Remove the CSS param from ABMComp.Initialize(“ABMComp”, Me, InternalPage, ID, CSS)

ABMComp.Initialize("ABMComp", Me, InternalPage, ID) '<-- CSS param deleted

3. In Sub ABMComp_Build(InternalPage As ABMPage, internalID As String) As String remove the InteranlPage param:

Sub ABMComp_Build(internalID As String) As String

4. In Page_Build(), load the css file you created:



Click here to Donation and support ABMaterial

B4J: creating a reCAPTCHA component in ABMaterial

reCAPTCHA is a free service that protects your website from spam and abuse. reCAPTCHA uses an advanced risk analysis engine and adaptive CAPTCHAs to keep automated software from engaging in abusive activities on your site. It does this while letting your valid users pass through with ease.

First, we do have to register a Google API key for our WebApp here.

After registering, we receive our API key:
For this demo, I’m using the developer API key (which works with localhost) so it will show a warning that this is not the real key in red.
NOTE: this code is for ABMaterial 3.75. For earilier versions, see below what needs to be changed in the code.

Create a new class CompReCAPTCHA:

'Class module
Sub Class_Globals
   Public ABMComp As ABMCustomComponent
   Dim mAPIKey As String
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(InternalPage As ABMPage, ID As String, APIKey As String)
   ABMComp.Initialize("ABMComp", Me, InternalPage, ID, "")
   mAPIKey = APIKey
End Sub
Sub ABMComp_Build(InternalPage As ABMPage, internalID As String) As String
   Return $"
<div id="${internalID}render" class="g-recaptcha" data-sitekey="${mAPIKey}"></div>
End Sub
' Is useful to run some initalisation script.
Sub ABMComp_FirstRun(InternalPage As ABMPage, internalID As String)
   Dim script As String = $"grecaptcha.render(
{"sitekey": "${mAPIKey}", "theme": "light"}
) "$
   InternalPage.ws.Eval(script, Array As Object(ABMComp.ID))
   ' flush not needed, it's done in the refresh method in the lib
End Sub

public Sub Reset(InternalPage As ABMPage)
   Dim script As String = $"grecaptcha.reset()"$
   InternalPage.ws.Eval(script, Null)
End Sub

public Sub CheckValidation(InternalPage As ABMPage) As Boolean
   Dim script As String = $"return (grecaptcha && grecaptcha.getResponse().length !== 0);"$
   Dim ret As Future = InternalPage.ws.EvalWithResult(script, Null)

   Return ret.Value
End Sub
' runs when a refresh is called
Sub ABMComp_Refresh(InternalPage As ABMPage, internalID As String)
   Dim script As String = $""$
   InternalPage.ws.Eval(script, Array As Object(ABMComp.ID))
End Sub
' do the stuff needed when the object is removed
Sub ABMComp_CleanUp(InternalPage As ABMPage, internalID As String)
End Sub

In BuildPage(), add the Google api:

page.AddExtraJavaScriptFile("<a class="linkification-ext" title="Linkification: https://www.google.com/recaptcha/api.js" href="https://www.google.com/recaptcha/api.js">https://www.google.com/recaptcha/api.js</a>")

In Class_Globals make a variable:


In ConnectPage create the component (and a couple of buttons):

' note that this is Googles demo API key.  Use your own!
myReCAPTCHA.Initialize(page, "myReCAPTCHA", "6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI")

Dim btn As ABMButton
btn.InitializeFlat(page, "btn", "", "", "Submit", "")

Dim btn2 As ABMButton
btn2.InitializeFlat(page, "btn2", "", "", "Reset", "")

' refresh the page

The code in the buttons to check its validation and to reset the captcha.

Sub btn_Clicked(Target As String)
   Dim bool As Boolean = myReCAPTCHA.CheckValidation(page)
End Sub

Sub btn2_Clicked(Target As String)
End Sub

Et voila, we do have a new reCAPTCHA component at our disposal in ABMaterial!

For ABMaterial pre 3.75, make these changes in the code:

Public Sub Initialize(InternalPage As ABMPage, ID As String, APIKey As String)
   ABMComp.Initialize("ABMComp", Me, InternalPage, ID) ' last param removed
   mAPIKey = APIKey
End Sub
Sub ABMComp_Build( internalID As String) As String ' InternalPage param removed
   Return $"
<div id="${internalID}render" class="g-recaptcha" data-sitekey="${mAPIKey}"></div>
End Sub

Until next Time!


Click here to Donation and support ABMaterial

B4J: Printing/Reporting with upcoming ABMaterial 3.75

A sneak peek on a new feature of ABMaterial for B4J I’m working on: Printing/Reporting.

This is an example of a report in ABMaterial. You build it right into your other pages and just by setting a couple of parameters, you control what will be shown on screen and what will be printed.

The block your see above ‘INVOICE’ will not be printed, just my setting lbl.IsPrintable = false.  What is shown on screen you control by using the normal ABM.VISIBLE_ constants. For example there are some additional notes with a chart that you can’t see here on screen, but will be printed.

You can use almost every ABM component in your report. Good examples are the ABMChart, the ABMChronologyList or even like in this example you can let your client sign the document using an ABMSignaturePad and print it right out.

The ABM components avoid being split over two pages (e.g. an image will be printed on the next page if it does not fit). Browsers let you control very little on how things are printed, but I’ve added some things like ReportFooter.row(3).PrintPageBreak = ABM.PRINT_PAGEBREAK_BEFORE_ALWAYS to give you some control.

Note that the navigation bar is not printed either. All of this is taken care for you automatically by ABMaterial!

Some example pdf prints from the demo app (without making any modification to the source code!). Try printing the same pages with ABMaterial 3.50 or before and you’ll see the difference.


Making the above ‘invoice report’ is really simple and in the spirit of ABMaterial. I’ve created a new component ABMReport (and ABMReportBlock) which you can use just like you would make an ABMCustomComponent.

Relevant source code snippet of the above report (the Build event is where the magic happens, the rest are more helper methods):

Sub Class_Globals
   Dim ABM As ABMaterial
   Dim Report As ABMReport

   ' some CSS to format the 'body' of the report
   Dim ReportCSS As String = $"
   .repheader, .repfooter, .repheaderright, .repfooterright {
     background-color: lightgray;
     font-size: 1.8rem;
   .repheaderright, .repbodyright, .repfooterright {
     text-align: right;
   .repbody, .repbodyright {
     font-size: 1.5rem;

   @media only print {
     html { font-size: 60%}

   Dim mSQL As SQL 'ignore
   Dim mInvoiceID As Int 'ignore
End Sub

Public Sub Initialize(InternalPage As ABMPage, ID As String)
   Report.Initialize("Report", Me, InternalPage, ID, ReportCSS, "")
End Sub

Sub Report_Build(InternalPage As ABMPage, internalID As String)
   ' the report header
   Dim ReportHeader As ABMContainer = Report.Header.InitializeAsContainer(InternalPage, "reportheader", "", ABM.PRINT_PAGEBREAK_INSIDE_AVOID)
   ReportHeader.AddRows(1,False, "border").AddCells12(1,"")
   ReportHeader.AddRows(1,False, "").AddCellsOS(1,0,0,0,9,9,9,"").AddCellsOSMP(1,0,0,0,3,3,3,20,0,0,0,"right")
   ReportHeader.AddRows(1,False, "").AddCellsOS(1,0,0,0,7,7,7,"").AddCellsOS(1,0,0,0,5,5,5,"")

   ReportHeader.CellR(0,1).AddComponent(BuildLabel(InternalPage, "repTitle", "{B}I N V O I C E{/B}", ABM.SIZE_H4, "center", True, ABM.VISIBILITY_ALL))
   ReportHeader.CellR(1,1).AddComponent(BuildLabel(InternalPage, "repAddress", "Jonathan Neal{BR}{BR}101 E. Chapman Ave{BR}Orange, CA 92866{BR}{BR}(800) 555-1234",ABM.SIZE_H5, "", True, ABM.VISIBILITY_ALL))
   ReportHeader.CellR(0,2).AddComponent(BuildImage(InternalPage, "repImg", "../images/logo2.png", True, ABM.VISIBILITY_ALL))
   ReportHeader.CellR(1,1).AddComponent(BuildLabel(InternalPage, "repCompany", "{B}Some Company{/B}{BR}{B}c/o Some Guy{/B}", ABM.SIZE_H4, "", True, ABM.VISIBILITY_ALL))
   ReportHeader.CellR(0,2).AddComponent(Build3Block(InternalPage, "repInvoiceData", "{B}Invoice #{/B}", "101138", "{B}Date{/B}", "Januari 1, 2017","{B}Amount Due{/B}","$ 600.00", True, ABM.VISIBILITY_ALL)   )

   ' the report body
   Dim Body As ABMReport
   Body.Initialize("Body", Me, InternalPage, internalID & "body", "", ABM.PRINT_PAGEBREAK_INSIDE_AVOID)
   ' header of the body
   Body.Header.InitializeAsTexts(Array As String("Year(s) Overview", "Q1", "Q2", "Q3", "Q4"), Array As Int(40,15,15,15,15), Array As String("repheader","repheaderright","repheaderright","repheaderright","repheaderright"), "")

   ' detail lines: here you can e.g. run queries but for demo purposes let's do everything manual
   Dim sums(5) As Int
   For i = 1 To 9
     ' change the class of the last column on the second row
     Dim ColorClass As String = "repbodyright"
     If i = 2 Then
       ColorClass = "repbodyright red-text"
     End If

     ' create a text block
     Dim block As ABMReportBlock
     block.InitializeAsTexts(Array As String("Year 200" & i, 200*i, 300*i, 400*i, 500*i), Array As Int(40,15,15,15,15), Array As String("repbody","repbodyright",ColorClass, "repbodyright","repbodyright"), "")

     ' dirty way to make the sums
     For k = 1 To 4
       sums(k) = sums(k) + (20 + (k-1)*100) * i

   ' footer of the body
   Body.Footer.InitializeAsTexts(Array As String("{NBSP}", sums(1) , sums(2), "{B}" & sums(3) & "{/B}", sums(4)), Array As Int(40,15,15,15, 15), Array As String("repfooter", "repfooterright","repfooterright","repfooterright","repfooterright"), "")
   ' add the subreport 'body' to the body of the main report

   ' the report footer
   Dim ReportFooter As ABMContainer = Report.Footer.InitializeAsContainer(InternalPage, "reportfooter", "", "")

   ReportFooter.AddRows(1,False, "").AddCells12(1,"")
   ReportFooter.AddRows(1,False, "").AddCellsOS(1,0,0,0,2,2,2,"").AddCellsOSMP(1,0,0,0,5,5,5,0,0,10,40,"").AddCellsOS(1,0,0,0,5,5,5,"")
   ReportFooter.AddRows(1,False, "").AddCells12(1,"")
   ReportFooter.AddRows(1,False, "").AddCells12(1,"")
   ReportFooter.AddRows(1,False, "").AddCellsOSMP(1,4,4,4,4,4,4,0,0,0,0,"")
   ReportFooter.AddRows(2,False, "").AddCells12(1,"")

   ReportFooter.CellR(1,1).AddComponent(BuildLabel(InternalPage, "RepSignatureLbl", "Please sign this document", ABM.SIZE_H5, "", True, ABM.VISIBILITY_ALL))
   ReportFooter.CellR(0,2).AddComponent(BuildSignature(InternalPage, "repSignature", True, ABM.VISIBILITY_ALL))
   ReportFooter.CellR(0,3).AddComponent(Build3Block(InternalPage, "repTotalData", "{B}Total{/B}","$ 600.00","{B}Amount Paid{/B}","$ 0.00","{B}Balance Due{/B}","$ 600.00", True, ABM.VISIBILITY_ALL))

   ' avoid row 2 to be broken (can give weird results, depends on browser support!)
   ReportFooter.row(2).PrintPageBreak = ABM.PRINT_PAGEBREAK_INSIDE_AVOID

   ' alaways break before printing row 3
   ReportFooter.row(3).PrintPageBreak = ABM.PRINT_PAGEBREAK_BEFORE_ALWAYS

   ' all the rest is only visible on the print, not on the screen
   ReportFooter.CellR(1,1).AddComponent(BuildLabel(InternalPage, "AdditionalNotes", "{B}A D D I T I O N A L {NBSP}  N O T E S{/B}", ABM.SIZE_H4, "center", True, ABM.VISIBILITY_HIDE_ALL))
   ReportFooter.CellR(0,1).AddComponent(BuildDivider(InternalPage, "divider", True, ABM.VISIBILITY_HIDE_ALL))
   ReportFooter.CellR(0,1).AddComponent(BuildLabel(InternalPage, "note1", "Note 1: A finance charge of 1.5% will be made on unpaid balances after 30 days.", ABM.SIZE_H6, "", True, ABM.VISIBILITY_HIDE_ALL))
   ReportFooter.CellR(1,1).AddComponent(BuildLabel(InternalPage, "note2", "Note 2: Your current purchase chart.", ABM.SIZE_H6, "", True, ABM.VISIBILITY_HIDE_ALL))

   ' create a pie chart
   Dim chart5 As ABMChart
   chart5.Initialize(InternalPage, "chart5", ABM.CHART_TYPEPIE, ABM.CHART_RATIO_SQUARE, "chart5theme")
   chart5.Visibility = ABM.VISIBILITY_HIDE_ALL
   ' Something special, Label interpolation.  the value will transformed into a percentage format (This is Javascript!)
   Dim SumValues As Int = 30+50+70+80+100+140+170
   chart5.OptionsPie.LabelInterpolation = "Math.round(value / " & SumValues  & " * 100) + '%'"
   ' add ONE serie
   Dim Serie5A As ABMChartSerie
   Serie5A.SetValues(Array As Int(30,50,70,80,100,140,170))
   ' add the chart to the cell
End Sub

' Helper Methods
Sub BuildLabel(internalPage As ABMPage, ID As String, Text As String, Size As String, theme As String, isPrintable As Boolean, Visibility As String) As ABMLabel
   Dim tmpLbl As ABMLabel
   tmpLbl.Initialize(internalPage, ID, Text, Size, False, theme)
   tmpLbl.IsPrintable = isPrintable
   tmpLbl.Visibility = Visibility
   Return tmpLbl
End Sub

It will never be able to print everything of course (e.g. scrolling components), but I’m pretty happy with the things it will be able to do.  Browser support for report printing is still in its infancy I’m afraid and it will take some time before all browsers are lined up supporting all HTML printing features.

But, together with the new ABMreport component, you will already be able to create reports with very little effort using ABMaterial and B4J!

ABMaterial 3.75 will be available for donators in a couple of weeks.


Click here to Donation and support ABMaterial