B4J Tutorial [SithasoDaisy5] - Create a WebApp From An Excel Spreadsheet / A Database - Part 1

Hi Fam...

Let's say you have an excel sheet / a database that you want to convert to a WebApp, how can you do it?

SithasoDaisy5 has a basic, simple app builder that can help you create a table and a preference dialog.

To create your app, you will use a project template of SithasoDaisy5.

Let's begin.. we will start with the manual process of doing this, then deal with the automated leg of it.

Before we continue, please ensure that your b4j environment is setup properly. You can do so by following this tutorial.


Have fun!
 
Last edited:

Mashiane

Expert
Licensed User
Longtime User
1. Start the Demo


For this you will use the Table Builder and Preference Dialog Builder

1744803524688.png


2. Creating a Table

First we need to create a table that will store our records. Click Table Builder.

Click Add in the table and activate the preference dialog. We will create a table for our database called people. A single person is called person whilst a collection is called People. The display value is the field to display when a person is deleted. For this we will show the first name.

Each person record will have an id, let's make this a primary key, it will not be auto-increment. Our table will have an alpha chooser and also a column chooser.

1744803628043.png


Click Apply to save the table.

The table listing will be saved.

1744803773318.png


The alpha chooser for this table is the table name, thus the chip with P on the table. If we had another table with another name, its prefix will be listed there. Clicking an alpha chooser filters the table listing to only list those records starting with that name provided.

The column chooser here shows the column names of the table. These you can toggle to hide and show a table.
 

Mashiane

Expert
Licensed User
Longtime User
3. Creating Table Fields

Each table we will create will have fields, these will have titles and other properties. So we will use the preference builder to create them.

Click the fields action button, this will activate the preference dialog builder. The preference builder will start empty.

1744804052571.png


We will create all the fields we need for our table. We will start with the id i.e. primary key.

3.1 Creating the ID primary key.

On the available components, click the TextBox, this will activate the preference dialog for a textbox, change its properties. We dont want this field to appear on the table and we also dont want it to show on the table. To hide it in the table, we will have to set the column type to None. When done, click Apply to store the field definition. We are using local storage for these schemas.

1744804280822.png


3.2 Adding other fields

You can use the same process of choosing the Available components or adding or cloning the fields that will be in your table. For example, for the FamilyTree, we added avatars, file choosers, radio groups and other fields. Here you are able to define which components will be linked to each of the fields you will use.

1744804504407.png


3.3 When you create a preference dialog field, you are also able to have a preview of what your UI will look like. For example, for the gender radio group, the default value is Male, we have used a Key Value for the Options and on the right, the preview shows the title and the values that will be displayed.

1744804622107.png


After we completed defining the fields we want to be in our database, we have a final preference dialog that represents the final version of our app.

1744804760583.png


This is how the Family Tree MySQL REST API (Php) app was designed.


I am attaching here the complete structure of our UI definition, including the table and preference dialog. You can import it to explore.
 

Attachments

  • people (2).zip
    1.9 KB · Views: 134
Last edited:

Mashiane

Expert
Licensed User
Longtime User
4. Generating the UI for our app

The final version of our WebApp will look like this, composed of a table to list our family members and then a preference dialog for us to Add Edit each of the famly members.

1744804936769.png



In the preference builder, we have buttons to perform some functions, including code generation.

1744805076483.png


Clicking the code generation button, will generate the code needed for you to generate the UI you see above. This is the code you will use in your SithasoDaisy5 project.

5. People (source code)

B4X:
'****** Add this to pgIndex/BROWSERApp.AddDatabaseSchemas
app.AddDataModel("people", "id", false)
app.AddDataModelLongTexts("people", Array("profiledisplay","clannames"))
app.AddDataModelStrings("people", Array("id","gender","firstname","middlename","lastname","suffix","dateofbirth","placeofbirth","dateofdeath","placeofdeath","father","mother","parentsstatus"))
app.AddDataModelBooleans("people", Array("isliving"))
'****** Add this to pgIndex/BROWSERApp.CreateDrawerMenu
drawermenu.AddMenuItemIconText("pg-people", "", "People", False)
'****** Add this to pgIndex/BROWSERApp.drawermenu_ItemClick [Case Statement]
'****** Ensure you have created a code module named 'pgPeople' also with the source code.
Case "people"
    pgPeople.Show(App)
    '****** Open your database and execute this script to create your table
    CREATE TABLE IF NOT EXISTS `people` (`id` VARCHAR(255) PRIMARY KEY, `profiledisplay` LONGTEXT, `gender` VARCHAR(255), `firstname` VARCHAR(255), `middlename` VARCHAR(255), `lastname` VARCHAR(255), `suffix` VARCHAR(255), `dateofbirth` VARCHAR(255), `isliving` BOOL, `placeofbirth` VARCHAR(255), `dateofdeath` VARCHAR(255), `placeofdeath` VARCHAR(255), `father` VARCHAR(255), `mother` VARCHAR(255), `parentsstatus` VARCHAR(255), `clannames` LONGTEXT)
    '*****
    Sub Process_Globals
        Private BANano As BANano                            'ignore
        Private app As SDUI5App                         'ignore
        Private tblPeople As SDUI5Table         'ignore
        Private prefPeople As SDUI5Preferences  'ignore
        Private SDUI5Column1 As SDUI5Column             'ignore
        Private SDUI5Column2 As SDUI5Column2                'ignore
        Private Mode As String = ""                     'CRUD control
        Private toDeleteID As String                        'id of item to delete
        Private toDeleteName As String                  'name of item to delete
        Public name As String = "people"
        Public title As String = "People"
        Public icon As String = "./assets/page.svg"
        Public color As String = "#000000"
        'define foreign table maps
        Private PeopleObject As Map
        Private PeopleObjectM As Map
    End Sub
    'executed when the page is shown
    Public Sub Show(MainApp As SDUI5App)
        app = MainApp
        app.PagePause
        'load the layout
        BANano.LoadLayout(app.PageView, "peopleview")
        'update navbar title on baselayout
        pgIndex.UpdateTitle("People")
        'lets set up the table
        tblPeople.Title = "People"
        BANano.Await(BuildTablePeople)
        'setup the preference dialog
        BANano.Await(BuildPreferencePeople)
        'mount the records
        BANano.Await(MountPeople)
        app.PageResume
    End Sub
    Private Sub BuildTablePeople
        'lets create the table columns
        tblPeople.Title = "People"
        tblPeople.HasAlphaChooser = true
        tblPeople.AlphaChooserColumn = "firstname"
        tblPeople.HasColumnChooser = true
        tblPeople.AddNewTooltip = "Add a Person"
        tblPeople.RefreshTooltip = "Refresh People"
        'tblPeople.DeleteAllTooltip = "Delete all People"
        'tblPeople.UploadToolbarTooltip = "Upload People"
        'tblPeople.DownloadToolbarTooltip = "Download People"
        tblPeople.BackTooltip = "Go back to Dashboard"
        tblPeople.HasEdit = True
        tblPeople.EditTooltip = "Edit Person"
        tblPeople.HasClone = True
        tblPeople.CloneTooltip = "Clone Person"
        tblPeople.HasDelete = True
        tblPeople.DeleteTooltip = "Delete Person"
        tblPeople.AlphaChooserColumn = "firstname"
        'lets build the table columns
        tblPeople.AddColumn("id", "#")
        tblPeople.SetColumnVisible("id", False)
        tblPeople.AddColumnAvatar("profiledisplay", "Profile Display", "80px", "circle")
        tblPeople.AddColumnBadge("gender", "Gender", "neutral")
        tblPeople.AddColumnTextBox("firstname", "First Name", false)
        tblPeople.AddColumnTextBox("middlename", "Middle Name", false)
        tblPeople.SetColumnVisible("middlename", False)
        tblPeople.AddColumnTextBox("lastname", "Last Name", false)
        tblPeople.AddColumnTextBox("suffix", "Suffix", false)
        tblPeople.SetColumnVisible("suffix", False)
        tblPeople.AddColumnDatePicker("dateofbirth", "Date of Birth", false, "Y-m-d", "F j, Y", False, false, False, "en")
        tblPeople.AddColumnCheckBox("isliving", "Living", "success", false)
        tblPeople.AddColumnTextBox("placeofbirth", "Place of Birth", false)
        tblPeople.SetColumnVisible("placeofbirth", False)
        tblPeople.AddColumnDatePicker("dateofdeath", "Date of Death", false, "Y-m-d", "F j, Y", False, false, False, "en")
        tblPeople.SetColumnVisible("dateofdeath", False)
        tblPeople.AddColumnTextBox("placeofdeath", "Place of Death", false)
        tblPeople.SetColumnVisible("placeofdeath", False)
        tblPeople.AddColumnSelect("father", "Father", false, True, CreateMap())
        tblPeople.SetColumnVisible("father", False)
        tblPeople.AddColumnSelect("mother", "Mother", false, True, CreateMap())
        tblPeople.SetColumnVisible("mother", False)
        tblPeople.AddColumnSelect("parentsstatus", "Parents Status", false, True, CreateMap("married":"Married","divorced":"Divorced","nevermarried":"Never Married","other":"Other","separated":"Separated"))
        tblPeople.SetColumnVisible("parentsstatus", False)
        'add edit/delete/clone buttons
        tblPeople.AddDesignerColums
        'move back button to the end
        tblPeople.MoveBackButton
    End Sub
    Private Sub BuildPreferencePeople
        prefPeople.AddPropertyTextBox("id", "#", "", true)
        prefPeople.SetPropertyVisible("id", False)
        prefPeople.AddPropertyAvatar("profiledisplay", "Profile Display", "80px", "circle", "./assets/mashy.jpg")
        prefPeople.SetPropertyAvatarRing("profiledisplay", false, "primary", "2", "base-100")
        prefPeople.SetPropertyAvatarOnline("profiledisplay", false, false)
        prefPeople.AddPropertyCamera("selectprofile", "Choose Profile Image", "80px", "42px", "error", "#ffffff")
        Dim genderoptions As Map
        genderoptions.Initialize
        genderoptions.put("Male", "Male")
        genderoptions.put("Female", "Female")
        prefPeople.AddPropertyRadioGroup("gender", "Gender", "Male", "neutral", "#22c55e", genderoptions)
        prefPeople.AddPropertyTextBox("firstname", "First Name", "", true)
        prefPeople.AddPropertyTextBox("middlename", "Middle Name", "", false)
        prefPeople.AddPropertyTextBox("lastname", "Last Name", "", true)
        prefPeople.AddPropertyTextBox("suffix", "Suffix", "", false)
        prefPeople.AddPropertyDatePicker("dateofbirth", "Date of Birth", "", false, "Y-m-d", "F j, Y", "en")
        prefPeople.AddPropertyCheckBox("isliving", "Living", true, "success")
        prefPeople.SetPropertyChecked("isliving", true)
        prefPeople.AddPropertyTextBox("placeofbirth", "Place of Birth", "", false)
        prefPeople.AddPropertyDatePicker("dateofdeath", "Date of Death", "", false, "Y-m-d", "F j, Y", "en")
        prefPeople.AddPropertyTextBox("placeofdeath", "Place of Death", "", false)
        Dim fatheroptions As Map
        fatheroptions.Initialize
        prefPeople.AddPropertySelect("father", "Father", "", false, fatheroptions)
        Dim motheroptions As Map
        motheroptions.Initialize
        prefPeople.AddPropertySelect("mother", "Mother", "", false, motheroptions)
        Dim parentsstatusoptions As Map
        parentsstatusoptions.Initialize
        parentsstatusoptions.put("married", "Married")
        parentsstatusoptions.put("divorced", "Divorced")
        parentsstatusoptions.put("nevermarried", "Never Married")
        parentsstatusoptions.put("other", "Other")
        parentsstatusoptions.put("separated", "Separated")
        prefPeople.AddPropertyFilter("parentsstatus", "Parents Status", "married", "neutral", "#22c55e", parentsstatusoptions)
        prefPeople.AddPropertyTextArea("clannames", "Clan Names", "", false, "4")
    End Sub
    'load People records from the database
    Private Sub MountPeople
        SDUI5Column1.Size = 12
        SDUI5Column2.Visible = False
        'turn to add mode
        app.PagePause
        'load foreign tables
        BANano.Await(LoadPeople)
        'select People from the database
        Dim db As SDUIMySQLREST
        db.Initialize(Me, "people", Main.ServerURL, "people")
        'link this api class to the data models
        db.SetSchemaFromDataModel(app.DataModels)
        'the api file will be api.php
        db.ApiFile = "api"
        'we are using an api key to make calls
        db.UseApiKey = True
        'specify the api key
        db.ApiKey = Main.APIKey
        'clear any where clauses
        db.CLEAR_WHERE
        'order by firstname
        db.ADD_ORDER_BY("firstname")
        'order by lastname
        db.ADD_ORDER_BY("lastname")
        'execute a select all
        BANano.Await(db.SELECT_ALL)
        'load foreign tables content on columns
        tblPeople.SetColumnOptions("father", peopleObject)
        tblPeople.SetColumnOptions("mother", peopleObject)
        'load the records to the table
        tblPeople.SetItemsPaginate(db.result)
        app.PageResume
    End Sub
    'executed when the add button is clicked on the table
    Private Sub tblPeople_Add (e As BANanoEvent)
        e.preventdefault
        app.PagePause
        'load foreign tables
        BANano.Await(LoadPeople)
        BANano.Await(AddMode)
        app.PageResume
    End Sub
    'executed when the refresh button is clicked on the table
    Private Sub tblPeople_Refresh (e As BANanoEvent)
        e.PreventDefault
        app.PagePause
        BANano.Await(MountPeople)
        app.pageresume
    End Sub
    'executed when the back button is clicked on the table
    Private Sub tblPeople_Back (e As BANanoEvent)
        e.preventdefault
        'show the dashboard
        pgDashboard.Show(app)
        'Main.MyApp.ShowDashBoard
    End Sub
    'executed to prepare the preference dialog for adding a new record
    private Sub AddMode
        'turn the mode to CREATE
        Mode = "C"
        SDUI5Column1.Size = 8
        SDUI5Column2.Visible = True
        'change the title of the pref dialog
        prefPeople.Title = "Add Person"
        BANano.Await(prefPeople.SetPropertySelectMap("father", peopleObject))
        BANano.Await(prefPeople.SetPropertySelectMap("mother", peopleObject))
        'set the default properties
        BANano.Await(prefPeople.SetPropertyBagDefaults)
        prefPeople.SetPropertyValue("id", app.NextID)
        'focus on the firstname
        prefPeople.SetPropertyFocus("firstname")
    End Sub
    'executed when a table row is being edited
    private Sub EditMode(item As Map)
        'turn the mode to UPDATE
        Mode = "U"
        SDUI5Column1.Size = 8
        SDUI5Column2.Visible = True
        'change the title of the pref dialog
        prefPeople.Title = "Edit Person"
        BANano.Await(prefPeople.SetPropertySelectMap("father", peopleObject))
        BANano.Await(prefPeople.SetPropertySelectMap("mother", peopleObject))
        'set the properties
        banano.Await(prefPeople.SetPropertyBagDefaults)
        item.Remove("selectprofile")
        prefPeople.PropertyBag = item
        'focus on the firstname
        prefPeople.SetPropertyFocus("firstname")
    End Sub
    'executed when a table row is being edited
    private Sub CloneMode(item As Map)
        'turn the mode to UPDATE
        Mode = "C"
        SDUI5Column1.Size = 8
        SDUI5Column2.Visible = True
        'change the title of the pref dialog
        prefPeople.Title = "Add Person"
        BANano.Await(prefPeople.SetPropertySelectMap("father", peopleObject))
        BANano.Await(prefPeople.SetPropertySelectMap("mother", peopleObject))
        'set the properties
        banano.Await(prefPeople.SetPropertyBagDefaults)
        item.Put("id", app.NextID)
        item.Remove("selectprofile")
        prefPeople.PropertyBag = item
        'focus on the firstname
        prefPeople.SetPropertyFocus("firstname")
    End Sub
    'executed when a table row edit button is clicked
    Private Sub tblPeople_EditRow (Row As Int, item As Map)
        app.pagepause
        'load foreign tables
        BANano.Await(LoadPeople)
        Dim sID As String = item.Get("id")
        'execute a read from the database
        Dim db As SDUIMySQLREST
        db.Initialize(Me, "people", Main.ServerURL, "people")
        'assign schema
        db.SetSchemaFromDataModel(app.DataModels)
        'use api key
        db.ApiFile = "api"
        db.UseApiKey = True
        db.ApiKey = Main.APIKey
        'read the record
        Dim rec As Map = BANano.Await(db.READ(sID))
        BANano.Await(EditMode(rec))
        app.pageresume
    End Sub
    'executed when a table row delete button is clicked
    Private Sub tblPeople_DeleteRow (Row As Int, item As Map)
        'get the id and firstname of the item to delete
        toDeleteID = item.Get("id")
        toDeleteName = item.Get("firstname")
        'build the confirmation message
        Dim sMsg As String = $"<h2 class="text-2xl font-bold mt-2">${toDeleteName}</h2><br>Are you sure that you want to delete this Person?"$
        'wait for the user to confirm
        Dim bConfirm As Boolean = BANano.Await(app.ShowSwalConfirmWait("Confirm Delete", sMsg, "Yes", "No"))
        'the user has click No, exit
        If bConfirm = False Then Return
        app.pagepause
        'execute a delete using the id of the Person
        Dim db As SDUIMySQLREST
        db.Initialize(Me, "people", Main.ServerURL, "people")
        db.SetSchemaFromDataModel(app.DataModels)
        db.ApiFile = "api"
        db.UseApiKey = True
        db.ApiKey = Main.APIKey
        BANano.Await(db.DELETE(toDeleteID))
        'reload the People
        BANano.Await(MountPeople)
        app.pageresume
    End Sub
    'executed when a table row clone button is clicked
    Private Sub tblPeople_CloneRow (Row As Int, item As Map)
        app.PagePause
        'load foreign tables
        BANano.Await(LoadPeople)
        Dim sID As String = item.Get("id")
        'execute a read from the database
        Dim db As SDUIMySQLREST
        db.Initialize(Me, "people", Main.ServerURL, "people")
        'assign schema
        db.SetSchemaFromDataModel(app.DataModels)
        'use api key
        db.ApiFile = "api"
        db.UseApiKey = True
        db.ApiKey = Main.APIKey
        'read the record
        Dim rec As Map = BANano.Await(db.READ(sID))
        BANano.Await(CloneMode(rec))
        app.PageResume
    End Sub
    'trap changes on table inline edits
    Private Sub tblPeople_ChangeRow (Row As Int, Value As Object, Column As String, item As Map)
        item.Remove("selectprofile")
        'update the map column with value
        item.Put(Column, Value)
        'update the row contents at this position
        tblPeople.UpdateRow(Row, item)
        '
        'get the CurrentPage
        tblPeople.SaveLastAccessedPage
        'process the update Person
        'execute an update using the id of the Person
        Dim db As SDUIMySQLREST
        db.Initialize(Me, "people", Main.ServerURL, "people")
        db.SetSchemaFromDataModel(app.DataModels)
        db.ApiFile = "api"
        db.UseApiKey = True
        db.ApiKey = Main.APIKey
        db.SetRecord(item)
        'execute an update
        Dim newid As String = banano.Await(db.UPDATE)
        If newid <> "" Then
            app.ShowSwalSuccess("The Person has been saved!")
        Else
            app.ShowSwalError("The Person could not be saved, please try again!")
            Return
        End If
        're-load the People
        banano.Await(MountPeople)
        'go to the last page
        banano.Await(tblPeople.ShowLastAccessedPage)
        tblPeople.SetRowEnsureVisible(Row)
    End Sub
    'executed when a Apply button is clicked on the preference dialog
    Private Sub prefPeople_Yes_Click (e As BANanoEvent)
        e.PreventDefault
        'validate the property bag details
        Dim bValid As Boolean = BANano.Await(prefPeople.IsPropertyBagValid)
        If bValid = False Then
            BANano.Await(app.ShowSwalErrorWait("Person", "The person details are not complete!", "Ok"))
            Return
        End If
        'get the property bag fields as a map
        Dim pb As Map = BANano.Await(prefPeople.PropertyBag)
        pb.Remove("selectprofile")
        app.pagepause
        'open the database and save the record
        Dim db As SDUIMySQLREST
        db.Initialize(Me, "people", Main.ServerURL, "people")
        db.SetSchemaFromDataModel(app.DataModels)
        db.ApiFile = "api"
        db.UseApiKey = True
        db.ApiKey = Main.APIKey
        'pass the map record
        db.SetRecord(pb)
        Select Case Mode
        Case "C"
            'create a new record
            Dim nid As String = BANano.Await(db.CREATE)
        Case "U"
            'update an existing record
            Dim nid As String = BANano.Await(db.UPDATE)
        End Select
        If nid <> "" Then
            app.ShowToastSuccess("Person has been created/updated.")
        Else
            app.ShowToastError("Person has NOT been created/updated. Please try again")
            Return
        End If
        BANano.Await(MountPeople)
        app.pageresume
    End Sub
    'executed when a Cancel button is clicked on the preference dialog
    Private Sub prefPeople_No_Click (e As BANanoEvent)
        SDUI5Column1.Size = 12
        SDUI5Column2.Visible = False
    End Sub
    'this is executed when prefPeople_selectprofile_filechange is fired
    Private Sub prefPeople_selectprofile_filechange(e As BANanoEvent)
        e.PreventDefault
        'has the file been selected
        Dim fileObj As Map = prefPeople.GetFileFromEvent(e)
        'the user cancelled, exit
        If banano.IsNull(fileObj) Or banano.IsUndefined(fileObj) Then Return
        'get file details
        Dim fileDet As FileObject
        fileDet = App.UI.GetFileDetails(fileObj)
        'get the file name
        Dim fn As String = fileDet.FileName
        'you can check the size here
        Dim fs As Long = fileDet.FileSize
        Dim maxSize As Int = App.UI.ToKiloBytes(500)
        If fs > maxSize Then
                app.ShowToastError("File Size is limited to 500KB!")
                Return
        End If
        Dim fText As String = BANano.Await(app.readAsDataURLWait(fileObj))
        'Dim fJSON As Map = BANano.Await(app.readAsJsonWait(fileObj))
        'Dim fBuffer As Object = BANano.Await(app.readAsArrayBufferWait(fileObj))
        'Dim fText As String = BANano.Await(app.readAsTextWait(fileObj))
        'start uploading the file to assets folder
        'fileDet = UI.UploadFileWait(fileObj)
        'fileDet = UI.UploadFileOptionsWait(fileObj, "../assets", "n")
        prefPeople.SetPropertyValue("profiledisplay", fText)
    End Sub
    'Load People
    Private Sub LoadPeople              'ignore
        PeopleObject.Initialize
        PeopleObjectM.Initialize
        Dim db As SDUIMySQLREST
        db.Initialize(Me, "people", Main.ServerURL, "people")
        'link this api class to the data models
        db.SetSchemaFromDataModel(app.DataModels)
        'the api file will be api.php
        db.ApiFile = "api"
        'we are using an api key to make calls
        db.UseApiKey = True
        'specify the api key
        db.ApiKey = Main.APIKey
        'clear any where clauses
        db.CLEAR_WHERE
        'order by firstname
        db.ADD_ORDER_BY("firstname")
        'order by lastname
        db.ADD_ORDER_BY("lastname")
        db.ADD_FIELDS(Array("firstname","id","lastname"))
        'execute a select all
        BANano.Await(db.SELECT_ALL)
        Do While db.NextRow
            Dim rec As Map = db.Record
            Dim sid As String = db.GetString("id")
            Dim sfirstname As String = db.GetString("firstname")
            Dim slastname As String = db.GetString("lastname")
            peopleObject.Put(sid, sfirstname & " " & slastname)
            peopleObjectM.Put(sid, rec)
        Loop
    End Sub

The code before Process_Globals is what you will need in your code module. The rest you will have to put in special places in the template we will use.
 

Mashiane

Expert
Licensed User
Longtime User
Top