B4J Tutorial [ABMaterial] - ABMCustomComponent - Google Charts


Licensed User
Requires ABM version 2.0 or above...

Google Charts - LineChart

My second stab at using a custom component ( which generally means adding js script into the project).
This is a bit more complicated - yet truly easy when you see how it flows...

Since we are adding Google Charts, we need to add the loader - in BuildPage sub add...

Now, building a chart and displaying it is very easy - using Google charts API.
As I have found, all other chart libs require you to set properties depending on what is to be rendered. Google charts does this automatically (huge code savings!). Being able to use Google Charts in ABMaterial is a big bonus. Thank you @alwaysbusy for providing the examples.

My example: Show time on X axis (or as Google calls it - hAxis) - hours, minutes and seconds.

Sub PutChartdata

    myLineChart.Rows.Clear   ' clear rows and cols since we will rebuild these lists
    Dim num As Int = selpid.Size    ' Determine how many lines to diplay from the map size
    myLineChart.Title = "ECM PID Values (for each hour)"
    myLineChart.Subtitle = "by the second"  ' only valid in material charts (not used)
    myLineChart.AddColumn("timeofday", "Time of Day") 'use timeofday data type for XAxis
    If num = 0 Then
       Toast(" At Least 1 PID Must Be Selected",5000)
    End If
    Select num  ' sets the legend titles
    Case 1
       myLineChart.AddColumn("number", getIDname(selpid.GetKeyAt(0)) )

    Case 2
       myLineChart.AddColumn("number",  getIDname(selpid.GetKeyAt(0)) )
       myLineChart.AddColumn("number", getIDname(selpid.GetKeyAt(1)))

    Case 3
       myLineChart.AddColumn("number", getIDname(selpid.GetKeyAt(0)) )
       myLineChart.AddColumn("number", getIDname(selpid.GetKeyAt(1)))
       myLineChart.AddColumn("number", getIDname(selpid.GetKeyAt(2)))

    Case 4
       myLineChart.AddColumn("number", getIDname(selpid.GetKeyAt(0)) )
       myLineChart.AddColumn("number", getIDname(selpid.GetKeyAt(1)))
       myLineChart.AddColumn("number", getIDname(selpid.GetKeyAt(2)))
       myLineChart.AddColumn("number", getIDname(selpid.GetKeyAt(3)))

    End Select
    For i = 0 To splist.Size-1
        Dim dt As Long
        dt = rngdate.Get(i)
        Dim hour, mins, sec  As Int
        hour = DateTime.GetHour(dt)
        mins = DateTime.GetMinute(dt)
        sec  = DateTime.GetSecond(dt)
        ' pass array of hours, minutes and seconds
        Select num ' using the same AddRow sub, pass up to 4 lists

          Case 1
           myLineChart.AddRow(Array As Int(hour,mins,sec) , splist.Get(i), 0.0, 0.0, 0.0,num)
          Case 2
           myLineChart.AddRow(Array As Int(hour,mins,sec) , splist.Get(i), rpmlist.Get(i)/100, 0.0, 0.0,num)
          Case 3
           myLineChart.AddRow(Array As Int(hour,mins,sec) , splist.Get(i), rpmlist.Get(i)/100, thlist.Get(i), 0.0,num)
          Case 4
           myLineChart.AddRow(Array As Int(hour,mins,sec) , splist.Get(i), rpmlist.Get(i)/100, thlist.Get(i), gpslist.Get(i),num)
        End Select
    page.Cell(2,2).Refresh  ' this will cause the custom refresh event to fire - producing the chart
End Sub
The AddRow sub, in the CustomGoogleChart class was modified to handle my needs. I didn't even bother to change the param names...

public Sub AddRow(Day() As Int , Guardians As Double, Avengers As Double, Transformers As Double, Lastcol As Double, numcols As Int)

Select numcols
    Case 1
        If Rows.Size > 0 Then ' with comma
            Rows.Add($",[ [${Day(0)},${Day(1)},${Day(2)}], ${Guardians} ]"$)
        Else ' first one, does not need a comma
            Rows.Add($"[ [${Day(0)},${Day(1)},${Day(2)}], ${Guardians}  ]"$)
        End If
    Case 2
        If Rows.Size > 0 Then ' with comma
            Rows.Add($",[ [${Day(0)},${Day(1)},${Day(2)}], ${Guardians}, ${Avengers} ]"$)
        Else ' first one, does not need a comma
            Rows.Add($"[ [${Day(0)},${Day(1)},${Day(2)}], ${Guardians}, ${Avengers} ]"$)
        End If
    Case 3
        If Rows.Size > 0 Then ' with comma
            Rows.Add($",[ [${Day(0)},${Day(1)},${Day(2)}], ${Guardians}, ${Avengers}, ${Transformers} ]"$)
        Else ' first one, does not need a comma
            Rows.Add($"[ [${Day(0)},${Day(1)},${Day(2)}], ${Guardians}, ${Avengers}, ${Transformers} ]"$)
        End If
    Case 4
        If Rows.Size > 0 Then ' with comma
            Rows.Add($",[ [${Day(0)},${Day(1)},${Day(2)}], ${Guardians}, ${Avengers}, ${Transformers}, ${Lastcol} ]"$)
        Else ' first one, does not need a comma
            Rows.Add($"[ [${Day(0)},${Day(1)},${Day(2)}], ${Guardians}, ${Avengers}, ${Transformers}, ${Lastcol} ]"$)
        End If
End Select

End Sub
This sub (ABMComp_Refresh) actually draws the chart. Took awhile to figure this one out.

Sub ABMComp_Refresh(InternalPage As ABMPage, internalID As String)
    ' use these methods to adjust the object
    ' ABM.AddClass
    ' ABM.RemoveClass
    ' ABM.AddHTML
    ' ABM.InsertHTMLAfter
    ' ABM.RemoveHTML
    ' ABM.GetProperty
    ' ABM.SetProperty
    ' ABM.RemoveProperty
    ' ABM.GetStyleProperty
    ' ABM.SetStyleProperty
    ' do some script stuff like you do in RunJavaScript   
    'Dim script As String = $""$   
    Dim script As String = $"
         // Define the chart To be drawn.
         var data = new google.visualization.DataTable();
          var options = { 
              width: '100%',
              height: 750,
            backgroundColor:  'transparent',
              chartArea: {backgroundColor    :  '#E3F2FD'},

            title: '${Title}',  
            vAxis: {title: "ECM Data Collected", minValue:0, gridlines: {count:20, color: 'gray'} },
              hAxis: {title: "Time of Day", gridlines: {count:20,  color: 'gray'} },       
            lineWidth: 1.3,               
            explorer: {
                actions: ['dragToZoom', 'rightClickToReset'],
                axis: 'horizontal',
                keepInBounds: true,
                maxZoomIn: 100.0
              legend: { position: 'bottom' },

          // Instantiate And draw the chart.
          var chart = new google.visualization.LineChart(document.getElementById('${internalID}'));
          chart.draw(data, options);     
    InternalPage.ws.Eval(script, Null)   
End Sub
Select items for each line. Max 4 so chart doesn't get too busy.


The Main form...
Each page shows 1 hours worth of data. Note how the hAxis labels automatically adjust (rotate) for best fit.


Zooming in was easy using the explorer actions. Simply right-click to zoom out to full view.
Tooltips are automatic and show up for each data point, for each line in chart!


Have fun with your Google Charts project in ABMaterial.


Licensed User
Further to above, I have added GPS lat/lon to a list so we can show a Google Map - plotting various points.


This image shows 4 points we have selected and defined as exceptions (when the value exceeds our definition).
Note: Google Maps will NOT show additional markers that contain the same lat.lon.
In this example, I added 0.0003 (.0006, .0009, etc) to each (original) longitude point so Google would include it as a new Marker.


This is where we have defined what the exceptions shall be for each PID Flag value. Setting this value to "0" will ignore it.


Here we set what the map resolution will be. Set it from 1 to 60 seconds.
The checkbox (checked) will ONLY show exceptions on the map. Un-checked - it will show each data point (for the defined resolution) as well as any exceptions found.

All accomplished easily in pure ABM and B4J.
I can't imagine what I would have to pay for some 'guru' to accomplish the same in some other language(s)!


Well-Known Member
Licensed User
Dear Harris,

Your sample code looks promising, unfortunately I can't figure out from where to start. I believe that Google Charts are fast in loading time.

Would you mind showing the BuildPage() or ConnectPage() to see, how the graphs are defined in the page.
For eg something like the code shown just below. What I have shown below is creating an ABMChart. To use GoogleChart, how do I define the graphs on the ConnectPage()
Dim chartSales As ABMChart
chartSales.Initialize(page, "chartSales", ABM.CHART_TYPEBAR, ABM.CHART_RATIO_GOLDENSECTION, "chart4theme")
' set some options
chartSales.OptionsBar.StackBars = True
chartSales.OptionsBar.HorizontalBars = False
You have mentioned that in BuildPage sub add page.AddExtraJavaScriptFile("custom/loader.js")
Do I need any other library files to use GoogleCharts ?

I am really stuck with showing a graph on the page, started with ABMChart but the delay to load the graph is bit time consuming, then tried FrappeChart. It is very fast to load the chart, unfortunately the FrappeChart class requires few more options to make it really useful. My skill level in Java and wrapping is zero. So now wanted to experiment with Google charts
Last edited:


Licensed User
Charts are fun - aren't they?
You have fooled around with the rest, so this shouldn't be that difficult.

Sometimes, when your need is simple, use Erel's Chart class. It simply builds bar or line on a canvas..
I used it to produce the attached...

public Sub BuildPage()
    ' initialize the global vars
    Dim cnt1 As ABMContainer
    Dim tblCases As ABMTable
    Dim myLineChart As CustomGoogleChart2    ' the class for the google chart
    Dim pagination, pagination1  As ABMPagination
    Dim gm1 As ABMGoogleMap
    DateTime.TimeFormat = "HH:mm"

    ' initialize this page using our theme
    page.InitializeWithTheme(Name, "/ws/" & ABMShared.AppName & "/" & Name, False, ABMShared.SessionMaxInactiveIntervalSeconds, theme)
    page.ShowLoaderType=ABM.LOADER_TYPE_MANUAL ' NEW
    page.PageTitle = "ECM Data Viewer"
    page.PageDescription = "The pdf viewer component "   
    page.PageHTMLName = "index.html"
    page.PageKeywords = "ABMaterial, material design, B4X, B4J, SEO, framework, search engine optimization"
    page.PageSiteMapPriority = "0.50"
    page.PageSiteMapFrequency = ABM.SITEMAP_FREQ_MONTHLY
    'page.UseGoogleAnalytics(ABMShared.TrackingID, Null) ' IMPORTANT Change this to your own TrackingID !!!!!!!
    ABMShared.BuildNavigationBar(page,  Page_name, "../images/logo.png", "", "Apps", Page_name)
    page.AddRowsM( 1,False,  1,  1, "").AddCells12(1,"") '.AddCellsOSMP(1,0,0,0, 2,2,2,   5 , 5, 0,0,"cnter").AddCellsOSMP(1,0,0,0,3,3,3,  5,5,30,10,"cnter").AddCellsOSMP(1,0,0,0,3,3,3,  5 ,  5,30,10,"cnter").AddCellsOSMP(1,0,0,0,2,2,2 ,   15,   5,30, 10,"cnter").AddCellsOSMP(1,0,0,0,2,2,2 ,   10,   5,30, 10,"cnter")
    page.AddRowsM2(2, False, 10,10, 20,20,"").AddCells12(1,"")
    page.BuildGrid 'IMPORTANT once you loaded the complete grid AND before you start adding components
    ' add paragraph   

    cnt1.Initialize( page, "cnt1","cntth")
    cnt1.AddRowsM( 1, True , 5,  0,  "").AddCellsOSMP(1, 0, 0,   0,  12, 12, 12, 15 ,10,  20 ,20, "") '.AddCellsOS(1,0,0,0,0,8,8, "")
    cnt1.AddRowsM( 4, False, 0, 0, "") .AddCellsOSMP(1,0,0,0 ,6, 6,6, 10, 10,15,0,  "").AddCellsOSMP(1,0,0,0, 6,6,6,10,10,15,0,  "")
    cnt1.AddRowsM2(1 ,False , 1,   10, 10,10, "").AddCellsOS(1,0, 0,0,12, 12, 12, "") '"cellc") '.AddCellsOS(1,0,0,0,0,8,8, "")
    cnt1.AddRowsM(3, False,  0,   10,  "").AddCellsOS(1,0, 0,0,12, 12, 12, "") '.AddCellsOS(1,0,0,0,0,8,8, "")
    cnt1.BuildGrid' IMPORTANT
    tblCases.Initialize(page  , "tblCases",   False,   True, True, "tbltheme")
    tblCases.SetHeaders(         Array As String( "SD", "ED", "FN" ,"Date", "Unit #" , "Driver","DRVID"   ))
    tblCases.SetHeaderThemes(    Array As String(  "bgb" , "bgb" , "bgb" , "bgc", "bgc"    , "bgc" ,  "bgc"     ))
    tblCases.SetColumnVisible(    Array As Boolean( False,False,False,  True , True, True, False    ))
    pagination.Initialize(page, "pagination",  3,   True, True, "")

    pagination1.Initialize(page, "pagination1",    15,   True,  True, "")

 Log("    BuildPage was built")
End Sub

Sub ConnectPage
'    page.GoogleMapsAPIExtras="key=AIzaSyCZMltM-eGgnIhPbiFsogjNIvhKgWMk-34&libraries=geometry,places"
    gm1.Initialize(page,"gm1",  68.069423,-162.850616,  14,  850 ,ABM.GOOGLEMAPTYPE_SATELLITE,0)

    gm1.Draggable = True
    gm1.HasMapTypeControl = True
    gm1.HasStreetViewControl = True
    gm1.HasZoomControl = True

    Dim combdrv As ABMCombo
    combdrv.Initialize(page, "combdrv","Select Driver",500, "")
    combdrv.AddItem(  "0", "ALL", BuildSimpleItem(""&"0", "mdi-action-star-rate", "{NBSP}{NBSP}"&"ALL"))

    Dim sql As SQL = DBM.GetSQL
    Dim users As List = DBM.SQLSelect(sql, "SELECT * FROM emp where comp_id = "&Main.comp_id&" Order by Last_name") ' WHERE userId=" & SelectedRowId)
    If users.Size > 0 Then
        For i = 0 To users.Size -1
           Dim user As Map = users.Get(i)
         '  Log(" driver map: "&user)
           Dim id As String = user.Get("pk")
           Dim val As String = user.Get("last_name")&", "&user.Get("first_name")
          ' Log(" Driver: "&val)
           combdrv.AddItem(  id, val, BuildSimpleItem(""&id, "mdi-action-star-rate", ""&val))
    End If

    Dim combtrk As ABMCombo
    combtrk.Initialize(page, "combtrk","Select Vehicle",500, "")
    combtrk.AddItem(  "0", "ALL", BuildSimpleItem(""&"0", "mdi-action-star-rate", "{NBSP}{NBSP}"&"ALL"))

    Dim sql As SQL = DBM.GetSQL
    Dim users As List = DBM.SQLSelect(sql, "SELECT * FROM vehicle where comp_id = "&Main.comp_id&" AND unit_type = "&veh_type &" Order by unit_number") ' WHERE userId=" & SelectedRowId)
    If users.Size > 0 Then
        For i = 0 To users.Size -1
           Dim user As Map = users.Get(i)
         '  Log(" trk map: "&user)
           Dim id As String = user.Get("unit_number")
           Dim val As String = user.Get("unit_number")
          ' Log(" truck: "&val)
           combtrk.AddItem(  id, val, BuildSimpleItem(""&id, "mdi-action-star-rate", "{NBSP}{NBSP}"&val))
    End If

    Dim startdate As ABMDateTimePicker
    startdate.Initialize(page, "startdate",ABM.DATETIMEPICKER_TYPE_DATE ,qsd,"Start Date ","") ' ABM.INPUT_TEXT, "Plate #", False, "lightblue")
    startdate.ClearText = ""

    Dim enddate As ABMDateTimePicker
    enddate.Initialize(page, "enddate",ABM.DATETIMEPICKER_TYPE_DATE , qed,"End Date ","") ' ABM.INPUT_TEXT, "Plate #", False, "lightblue")
'    enddate.SetDate(qed)
    enddate.ClearText = ""

    Dim cbfilt As ABMCheckbox
    cbfilt.Initialize (page,"cbfilt", "Exceptions Only",False,"")
    cbfilt.IsFilled = True
    Dim btnshowus As ABMCheckbox
    btnshowus.Initialize( page ,  "btnshowus", "US MILES", True,"")
    btnshowus.IsFilled = True

    Dim btnrefresh As ABMButton
    btnrefresh.InitializeFlat(  page ,  "btnrefresh",  "mdi-action-autorenew", ABM.ICONALIGN_LEFT,"Refresh Map", "amber")

    Dim btnpids As ABMButton
    btnpids.InitializeFlat( page ,  "btnpids", "mdi-action-settings", ABM.ICONALIGN_LEFT,"Select/View PIDs","amber")

    Dim hist As ABMLabel
    hist.Initialize(page,"hist","Using CURRENT Data",ABM.SIZE_H5,False, "")
    If Main.ecmFolder <> Main.histecmFolder Then
        usinghist = True
        hist.Text = "Using HISTORIC Data"
        usinghist = False   
    End If

    Dim chbgps As ABMCheckbox
    chbgps.Initialize( page ,  "chbgps", "GPS DATA ONLY", False, "chbox")
    chbgps.IsFilled = True




    cnt1.Cell(6, 1).AddComponent(tblCases)
    myLineChart.Initialize(page,  "myLinechart")


    page.FinishedLoading 'IMPORTANT   
    Log(" Finished loading and connecting page")

    page.NavigationBar.Title = "ECM Data Viewer" 'page.ws.Session.GetAttribute2( "UserAction", "")
    Dim extraSideBar As ABMSideBar
    extraSideBar.InitializeasPanel(page, "extrasidebar",   580,   60,   Null, "nav3theme", "pnl1",  "")

    '  extraSideBar.InitializeAsPanel(page1, "extrasidebar", 450,  130,   Null,   "nav2theme", "extrasidebarcontent",  "")
    extraSideBar.Content.AddRowsM(2, False, 0, 0, "").AddCells12MP(1,   10, 10,  10, 10, "")
    extraSideBar.Content.Cell(1,1).AddComponent( cnt1)
    page.NavigationBar.AddTopItemWithSideBar( "SideBar",  "Setup Graph",    "mdi-editor-insert-chart",  "", True, extraSideBar)
    CallSubDelayed2(Me, "LoadInspects", 1)
'    LoadInspects(1)

'    page.ShowSideBar("SideBar")

End Sub