B4J Tutorial [ABMaterial] Custom component ABMTableGrid (team project)

Discussion in 'B4J Tutorials' started by alwaysbusy, Feb 6, 2017.

  1. alwaysbusy

    alwaysbusy Well-Known Member Licensed User

    One of the most asked for components in ABMaterial is an editable Grid. We already have ABMTable and ABMTableMutable, but they have their limitations when it comes to being editable. So, as this kind of object is huge, I suggest we make it a Team Effort to build one.

    I did a look around and found a rather nice one jsGrid http://js-grid.com/ which is feature rich, easy to use and fits rather well with the ABMaterial look and feel.

    I made the first part: modified the default CSS so it works with ABMaterial (checkboxes and combos did not work), created a basic start for the class and how to use it in a project.

    Copy the CSS and JS files from the attachment into the css/custom/ and /js/custom/ folders of the ABMaterial www folder.

    Start with localhost:51042/GridDemo


    My current result looks like this (allows adding, deleting, changing, sorting and filtering):

    upload_2017-2-6_12-34-40.png

    The ABMTableGrid class (see also attachment):
    Code:
    'Class module
    Sub Class_Globals
       
    Public ABMComp As ABMCustomComponent
       
    Private cmbCountries As String
       
    Private DBdata As String
    End Sub

    'Initializes the object. Countries and data are Json Strings
    Public Sub Initialize(InternalPage As ABMPage, ID As String, Countries As String, data As String)
       ABMComp.Initialize(
    "ABMComp", Me, InternalPage, ID)
       cmbCountries = Countries
       DBdata = data
    End Sub

    Sub ABMComp_Build(internalID As StringAs String
      
    Return $"<div id="${internalID}"></div><script>var _${internalID};</script>"$
    End Sub

    Sub ABMComp_FirstRun(InternalPage As ABMPage, internalID As String)
      
    Dim script As String = $"_${internalID} = $("#${internalID}").jsGrid({
      height: "100%",
      width: "100%",
      filtering: true,
         inserting: true,
      editing: true,
      sorting: true,
      paging: true,
      autoload: true,
      pageSize: 10,
      pageButtonCount: 5,
      deleteConfirm: "Do you really want to delete the client?",
      controller: {
           loadData: function(filter) {
             return $.grep(${
    DBdata}, function(client) {
              var fName = filter.Name.toUpperCase();
               var fAddress = filter.Address.toUpperCase();
               return (!filter.Name || client.Name.toUpperCase().indexOf(fName) > -1)
         && (!filter.Age || client.Age === filter.Age)
         && (!filter.Address || client.Address.toUpperCase().indexOf(fAddress) > -1)
         && (!filter.Country || client.Country === filter.Country)
         && (filter.Married === undefined || client.Married === filter.Married);
           });          
           },
           insertItem: function(insertingClient) {
            var json = JSON.stringify(insertingClient);
          b4j_raiseEvent('${
    ABMComp.ID}_inserted', {'value':json});
         },
         updateItem: function(updatingClient) {
             var json = JSON.stringify(updatingClient);
             b4j_raiseEvent('${
    ABMComp.ID}_updated', {'value':json});
           },
         deleteItem: function(deletingClient) {
             var json = JSON.stringify(deletingClient);
         b4j_raiseEvent('${
    ABMComp.ID}_deleted', {'value':json});
         }
         },
      fields: [
           { name: "Name", type: "text", width: 150 },
      { name: "Age", type: "number", width: 50 },
      { name: "Address", type: "text", width: 200 },
      { name: "Country", type: "select", items: ${
    cmbCountries}, valueField: "Id", textField: "Name" },
      { name: "Married", type: "checkbox", title: "Is Married", sorting: false},
      { type: "control" }
      ]
      });"$


      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 SetDBData(data As String)
       DBdata = data
    End Sub

    Sub ABMComp_Refresh(InternalPage As ABMPage, internalID As String)
      
    Dim script As String = $"$("#${internalID}").jsGrid("refresh");"$
      InternalPage.ws.Eval(script, 
    Null)
    End Sub

    ' do the stuff needed when the object is removed
    Sub ABMComp_CleanUp(InternalPage As ABMPage, internalID As String)

    End Sub
    Usage:
    Code:
    Sub Class_Globals
       ...
       
    Dim myTableGrid As ABMTableGrid
       
    ' fake database for demo purposes
       Dim database As Map
    End Sub

    ' make sure you copied the files from the .zip in the right folders
    public Sub BuildPage()
       ...
       
    page.AddExtraCSSFile("custom/jsgrid.min.css")
       
    page.AddExtraCSSFile("custom/jsgrid-theme.min.css")
       
    page.AddExtraJavaScriptFile("custom/jsgrid.min.js")
       ...
    End Sub

    public Sub ConnectPage()
    '   connecting the navigation bar


    '   init all your own variables (like a List, Map) and add your components

       
    ' as for the demo, we fake a real database here
       LoadFakeData

       
    page.Cell(2,1).SetFixedHeight(500)

       
    ' for our combo, we add a Json string counting the countries
       myTableGrid.Initialize(page"myTableGrid"$"[
      { Name: "", Id: 0 },
      { Name: "United States", Id: 1 },
      { Name: "Canada", Id: 2 },
      { Name: "United Kingdom", Id: 3 },
      { Name: "France", Id: 4 },
      { Name: "Brazil", Id: 5 },
      { Name: "China", Id: 6 },
      { Name: "Russia", Id: 7 }
      ]"$
    , LoadData)
         
    page.Cell(2,1).AddComponent(myTableGrid.ABMComp)

       
    ' refresh the page
       page.Refresh
       
    ' Tell the browser we finished loading
       page.FinishedLoading
       
    ' restoring the navigation bar position
       page.RestoreNavigationBarPosition
    End Sub

    Sub LoadData() As String
       
    ' we have to return a Json string
       Dim data As StringBuilder
       data.Initialize
       data.Append(
    "[")
       
    For i = 0 To database.size - 1
         
    If i > 0 Then
           data.Append(
    ",")
         
    End If
         data.Append(database.get(i))
       
    Next
       data.Append(
    "]")
       
    Return data.ToString
    End Sub

    Sub myTableGrid_Inserted(value As Map)

    End Sub

    Sub myTableGrid_Updated(value As Map)

    End Sub

    Sub myTableGrid_Deleted(value As Map)

    End Sub

    Sub LoadFakeData()
       database.Initialize
       database.Put(
    0,$"{"ID": 0,"Name": "Otto Clay","Age": 61,"Country": 6,"Address": "Ap #897-1459 Quam Avenue","Married": false}"$)
       database.Put(
    1,$"{"ID": 1,"Name": "Connor Johnston","Age": 73,"Country": 7,"Address": "Ap #370-4647 Dis Av.","Married": false}"$)
       database.Put(
    2,$"{"ID": 2,"Name": "Lacey Hess","Age": 29,"Country": 7,"Address": "Ap #365-8835 Integer St.","Married": false}"$)
       database.Put(
    3,$"{"ID": 3,"Name": "Timothy Henson","Age": 78,"Country": 1,"Address": "911-5143 Luctus Ave","Married": false}"$)
       database.Put(
    4,$"{"ID": 4,"Name": "Ramona Benton","Age": 43,"Country": 5,"Address": "Ap #614-689 Vehicula Street","Married": true}"$)
       database.Put(
    5,$"{"ID": 5,"Name": "Ezra Tillman","Age": 51,"Country": 1,"Address": "P.O. Box 738, 7583 Quisque St.","Married": true}"$)
       database.Put(
    6,$"{"ID": 6,"Name": "Dante Carter","Age": 59,"Country": 1,"Address": "P.O. Box 976, 6316 Lorem, St.","Married": false}"$)
       database.Put(
    7,$"{"ID": 7,"Name": "Christopher Mcclure","Age": 58,"Country": 1,"Address": "847-4303 Dictum Av.","Married": true}"$)
       database.Put(
    8,$"{"ID": 8,"Name": "Ruby Rocha","Age": 62,"Country": 2,"Address": "5212 Sagittis Ave","Married": false}"$)
       database.Put(
    9,$"{"ID": 9,"Name": "Imelda Hardin","Age": 39,"Country": 5,"Address": "719-7009 Auctor Av.","Married": false}"$)
       database.Put(
    10,$"{"ID": 10,"Name": "Jonah Johns","Age": 28,"Country": 5,"Address": "P.O. Box 939, 9310 A Ave","Married": false}"$)
       database.Put(
    11,$"{"ID": 11,"Name": "Herman Rosa","Age": 49,"Country": 7,"Address": "718-7162 Molestie Av.","Married": true}"$)
       database.Put(
    12,$"{"ID": 12,"Name": "Arthur Gay","Age": 20,"Country": 7,"Address": "5497 Neque Street","Married": false}"$)
       database.Put(
    13,$"{"ID": 13,"Name": "Xena Wilkerson","Age": 63,"Country": 1,"Address": "Ap #303-6974 Proin Street","Married": true}"$)
       database.Put(
    14,$"{"ID": 14,"Name": "Lilah Atkins","Age": 33,"Country": 5,"Address": "622-8602 Gravida Ave","Married": true}"$)
       database.Put(
    15,$"{"ID": 15,"Name": "Malik Shepard","Age": 59,"Country": 1,"Address": "967-5176 Tincidunt Av.","Married": false}"$)
       database.Put(
    16,$"{"ID": 16,"Name": "Keely Silva","Age": 24,"Country": 1,"Address": "P.O. Box 153, 8995 Praesent Ave","Married": false}"$)
       database.Put(
    17,$"{"ID": 17,"Name": "Hunter Pate","Age": 73,"Country": 7,"Address": "P.O. Box 771, 7599 Ante, Road","Married": false}"$)
       database.Put(
    18,$"{"ID": 18,"Name": "Mikayla Roach","Age": 55,"Country": 5,"Address": "Ap #438-9886 Donec Rd.","Married": true}"$)
       database.Put(
    19,$"{"ID": 19,"Name": "Upton Joseph","Age": 48,"Country": 4,"Address": "Ap #896-7592 Habitant St.","Married": true}"$)
       database.Put(
    20,$"{"ID": 20,"Name": "Jeanette Pate","Age": 59,"Country": 2,"Address": "P.O. Box 177, 7584 Amet, St.","Married": false}"$)
       database.Put(
    21,$"{"ID": 21,"Name": "Kaden Hernandez","Age": 79,"Country": 3,"Address": "366 Ut St.","Married": true}"$)
       database.Put(
    22,$"{"ID": 22,"Name": "Kenyon Stevens","Age": 20,"Country": 3,"Address": "P.O. Box 704, 4580 Gravida Rd.","Married": false}"$)
       database.Put(
    23,$"{"ID": 23,"Name": "Jerome Harper","Age": 31,"Country": 5,"Address": "2464 Porttitor Road","Married": false}"$)
       database.Put(
    24,$"{"ID": 24,"Name": "Jelani Patel","Age": 36,"Country": 2,"Address": "P.O. Box 541, 5805 Nec Av.","Married": true}"$)
       database.Put(
    25,$"{"ID": 25,"Name": "Keaton Oconnor","Age": 21,"Country": 1,"Address": "Ap #657-1093 Nec, Street","Married": false}"$)
       database.Put(
    26,$"{"ID": 26,"Name": "Bree Johnston","Age": 31,"Country": 2,"Address": "372-5942 Vulputate Avenue","Married": false}"$)
       database.Put(
    27,$"{"ID": 27,"Name": "Maisie Hodges","Age": 70,"Country": 7,"Address": "P.O. Box 445, 3880 Odio, Rd.","Married": false}"$)
       database.Put(
    28,$"{"ID": 28,"Name": "Kuame Calhoun","Age": 39,"Country": 2,"Address": "P.O. Box 609, 4105 Rutrum St.","Married": true}"$)
       database.Put(
    29,$"{"ID": 29,"Name": "Carlos Cameron","Age": 38,"Country": 5,"Address": "Ap #215-5386 A, Avenue","Married": false}"$)
       database.Put(
    30,$"{"ID": 30,"Name": "Fulton Parsons","Age": 25,"Country": 7,"Address": "P.O. Box 523, 3705 Sed Rd.","Married": false}"$)
       database.Put(
    31,$"{"ID": 31,"Name": "Wallace Christian","Age": 43,"Country": 3,"Address": "416-8816 Mauris Avenue","Married": true}"$)
       database.Put(
    32,$"{"ID": 32,"Name": "Caryn Maldonado","Age": 40,"Country": 1,"Address": "108-282 Nonummy Ave","Married": false}"$)
       database.Put(
    33,$"{"ID": 33,"Name": "Whilemina Frank","Age": 20,"Country": 7,"Address": "P.O. Box 681, 3938 Egestas. Av.","Married": true}"$)
       database.Put(
    34,$"{"ID": 34,"Name": "Emery Moon","Age": 41,"Country": 4,"Address": "Ap #717-8556 Non Road","Married": true}"$)
       database.Put(
    35,$"{"ID": 35,"Name": "Price Watkins","Age": 35,"Country": 4,"Address": "832-7810 Nunc Rd.","Married": false}"$)
    End Sub
    So this is were you guys can come in!

    Some suggestions:

    1. Adapt my class so the fields are generic (maybe by passing a Map with field definitions in initialize and then 'build' the correct JavaScript string in FirstRun.
    2. I only set some basic parameters (like sorting, filtering etc), but this component has a lot more to offer. They can become properties of the class. See the jsGrid link at the top of this article.

    And please share your changes in this topic, so everyone can profit from it!

    Lets do this :)
     

    Attached Files:

    Last edited: Feb 8, 2017
  2. Mashiane

    Mashiane Well-Known Member Licensed User

    Hi there...

    I have just taken a look at this and tried your suggestions. I'm however stuck with the 'select' type, for some reason (bang head), it's showing [object object]

    TableGrid.png

    This is what has been achieved so far...

    1. Dynamic Column addition including name and title and width and alignment. MashBuildColumnsMethod
    2. Added some properties for the grid for its options e.g. filtering, hide headings etc.
    3. Added a 'gender' selection column to the demo..

    Known Issues

    [Object object] showing on select fields: When i do a msgbox, the right content appears though.

    I'd like to use this and would use some advise, thanks for this component.

    Kind Regards

    Mashy

    The code..

    1. Text columns are left aligned by default
    2. Number columns are right aligned
    3. Checkboxes are centered
    4.1 Initialize the abmtablegrid... InitializeMash
    4.2 Add the columns with their respective types
    4.3 If you have a select, initialize it with the items (after adding the its column)
    4.4. Set the data

    Code:
    myTableGrid.InitializeMash(page,"mashgrid")
        
    'lets add the columns
        myTableGrid.MashAddText("Name","Full Name",150)
        myTableGrid.MashAddNumber(
    "Age","Age",50)
        myTableGrid.MashAddText(
    "Address","Residential Address",200)
        myTableGrid.MashAddSelect(
    "Country","Country of Birth",100)
        myTableGrid.MashAddCheckBox(
    "Married","Married?",60)
        
    'myTableGrid.MashAddSelect("Gender","Gender","")
        myTableGrid.MashAddControl
     
         
    Dim countries As Map
        countries.Initialize
        countries.put(
    "0","")
        countries.put(
    "1","United States")
        countries.put(
    "2","Canada")
        countries.put(
    "3","United Kingdom")
        countries.put(
    "4","France")
        countries.put(
    "5","Brazil")
        countries.put(
    "6","China")
        countries.put(
    "7","Russia"
        myTableGrid.MashSetSelect(
    "Country","Id","Name", countries)
     
        
    Dim gender As Map
        gender.Initialize
        gender.Put(
    "0","")
        gender.Put(
    "1","Male")
        gender.Put(
    "2","Female")
        myTableGrid.MashSetSelect(
    "Gender","Id","Name", gender)
        
    'myTableGrid.Heading = False
        'myTableGrid.Filtering = True
        myTableGrid.SetDBData(LoadData)
     
          
    page.Cell(2,1).AddComponent(myTableGrid.ABMComp)
    To run, copy the www folder (ABMaterial 3.75)
     

    Attached Files:

    PCastagnetti and joulongleu like this.
  3. PCastagnetti

    PCastagnetti Member Licensed User


    I attach the code that fixes the problem (only for sample)

    Code:
    private Sub MashJoin(lst As List, Delim As StringAs String
        
    Dim lStr As StringBuilder
        lStr.Initialize
        
    For Each strValue As String In lst
            strValue= strValue.Replace(
    "{Name: ","")
            strValue=strValue.SubString2(
    0,strValue.IndexOf(","))
            lStr.Append(strValue).Append(Delim)
        
    Next
        
    If lStr.ToString.EndsWith(Delim) Then
            lStr.Remove(lStr.Length-Delim.length,lStr.Length)
        
    End If
        
    Return lStr.tostring
    End Sub
    I know it would correct the incoming list, but it was just to show how the string should be created

    items: ["", "United States", "Canada", "United Kingdom"]

    instead of

    items: [{Name: "", Id: 0},{Name: "United States", Id: 1},{Name: "Canada", Id: 2},{Name: "United Kingdom", Id: 3}]



    I hope it can be useful
     
    joulongleu likes this.
  4. Mashiane

    Mashiane Well-Known Member Licensed User

    Thanks, but from the look of things its expecting the correct select definition. If you look at the data, the country is numeric and thus links back to the defined names. After taking another look, I realized that everything is in fact 'case SenSitive', after fixing that it worked. The issue however is that whilst in edit mode you are able to add a record and select an item from the list, it does not show on the saved record select control but as you click 'edit' it shows. Still investigating. Thanks for the heads up.

    PS: Sadly this component is not responsive so one can only use it with PCs it seems.
     
    PCastagnetti and joulongleu like this.
  5. Mashiane

    Mashiane Well-Known Member Licensed User

    Latest, we are able to add column dynamically and the filter part of the component builds the filters also based on the columns added.

    In this example I tried to add a date component, dololo. The gender seems to work ok but it does not display saved data from the table, I think its because of the structure of the data. I will try and match that and see what happens.

    Code:
    myTableGrid.InitializeMash(page,"mashgrid")
        
    'lets add the columns
        myTableGrid.MashAddText("Name","Full Name",150)
        myTableGrid.MashAddNumber(
    "Age","Age",50)
        myTableGrid.MashAddDate(
    "HireDate","Hire Date",60)
        myTableGrid.MashAddText(
    "Address","Residential Address",200)
        myTableGrid.MashAddSelect(
    "Country","Country of Birth",100)
        myTableGrid.MashAddCheckBox(
    "Married","Married?",60)
        myTableGrid.MashAddSelect(
    "Gender","Gender",60)
        myTableGrid.MashAddRating(
    "Rating","Rating",50)
        myTableGrid.MashAddControl
     
        
    'the ids should be added as string
        Dim countries As Map
        countries.Initialize
        countries.put(
    "0","")
        countries.put(
    "1","United States")
        countries.put(
    "2","Canada")
        countries.put(
    "3","United Kingdom")
        countries.put(
    "4","France")
        countries.put(
    "5","Brazil")
        countries.put(
    "6","China")
        countries.put(
    "7","Russia"
        countries.Put(
    "8""South Africa")
        myTableGrid.MashSetSelect(
    "Country","Id","Name", countries)
     
        
    'ids should be added as string
        Dim gender As Map
        gender.Initialize
        gender.Put(
    "0","")
        gender.Put(
    "1","Male")
        gender.Put(
    "2","Female")
        myTableGrid.MashSetSelect(
    "Gender","Id","Name", gender)
        
    'myTableGrid.Heading = False
        'myTableGrid.Filtering = True
        myTableGrid.SetDBData(LoadData)
     
        myTableGrid.MashSetRequired(
    "name",True)
        myTableGrid.MashSetRequired(
    "age"True)
        myTableGrid.MashSetRequired(
    "country"True)
        myTableGrid.MashSetNumericValueType(
    "country")
        myTableGrid.MashSetSelectedIndex(
    "Country","8")
     
        
    page.Cell(2,1).AddComponent(myTableGrid.ABMComp)
    TableGrid.png
     

    Attached Files:

    Molchyn and joulongleu like this.
  6. joulongleu

    joulongleu Member

    Hi Mashiane: very good In this example but Sub myTableGrid_Updated(value As Map) ,can't use
     
  7. Mashiane

    Mashiane Well-Known Member Licensed User

    What is the name that you have used in your .Initialize method, in my example its mashgrid so my event will be

    Code:
    mashgrid_updated(value as Map) etc.
    Honestly I did'nt test the events for this, I'm sure I can cook up a CRUD example soon.
     
    joulongleu likes this.
Loading...