B4J Question [ABMMaterial] [SOLVED] ABM 3.75 - Event still not catch on class made of ABMTable

incendio

Well-Known Member
Licensed User
Longtime User
Hello guys,

It seem that class made of ABMTable still not catch an event.

Here are my class declaration :
B4X:
Sub Class_Globals
    Private ABM As ABMaterial
    Public Tbl As ABMTable
    Private ObjectId As String
    Private TblTheme = "tbltheme" As String
    Private TblHeaderTheme = "tblheader" As String
    Private TblRowTheme = "tblrow" As String
    Private NoOfRows, NoOfCols As Int
    Private TmpId = -10 As Int
    Type CellInfo(Val As String,Thm As String)
End Sub

Sub Initialize(page As ABMPage,Handler As String, Headers() As String)
    Tbl.Initialize(page, Handler, False, False, False, TblTheme)
    Tbl.SetHeaders(Headers)
    Tbl.SetColumnDataFields(Headers)
   
    Private HeadThemes As List
    Private VisibilityCol As List
    HeadThemes.Initialize
    VisibilityCol.Initialize
    For i = 0 To Headers.Length - 1
        HeadThemes.Add(TblHeaderTheme)
        VisibilityCol.Add(True)
    Next
    Tbl.SetHeaderThemes(HeadThemes)
    Tbl.SetColumnVisible(VisibilityCol)
   
    Tbl.IsBordered = True
    Tbl.EventHandler = Me
    ObjectId = Handler
End Sub

Noticed in that code, a line, Tbl.EventHandler = Me, but class didn't have a Sub ParseEvent.
App compile and run OK, only :
1) No error in B4J's log
2) No event catch

With another class made of ABMInput, without Sub ParseEvent in that class, App won't run & B4J's log raised an error, indicated that Sub ParseEvent not found.
 

incendio

Well-Known Member
Licensed User
Longtime User
Removed Tbl.EventHandler = Me from the class, table's click event still not triggered on page that called this class.
On a class with ABMInput, when no event handler in this class, event will catch on page that called this class.

Are there something wrong with my codes or this is a bug?
 
Upvote 0

alwaysbusy

Expert
Licensed User
Longtime User
On a class with ABMInput, when no event handler in this class, event will catch on page that called this class.
Then this is the bug as it is not supposed to

There are only two possible scenarios:

1. The 'Classic' way
You cannot raise the events anywhere else than in the Page Class

Page Class
B4X:
dim mytbl as ABMTable = tblModule.BuildTable()
...
page.cell(1,1).AddComponent(mytbl)

' EVENTS ARE RAISED HERE
Sub mytbl_Clicked(...)

End Sub

tblModule Module
B4X:
Sub BuildTable() as ABMTable
     Dim tbl as ABMTable

     Return tbl
End Sub

2. The 'New' way
Events are ALWAYS raised in the Class

Page Class
B4X:
Dim myTbl As tblClass
myTbl.Initialize(page, "mytbl", Array As String("col 1", "col 2", "col 3"))
myTbl.AbTbl.AddRow("uniqueId1", Array As String("1", "2", "3"))
myTbl.AbTbl.SetRowThemes(Array As String("", "", ""))
page.Cell(4,1).AddComponent(myTbl.AbTbl)

tblClass Class
B4X:
Sub Class_Globals
   Public AbTbl As ABMTable
   
   Private ABM As ABMaterial 'ignore     
   
   Private ObjectId As String
End Sub

' MUST HAVE ITS OWN ParseEvent METHOD!
public Sub ParseEvent(params As Map)
   Dim eventName As String = params.Get("eventname")
   Dim eventParams() As String = Regex.Split(",",params.Get("eventparams"))
   
   ' Important! Here we remove the 'parent' part, so we can re-use the this component, as the events will have the same name
   ' e.g. myTopBar2aBtn1_Clicked will become Btn1_Clicked
   eventName = eventName.SubString(ObjectId.Length)
   
   If SubExists(Me, eventName) Then
     params.Remove("eventname")
     params.Remove("eventparams")
     Select Case params.Size
       Case 0
         CallSub(Me, eventName)
       Case 1
         CallSub2(Me, eventName, params.Get(eventParams(0)))
       Case 2
         If params.get(eventParams(0)) = "abmistable" Then
           Dim PassedTables As List = ABM.ProcessTablesFromTargetName(params.get(eventParams(1)))
           CallSub2(Me, eventName, PassedTables)
         Else
           CallSub3(Me, eventName, params.Get(eventParams(0)), params.Get(eventParams(1)))
         End If
       Case Else
         ' cannot be called directly, to many param
         CallSub2(Me, eventName, params)
     End Select
   End If
End Sub

'Initializes the object. You can add parameters to this method if needed.
public Sub Initialize(page As ABMPage, id As String, Headers() As String)   
   ObjectId = id
   ' THE PART AFTER 'ObjectId &' WILL DETERMINE THE EVENT NAME (IN THIS CASE tbl)
   AbTbl.Initialize(page, ObjectId & "tbl", False, False, True, "")
   AbTbl.SetHeaders(Headers)
   AbTbl.SetColumnDataFields(Headers)
   AbTbl.IsResponsive = True
   
   AbTbl.EventHandler = Me ' MUST USE EventHandler = Me
   
   Private HeadThemes As List
   HeadThemes.Initialize
   For i = 0 To Headers.Length - 1
     HeadThemes.Add("")
   Next
   AbTbl.SetHeaderThemes(HeadThemes)
   
   AbTbl.IsBordered = True   
End Sub

' THE EVENTS ARE RAISED HERE
Sub tbl_Clicked(PassedRowsAndColumns As List)
   Log("Clicked")
End Sub

A mix of both scenarios is not possible, and will never be. So in short, if you want the events to be raised in the Page Class, use a Module. If you want the events raised in a sub class, use a class + own ParseEvent + EventHandler = me
 
Upvote 0

incendio

Well-Known Member
Licensed User
Longtime User
The new way / class way, still not catch even. Here are the complete codes of my class.
B4X:
'Class module
Sub Class_Globals
    Private ABM As ABMaterial
    Public AbTbl As ABMTable
    Private ObjectId As String
    Private TblTheme = "tbltheme" As String
    Private TblHeaderTheme = "tblheader" As String
    Private TblRowTheme = "tblrow" As String
    Private NoOfRows, NoOfCols As Int
    Private TmpId = -10 As Int
    Type CellInfo(Val As String,Thm As String)
End Sub

public Sub ParseEvent(params As Map)
    Dim eventName As String = params.Get("eventname")
    Dim eventParams() As String = Regex.Split(",",params.Get("eventparams"))
    Log(eventName)
  
    ' Important! Here we remove the 'parent' part, so we can re-use the this component, as the events will have the same name
    ' e.g. myTopBar2aBtn1_Clicked will become Btn1_Clicked
    eventName = eventName.SubString(ObjectId.Length)
  
    If SubExists(Me, eventName) Then
        params.Remove("eventname")
        params.Remove("eventparams")
        Select Case params.Size
            Case 0
                CallSub(Me, eventName)
            Case 1
                CallSub2(Me, eventName, params.Get(eventParams(0)))
            Case 2
                If params.get(eventParams(0)) = "abmistable" Then
                    Dim PassedTables As List = ABM.ProcessTablesFromTargetName(params.get(eventParams(1)))
                    CallSub2(Me, eventName, PassedTables)
                Else
                    CallSub3(Me, eventName, params.Get(eventParams(0)), params.Get(eventParams(1)))
                End If
            Case Else
                ' cannot be called directly, to many param
                CallSub2(Me, eventName, params)
        End Select
    End If
End Sub


Sub Initialize(page As ABMPage,Handler As String, Headers() As String)
    ObjectId = Handler

    AbTbl.Initialize(page, Handler & "tbl", False, False, False, TblTheme)
    AbTbl.SetHeaders(Headers)
    AbTbl.SetColumnDataFields(Headers)
   
    Private HeadThemes As List
    Private VisibilityCol As List
    HeadThemes.Initialize
    VisibilityCol.Initialize
    For i = 0 To Headers.Length - 1
        HeadThemes.Add(TblHeaderTheme)
        If i < 2 Then
            VisibilityCol.Add(False)
        Else
            VisibilityCol.Add(True)
        End If
    Next
    AbTbl.SetHeaderThemes(HeadThemes)
    AbTbl.SetColumnVisible(VisibilityCol)
   
    AbTbl.IsBordered = True
    AbTbl.EventHandler = Me
    Log("initialize")
End Sub

'Start Build in Subs
Public Sub GetActiveRow As String
    Return IdtoRow(GetRowId)
End Sub

Public Sub GetRowId As String
    Return AbTbl.GetActiveRow
End Sub

Private Sub IdtoRow(Id As String) As String
    For x = 0 To NoOfRows - 1
        If AbTbl.GetString(x,0) = Id Then
            Return x
        End If
    Next
    Return "-1"
End Sub

Public Sub TtlRows As Int
    Return NoOfRows
End Sub

Public Sub TtlCols As Int
    Return NoOfCols
End Sub

Public Sub AddRow(Values() As String)
    Private Id As String
    Private row As List
    row.Initialize
           
    Private rCellThemes As List
    rCellThemes.Initialize
   
    For i = 0 To Values.Length-1
        Id = Values(0)
        row.Add(Values(i))
        rCellThemes.Add(TblRowTheme)
    Next
    AbTbl.AddRow(Id, row)
    AbTbl.SetRowThemes(rCellThemes)
    NoOfRows = NoOfRows+1
End Sub

Public Sub GetTmpId As String
    TmpId = TmpId - 1
    Return TmpId   
End Sub

Public Sub AddRowFromSQL(SQ As SQL,Query As String)
    Try
        AbTbl.Clear
        Private Values As ResultSet
        Private Id As String
        NoOfRows = 0
   
        Values = SQ.ExecQuery(Query)
        NoOfCols = Values.ColumnCount
        Do While Values.NextRow
            Private row As List
            row.Initialize
           
            Private rCellThemes As List
            rCellThemes.Initialize

            For i = 0 To NoOfCols - 1
                Id = Values.GetString2(0)
                row.Add(Values.GetString2(i))
                rCellThemes.Add(TblRowTheme)
            Next
            AbTbl.AddRow(Id, row)
            AbTbl.SetRowThemes(rCellThemes)
            NoOfRows = NoOfRows + 1
        Loop
        Values.Close
    Catch
        Log(LastException)
    End Try
End Sub

Public Sub CopyTbltoList() As List
    Private TblRows As List
    TblRows.Initialize
    For x = 0 To NoOfRows - 1
        Private TblCol As List
        TblCol.Initialize

        For y = 0 To NoOfCols - 1
            Private Cell As CellInfo
            Cell.Initialize
            Cell.Val = AbTbl.GetString(x,y)
            Cell.Thm = AbTbl.GetCellTheme(x,y)
            TblCol.Add(Cell)
            'Log("row : " & x & " Col : " & y & " Val : " & Cell.Val)
        Next
        TblRows.Add(TblCol)
    Next
    Return TblRows
End Sub

Public Sub CopyListtoTbl(TblRows As List)
    Private Id As String
    Private TblCol As List
    TblCol.Initialize
    Private Cell As CellInfo
    Cell.Initialize
   
    AbTbl.Clear

    For x = 0 To TblRows.Size - 1
        Private row As List
        row.Initialize
        Private rCellThemes As List
        rCellThemes.Initialize

        TblCol = TblRows.Get(x)
        For y = 0 To TblCol.Size - 1

            Cell = TblCol.Get(y)
           
            If y = 0 Then Id = Cell.Val

            row.Add(Cell.Val)
            rCellThemes.Add(Cell.Thm)
            'Log("x: " & x & " y : " & y & " Id : " & Id & " val : " & Cell.val)

        Next
        AbTbl.AddRow(Id, row)
        AbTbl.SetRowThemes(rCellThemes)
    Next
    NoOfRows = TblRows.Size
    NoOfCols = TblCol.Size
    AbTbl.Refresh
End Sub

Public Sub RemoveRow(Id As String)
    If Id = "" Then Return
   
    Private TblRows As List
    TblRows.Initialize
    For x = 0 To NoOfRows - 1
        If AbTbl.GetString(x,0) <> Id Then
            Private TblCol As List
            TblCol.Initialize

            For y = 0 To NoOfCols - 1
                Private Cell As CellInfo
                Cell.Initialize
                Cell.Val = AbTbl.GetString(x,y)
                Cell.Thm = AbTbl.GetCellTheme(x,y)
                TblCol.Add(Cell)
            Next
            TblRows.Add(TblCol)
        End If
    Next
    CopyListtoTbl(TblRows)
End Sub

Sub tbl_Clicked(PassedRowsAndColumns AsList)
   Log("Clicked")
End Sub

Codes to call from page
B4X:
Sub Class_Globals
   Public Name As String = "GoodGrp"  '<-------------------------------------------------------- IMPORTANT
   Public page As ABMPage

   Private ws As WebSocket
   Private theme As ABMTheme
   Private ABM As ABMaterial
   Private ABMPageId As String = ""
   Private txtGrp As InputTxt
   Private TblRows As List
   Private tbl1 As Table
End Sub

public Sub ConnectPage()
    ABMShared.ConnectNavigationBar(page)    
    page.Cell(1,1).AddComponent(txtGrp.Initialize(page,"txtGrp", "Grup Barang",60))
    page.Cell(2,1).SetFixedHeight(300, True)
    tbl1.Initialize(page,"tbl1",Array As String("ID","Tag","Grup Barang"))
    tbl1.AddRow(Array As String("-1","T","GROUP A"))
    page.Cell(2,1).AddComponent(tbl1.AbTbl)
    page.Refresh
    page.FinishedLoading
    page.RestoreNavigationBarPosition
    txtGrp.SetFocus
End Sub
Noticed that on Sub ParseEvent on class and on page, added
B4X:
Log(eventName)
Clicked even from class Table never registered on page/class.
 
Upvote 0

incendio

Well-Known Member
Licensed User
Longtime User
Huh, is that so simple? :oops:

But you were right :)

Now it is work. By the way, the class way without this code
B4X:
AbTbl.EventHandler = Me
Event will catch directly on the page. You said it was a bug, but I don't mind work in this way. This is give a flexible way for me, can catch event on class - if necessary, or just passed it to the caller page.
 
Upvote 0

alwaysbusy

Expert
Licensed User
Longtime User
Glad all worked out! :)

You said it was a bug
It is not a real bug per se. I consider it more as 'logical' bug as it is a bit counter intuitive that the event lands on a class where the object was not declared. But feel free to use it as you feel comfortable with!
 
Upvote 0
Top