B4J Code Snippet [ABMaterial] Making an ABMCombo MultiSelect with ABMCheckboxes

Discussion in 'B4J Code Snippets' started by Mashiane, Aug 5, 2018.

  1. Mashiane

    Mashiane Expert Licensed User

    Ola

    So i am sitting and thinking... I need some multi-check box functionality in my combo box. I need to return the selected items and be able to set each item to be checked. Then it dawns on me. One of the nice things about the ABMCombo is the functionality to add ABMContainers to it. So it should be possible to add a container with a checkbox as an item. This basically is a nicer alternative for what I intend doing. I can trap the combo click event, get the selected items and process what I need to do.

    ABMCombo.gif

    So, I need a container that has a checkbox, I should be able to set the checkbox on and off on addition.

    So I add this to my ABMShared..

    Code:
    'add a checkbox item to combo
    Sub ABMCheckBoxItem(page As ABMPage, id As String, text As String, checked As Boolean) As ABMContainer
        
    Dim ItemCont As ABMContainer
        ItemCont.Initialize(
    pageid"")
        ItemCont.AddRowsM(
    1,False,0,20"").AddCellsOSMP(1,0,0,0,12,12,12,0,0,10,10,"")
        ItemCont.BuildGrid 
    'IMPORTANT once you loaded the complete grid AND before you start adding components
     
        
    Dim chk As ABMCheckbox
        chk.Initialize(
    pageid,text,checked,"")
        ItemCont.Cell(
    1,1).AddComponent(chk)
        
    Return ItemCont
    End Sub
    Here I create a container with a checkbox, specifiying the id and text of the checkbox.

    So in my connectpage, I create a combo and add some check boxes to it.

    Code:
    Dim asel As ABMCombo
        asel.Initialize(
    page,"asel","Resources","650","")
        asel.AddItem(
    "opt1","Option 1",ABMShared.ABMCheckBoxItem(page,"opt1","Option 1",True))
        asel.AddItem(
    "opt2","Option 2",ABMShared.ABMCheckBoxItem(page,"opt2","Option 2",False))
        asel.AddItem(
    "opt3","Option 3",ABMShared.ABMCheckBoxItem(page,"opt3","Option 3",True))
        asel.AddItem(
    "opt4","Option 4",ABMShared.ABMCheckBoxItem(page,"opt4","Option 4",False))
        asel.AddItem(
    "opt5","Option 5",ABMShared.ABMCheckBoxItem(page,"opt5","Option 5",False))
        
    page.Cell(2,1).AddComponent(asel)
    Now, when the combo box is selected, I need to get the items that are checked in the combobox. So I add a click event. Off course, returning the selected items can be done anywhere in my code. The returned object is a list with the ids of each of the items selected.

    Code:
    Sub asel_Clicked(itemId As String)
        
    Dim lst As List = ABMShared.ABMComboGetChecked(page,"asel")
        
    Dim out As String = ABMShared.List2JSON(lst)
        
    page.Msgbox("",out,"asel checked","ok",False,"","")
    End Sub
    As noted above, I have called ABMComboGetChecked passing the combo box name to it. To be able to do this I needed to write some JQuery.

    Code:
    'get checked from combobox
    Sub ABMComboGetChecked(page As ABMPage, id As StringAs List
        
    Dim ulKey As String = $"dropdown${id}"$
        
    Dim script As String = $"var selected = [];
    $('#${
    ulKey} input:checked').each(function() {
        selected.push($(this).attr('id'));
    });
    var myJSON = selected.toString();
    return myJSON;"$

    Dim f As Future = page.ws.EvalWithResult(script,Null)
    Dim sout As String = f.value
    Dim items() As String = StrParse(",",sout)
    Dim lstItems As List
    lstItems.Initialize
    For Each strItem As String In items
        strItem = MvField(strItem,
    1,"-")
        lstItems.Add(strItem)
    Next
    Return lstItems
    End Sub
    When you right click on any HTML element on your page, you can see how its created. So the UL (unordered list) that stores the combo items is called dropdown + id of your component. The checkboxes are somewhere in that html element, so I call jquery to return all checked input ids inside the UL. Remember from this, the ABM dropdown is not a SELECT component but a UL.

    Each element id is suffixed with -id+'input' being for example opt1-opt1input, so we need to return opt1, thus the mvfield function. This ends up being shown on the msgbox in the click event.

    So, to be able to check anything in the combo, I also needed another method.

    Code:
    Sub iif(Expression2Evaluate As String, ReturnIfTrue As Object, ReturnIfFalse As Object) As Object
        
    If Expression2Evaluate = True Then Return ReturnIfTrue Else Return ReturnIfFalse
    End Sub

    'set checked
    Sub ABMComboSetChecked(page As ABMPage, compid As Stringid As String, bChecked As Boolean)
        
    If id.length = 0 Then Return
        
    If id.StartsWith("["And id.EndsWith("]"Then
            
    Dim lst As List = Json2List(id)
            
    id = MvFromList(lst,",")
        
    End If
        
    Dim parentID As String = $"dropdown${compid}"$
        
    'ability to set multiple items at once
        Dim spItems() As String = StrParse(",",id)
        
    Dim sbCode As StringBuilder
        sbCode.Initialize
        
    For Each strItem As String In spItems
            
    id = strItem
            
    Dim itmKey As String = $"${id}-${id}input"$
            
    Dim sChecked As String = iif(bChecked=True,"true","false")
            
    Dim script As String = $"$('#${parentID}').find('#${itmKey}').prop('checked', ${sChecked});"$
            sbCode.Append(script).Append(
    CRLF)
        
    Next
        
    page.ws.Eval(sbCode.tostring,Null)
        
    page.ws.flush
    End Sub

    Sub MvFromList(lst As List, Delim As StringAs String
        
    Dim lTot As Int
        
    Dim lCnt As Int
        
    Dim lStr As StringBuilder
        lStr.Initialize
        lTot = lst.Size - 
    1
        
    For lCnt = 0 To lTot
            lStr.Append(lst.Get(lCnt))
            
    If lCnt <> lTot Then lStr.Append(Delim)
        
    Next
        
    Return lStr.tostring
    End Sub
    This I can call whenever I need to.

    Code:
    ABMShared.ABMComboSetChecked(page"cboItems"", "opt5",True)
    As each element in a page needs a unique name, there can only be one opt5. This also creates the proper name for the element and then runs a JQuery script to check the element.

    Should you wish to check elements in ConnectPage, ensure you do so after page.refresh.

    Happy coding.

    PS: In Summary, just to show how I have currently implemented this...

    1. I build my normal modal sheet the same way it has been and add an ABMCombo in it.
    2. On Add / Edit, I show the modal and clear the contents of the controls and refresh the ones I need where necessary. As an example, to refresh my combo /w checkboxes combo I call this method after I have called .ShowModalSheet. This selects records form a database and loads them to the combo, all the items are un-checked. This is both applicable for Add / Edit CRUD functions.

    Code:
    'Refresh the contents of the ABMCombo at runtime.
    Private Sub RefreshOnLoad_cboforums()
        
    'Get access to the component in the page.
        Dim msgenpeople As ABMModalSheet = page.ModalSheet("msgenpeople")
        
    Dim cboforums As ABMCombo = msgenpeople.Content.Component("cboforums")
        
    'Clear the ABMCombo items
        cboforums.Clear
        
    'Define list details to load to the combo
        Dim results As List
        
    Dim resCnt As Int
        
    Dim resTot As Int
        
    Dim resMap As Map
        
    'variable to hold the source field
        Dim id As String
        
    'variable to hold the description field
        Dim Name As String
        
    'Read arguments from LocalStorage (if any)
        'Add a spinner to the page
        'Get connection from current pool if MySQL/MSSQL
        Dim jSQL As SQL = ABMShared.SQLGet
        
    'Get the records as a list of maps from the db
        results = ABMShared.SQLExecuteMaps(jSQL,"select id,name from Forums  order by name"Null)
        
    'Close the connection to the database
        ABMShared.SQLClose(jSQL)
        
    'Loop throught each record read and process it
        resTot = results.size - 1
        
    For resCnt = 0 To resTot
            
    'Get the record map
            resMap = results.get(resCnt)
            
    'process the id field
            id = resMap.get("id")
            Name = resMap.get(
    "name")
            cboforums.AddItem(
    id, Name, ABMShared.ABMCheckBoxItem(pageid, Name, False))
        
    Next
        
    'Refresh the ABMCombo contents
        cboforums.Refresh
    End Sub
    3. When a record is added / updated, I save the contents to a map which is used on my inserts database calls. To read the contents of the checked combo items, I call..

    Code:
    Dim lstcboforums As List = ABMShared.ABMComboGetCheckedModal(page"msgenpeople""cboforums")
        
    Dim outcboforums As String = ABMShared.List2JSON(lstcboforums)
        pMap.put(
    "forums",outcboforums)
    The contents of outcboforums will be like ["1","2","4"] which will be ids of the checked items, this is put on the pMap map object and saved to the db.

    4. When a record is selected for Edit i.e. ShowModal and view current record for editing, I read the db record to a map and then for the combo / w check, after it has been refreshed, just check the relevent items for this record.

    Code:
    Dim cboforumsContents As String
        cboforumsContents = pMap.getdefault(
    "forums","")
        ABMShared.ABMComboUnCheckAllModal(
    page"msgenpeople","cboforums")
        ABMShared.ABMComboSetCheckedModal(
    page"msgenpeople","cboforums", cboforumsContents,True)
     
    Last edited: Aug 14, 2018
    alwaysbusy, amaxco, inakigarm and 2 others like this.
  2. liulifeng77

    liulifeng77 Active Member Licensed User

    hi, I copied your code, but can't find
    StrParse() and
    MvField() ?
    can you give me a clue? thanks!
     
    joulongleu likes this.
  3. Mashiane

    Mashiane Expert Licensed User

    @liulifeng77 , these are custom methods i wrote, here you go.

    Code:
    Sub List2JSON(lst As ListAs String
        
    Dim sOut As String
        
    Dim jsonGen As JSONGenerator
        jsonGen.Initialize2(lst)
        sOut = jsonGen.ToString
        
    Return sOut
    End Sub
    Sub StrParse(Delimiter As String, MV As StringAs String()
        Delimiter = FixDelimiter(Delimiter)
        
    Return Regex.Split(Delimiter, MV)
    End Sub
    Code:
    Sub FixDelimiter(sValue As StringAs String
        
    If sValue = "|" Then sValue = "\|"
        
    If sValue = "." Then sValue = "\."
        
    If sValue = "\" Then sValue = "\\"
        
    If sValue = "^" Then sValue = "\^"
        
    If sValue = "$" Then sValue = "\$"
        
    If sValue = "?" Then sValue = "\?"
        
    If sValue = "*" Then sValue = "\*"
        
    If sValue = "+" Then sValue = "\+"
        
    If sValue = "(" Then sValue = "\("
        
    If sValue = ")" Then sValue = "\)"
        
    If sValue = "[" Then sValue = "\["
        
    If sValue = "{" Then sValue = "\{"
        
    If sValue = ";" Then sValue = "\;"
        
    Return sValue
    End Sub
    Code:
    Sub MvField(sValue As String, iPosition As Int, Delimiter As StringAs String
        
    If sValue.Length = 0 Then Return ""
        
    Dim xPos As Int: xPos = sValue.IndexOf(Delimiter)
        
    If xPos = -1 Then Return sValue
        
    Dim mValues() As String
        
    Dim tValues As Int
        Delimiter = FixDelimiter(Delimiter)
        
    If sValue.EndsWith(Delimiter) Then sValue = sValue & " "
        mValues = 
    Regex.split(Delimiter, sValue)
        tValues = mValues.Length -
    1
        
    Select Case iPosition
            
    Case -1
                
    Return mValues(tValues)
            
    Case -2
                
    Return mValues(tValues - 1)
            
    Case Else
                iPosition = iPosition - 
    1
                
    If iPosition <= -1 Then Return mValues(tValues)
                
    If iPosition > tValues Then Return ""
                
    Return mValues(iPosition)
        
    End Select
    End Sub
     
    Johan Hormaza and joulongleu like this.
  4. Mashiane

    Mashiane Expert Licensed User

    If you place the combobox in a modal sheet, you need to use the method below, first uncheck all the items and then check the items you want. The mdlName is the name of your modal sheet, pref lowercase. You can right click on on the html element and 'inspect element' to see how this was defined by ABM.

    Code:
    'unchech all if combo on page
    Sub ABMComboUnCheckAll(page As ABMPage, id As String)
        
    Dim ulKey As String = $"dropdown${id}"$
        
    Dim script As String = $"$('#${ulKey}').find('input[type=checkbox]:checked').removeAttr('checked');"$
        
    page.ws.Eval(script,Null)
        
    page.ws.Flush
    End Sub

    'unchech all if combo on modal content
    Sub ABMComboUnCheckAllModal(page As ABMPage, mdlName as stringid As String)
        
    Dim ulKey As String = $"dropdown${mdlName}-content-${id}"$
        
    Dim script As String = $"$('#${ulKey}').find('input[type=checkbox]:checked').removeAttr('checked');"$
        
    page.ws.Eval(script,Null)
        
    page.ws.Flush
    End Sub

    'get checked if combo on modal content
    Sub ABMComboGetCheckedModal(page As ABMPage, mdlName As Stringid As StringAs List
        
    Dim ulKey As String = $"dropdown${mdlName}-content-${id}"$
        
    Dim script As String = $"var selected = [];
        $('#${
    ulKey} input:checked').each(function() {
        selected.push($(this).attr('id'));
        });
        var myJSON = selected.toString();
        return myJSON;"$

        
    Dim f As Future = page.ws.EvalWithResult(script,Null)
        
    Dim sout As String = f.value
        
    Dim items() As String = StrParse(",",sout)
        
    Dim lstItems As List
        lstItems.Initialize
        
    For Each strItem As String In items
            strItem = MvField(strItem,
    1,"-")
            lstItems.Add(strItem)
        
    Next
        
    Return lstItems
    End Sub
     
    Johan Hormaza and joulongleu like this.
  5. Mashiane

    Mashiane Expert Licensed User

    NB: For this to work, ShowModal should have been called first before setting its control contents.

    To be able to set the checked items on the modal.

    e. ABMComboSetCheckedModal(page, "mdlName", "cboBox","opt1,opt3", True) to set all the ids

    Code:
    'set checked items on a page
    Sub ABMComboSetCheckedModal(page As ABMPage, mdlName As String, compid As Stringid As String, bChecked As Boolean)
        
    If id.length = 0 Then Return
        
    If id.StartsWith("["And id.EndsWith("]"Then
            
    Dim lst As List = Json2List(id)
            
    id = MvFromList(lst,",")
        
    End If
        
    Dim parentID As String = $"dropdown${mdlName}-content-${compid}"$
        
    'ability to set multiple items at once
        Dim spItems() As String = StrParse(",",id)
        
    Dim sbCode As StringBuilder
        sbCode.Initialize
        
    For Each strItem As String In spItems
            
    id = strItem
            
    Dim itmKey As String = $"${id}-${id}input"$
            
    Dim sChecked As String = iif(bChecked=True,"true","false")
            
    Dim script As String = $"$('#${parentID}').find('#${itmKey}').prop('checked', ${sChecked});"$
            sbCode.Append(script).Append(
    CRLF)
        
    Next
        
    page.ws.Eval(sbCode.tostring,Null)
        
    page.ws.flush
    End Sub

    [/COD
     
    Last edited: Aug 14, 2018
    Johan Hormaza and joulongleu like this.
  6. liulifeng77

    liulifeng77 Active Member Licensed User

    nice,I like it!。by the way, a weird problem.,when I clicked the item, sub XX_clicked(itemid as string) will be fired twice.
    Code:
    Sub choice_Clicked(itemId As String)
        selected_class.Clear
        selected_class = ABMShared.ABMComboGetChecked(
    page,"choice")
    '    Dim out As String = ABMShared.List2JSON(selected_class)
    '    page.Msgbox("",out,"asel checked","ok",False,"","")
        Log("check")
        
    'show_tab_message
    End Sub
     
    Last edited: Aug 15, 2018
    Mashiane likes this.
  7. Mashiane

    Mashiane Expert Licensed User

    @liulifeng77 , you are right. I tested what you are saying and just like I expected, the checkbox click event and then the combo box click event are being fired one after another. I guess the checkbox click bubbles up to the next event in line, thus the firing twice.

    As you are aware, I did an "element inspection" which gives the HTML content of the page/component. This is how ABM has defined the checkbox and combo to have those events fired. I guess if there was a way to turn an event off it could work, but then again, I don't know what other consequences it will have. I'm not prepared to find out.

    1. see <li> <div onclick = "comboclickarray(..." and further down "checkboxclickarray..." on input

    Finally, in my case I am not trapping the combo click event as I just want to return the selected items. That was the point initially and I am not going any further than that.

    Perhaps in your case, you can have yourself a button that one can click to process your selection and not trap the combo_click event at all, I see you assign that to a list. Or else play around with jQuery and see where it can take you.

    CheckComboClick.png

    PS: I will request AB to have a look at it.
     
    Last edited: Aug 15, 2018
    Johan Hormaza likes this.
  8. Mashiane

    Mashiane Expert Licensed User

    As per post number 1, the items in my ComboCheckBox component are loaded from the Forums table. I want when the contact details are listed, the forum a person belongs to gets listed as a chip.

    ComboChecked2Chips.png

    When loading records to a table, the records to display are filtered, an executemaps is ran and each record is returned as a map. So for each record processed, in the forums table I run

    Code:
    Dim forums As String = resMap.GetDefault("forums""")
            
    If forums = "" Then
                rCellValues.Add(
    "{NBSP}")
            
    Else
                
    Dim abmc As ABMContainer = ABMShared.ABMComboChecked2Chips(page,resMap,"forums","Forums","id","name")
                rCellValues.Add(abmc)
            
    End If
            
    'Add theme to the cell
            rCellThemes.Add("nocolor")
    What ABMComboChecked2Chips does is: for the selected record, the complete map is passed to it, the field to read in the current record is forums, this will return a list of all forums the user belongs to as ["1","2"] as per previonsly checked forums from the combocheck box, depending on what was saved in the database as selected by end user.

    i.e then for each of the selected forums in [1,2], go to the "Forums" table", use a field named "id" to return the contents of the "name" field.

    Code:
    'convert combo checked items to chips
    Sub ABMComboChecked2Chips(pg As ABMPage, rMap As Map, sFieldName As String,sSourceTable As String,sSourceIDField As String,ssourcedescriptionfield As StringAs ABMContainer
        
    'read the id of the record
        Dim sid As String = rMap.GetDefault("id","")
        
    'initialize the container
        Dim ItemCont As ABMContainer
        ItemCont.Initialize(pg, 
    "combo2chip" & sid, "")
        ItemCont.IsResponsiveContainer = 
    True
        ItemCont.AddRowsM(
    1,False,0,0"").AddCellsOSMP(1,0,0,0,12,12,12,0,0,0,0,"")
        ItemCont.BuildGrid 
    'IMPORTANT once you loaded the complete grid AND before you start adding components
       
        
    Dim msFieldName As String = rMap.GetDefault(sFieldName.ToLowerCase,"")
       
        
    'open the connection to the database
        Dim jSQL As SQL = SQLGet
        
    'convert the checked items to a list
        Dim itmItems As List = Json2List(msFieldName)
        
    For Each chkItem As String In itmItems
            
    Dim chkName As String = SQLSelectSingleResult(jSQL, $"select ${ssourcedescriptionfield} from ${sSourceTable} where ${sSourceIDField} = ?"$Array As String(chkItem))
    Dim sKey As String = $"combo2chip"${sid}${chkItem}"$
            
    Dim abmc As ABMChip
            abmc.Initialize(pg,sKey,chkName,
    False,"")
            abmc.Tag = chkName
            ItemCont.Cell(
    1,1).AddComponent(abmc)
        
    Next
        SQLClose(jSQL)
        
    Return ItemCont
    End Sub
    I will just now theme my chips for follow my user defined color scheme.

    Ta!
     
    Don Oso and Johan Hormaza like this.
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice