B4J Tutorial [BANanoVuetifyAD3] VFlexDialog + MySQL CRUD REST API (PHP) - using Axios

Hi there

Download BVAD3 7.35+
Download Source Code

Its its your first time with BVAD3, please follow instructions on Github ReadMe on how to get started

By the time you finish this, you will have a working VFlexDialog based app with MySQL CRUD REST API (PHP) working.

Category Listing

VFlexCRUDRESTAPI.jpg


Creating Categories

AddCategory.jpg


Updating Categories

EditCategory.jpg


Deleting Categories

DeleteCategory.jpg


Related Awesome Material

VFlexDialog - Inspired by PreferenceDialog
VFlexDialog Builder
Household Survey using VFlexDialog & BANanoKeyStore
Travel Trax MVP for Travel & Subsistence Logs (some section built with VFlexDialog)
WhatsApp CRM (built with VFlexDialog)

and mostly.

PHP-CRUD-API from Github for MySQL, SQLite, PostGres, MariaDB

Ok let's get to it..​

 
Last edited:

Mashiane

Expert
Licensed User
Longtime User
Step 1: Creating your BVAD3 App & Setting up the BackEnd

1.1 Download this b4xtemplate and save it in your Additional Libraries folder. B4J should be closed.
1.2. Open B4J, click File > New > BVAD3 Crud Rest Api PhP

1662733216769.png


1.3. Enter the project name to create your project

1.4 Creating the backend MySQL database

Create a MySQL database called wardlog. You can then execute this script to create your table.

B4X:
CREATE TABLE `categories` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `color` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1

1.5 Updating mysqlconfig.php file

Open the Files Tab, open up the mysqlconfig.php file and update it with your connection credentials for MySQL. We are adding this in case we want to call db scripts internally in the app outside the CRUD REST API.

B4X:
<?php
const DB_HOST = 'localhost';
const DB_NAME = 'wardlog';
const DB_USER = 'root';
const DB_PASS = '';
?>

1.6 Updating the api.php file.

Open the api.php file and update your connection parameters. This is a multi database CRUD REST API for MySQL, PostGres, SQLite, MSSQL. Both the mysqlconfig and api.php connection details should match.

1662713962060.png


1.7 Running the App on your WebServer

I am using Laragon as my development server due to its ease of use. You can use any web server of your choice as its just my personal option. The app is set up to open the WebApp to the default browser as soon as it starts.

This is done in the config.properties file. If you open the file, it has amongst other things.

B4X:
# Server configuration
ServerIP=https://localhost
#characterEncoding=utf8

You can change the ServerIP this to suit your needs. This can also point to your publicly available web server and this IS mostly use by BANano Inline PHP.

In Main.AppStart, I have also set the Publish folder, that is where the final webapp will be built by BANano, to c:\laragon\www. This folder is created by Laragon during installation, you need to ensure that the folder you publish to exists. For xampp, usually, you need to change the publish folder to c://xampp//htdocs

B4X:
#IgnoreWarnings:12, 15
Sub Process_Globals
    Public BANano As BANano 'ignore
    'this is the folder on your development server.
    Public AppName As String = "flexcrudrest"
    Public AppTitle As String = "VFlexDialog + MySQL CRUD REST API"
    Private Publish As String = "C:\laragon\www"
    Public Version As String = "1.00"
    Public ServerIP As String
End Sub

The AppName I have used to define my app is flexcrudrest, this is the additional folder that will be created when building the app. The contents of this folder is what is then published by yourself to a public web server via FTP.

You initialize BANano with the AppName and then publish the app as soon as you click run in b4j. The app is built, then opened in the browser and you can then publish your app via FTP when happy with it. From time to time, your web browser updates might not reflect as expected. You can clear the cache or do a hard refresh with Ctrl + Refresh

B4X:
BANano.Initialize("BANano", AppName, DateTime.now)

and

B4X:
BANano.Build(Publish)

1662734324501.png


The final package to FTP is made up of...

1662734420110.png
 
Last edited:

Mashiane

Expert
Licensed User
Longtime User
Step 2: Creating the User Interface with the VFlexDialog Builder

(The below video introduces you on how to create UI with VFlexBuilder for your app)






2.1 The code from the VFlexBuilder that will be used by your app.

B4X:
Sub BuildFlexCRUDER
    txtid = flexCRUDER.AddTextField (1, 1, "id", "#", "")
    flexCRUDER.SetItemOnDB("id", "Yes", "No")
    flexCRUDER.SetItemDataTypes("id", "Integer", "Integer")
    flexCRUDER.SetItemOnTable("id", "Normal", False, False ,False, "large")
    flexCRUDER.SetItemHidden("id", True)
    '
    txtname = flexCRUDER.AddTextField (1, 1, "name", "Name", "")
    flexCRUDER.SetItemHeadings("name", "", "Name")
    flexCRUDER.SetItemOnDB("name", "Yes", "Yes")
    flexCRUDER.SetItemOnTable("name", "Chip", True, True ,True, "large")
    flexCRUDER.SetItemRequired("name","Name", True)
    flexCRUDER.SetItemAutoFocus("name")
    flexCRUDER.SetItemNameColorImageFields("name", "name", "color", "")
    '
    colcolor = flexCRUDER.AddColor(2, 1, "color", "Color", "")
    flexCRUDER.SetItemOnDB("color", "Yes", "No")
    flexCRUDER.SetItemOnTable("color", "ColorSelect", False, False ,False, "large")
    flexCRUDER.SetItemRequired("color","Color", True)
    flexCRUDER.SetItemKeyValues("color", "id", "text", "color", "avatar")
    '
    flexCRUDER.AddRule("name", "namecheck")
    flexCRUDER.AddRule("color", "colorcheck")
    flexCRUDER.Refresh
End Sub

Sub namecheck(v As String) As Object    'ignoredeadcode
    v = BANanoShared.ValidString(v)
    If v = "" Then
        Return "The name is required!"
    Else
        Return True
    End If
End Sub
Sub colorcheck(v As String) As Object    'ignoredeadcode
    v = BANanoShared.ValidString(v)
    If v = "" Then
        Return "The color is required!"
    Else
        Return True
    End If
End Sub
 
Last edited:

Mashiane

Expert
Licensed User
Longtime User
Step 3: Creating the User Interface with the Abstract Designer

1662711125207.png


3.1 Build the UI

1. Create a new layout using the abstract designer
2. Add a VContainer - Fluid property should be off
3. Add a VRow inside the VContainer
4. Add a VCol inside the VRow, set the Cols property to be 12 - (in all devices the column should span 12 spaces)
5. Add a VueTable inside the VCol, set these properties (Manual=True, Buttons/Chip Elevation=0,All Columns Visible=False)
6. Add a BANanoDataSource inside the VContainer, set the Database Type=php_api, also set the BaseURL to be exact to where your app will access the REST API from, eg. http://localhost/wardlog/api.php
7. Add a VFlexDialog inside the VContainer (for our form entry). See more settings made on its properties in the project file.
8. Add a VMsgBox inside the VContainer (for delete confirmation)

3.2 Generate Members

1662711915456.png
 
Last edited:

Mashiane

Expert
Licensed User
Longtime User
Step 4: Explaining the Process Flow - Part 1

4.1 When you click Run in B4J, the app starts, this calls BANano_Ready. BANano_Ready calls pgIndex.Init. I usually dont add any code to Main except that.

B4X:
Sub BANano_Ready
    pgIndex.Init
End Sub

4.2 pgIndex.Init

pgIndex Init builds up your app by injecting HTML code to the body of your web page. Here we inject the baselayout, this is the navbar, the navigation drawer, the working area and other things. As part of this process, the sub AddPages is called. AddPages creates the pages for your app by calling Initialize on each page.

B4X:
Sub Init                    'ignoreDeadCode
    'initialize the app
    vuetify.Initialize(Me, Main.AppName)
    'load the base layout
    BANano.LoadLayout(vuetify.Here, "baselayout")
    'set the app title
    vuetify.SetData("apptitle", Main.AppTitle)
    'set the listview options
    drwlist.SetOptions(drwoptions)
    '
    'tell instance about the router view
    vuetify.BindRouterView(approuterview)
    'tell instace about the progress loader
    vuetify.BindProgressLoaderOn(appbar)
   
    'add other route component pages
    AddPages
   
    'render the ux
    vuetify.Serve
    'Log(vuetify.HTML)
    'if your project used VField and you will use the Package Manager
    'uncomment this run to get a list of 'real' components used
    'Vuetify.GetUsedComponents
End Sub

Adding each of the pages in the app.

B4X:
'code to add other pages to the app
Sub AddPages            'IgnoreDeadCode
    pgViewHome.Initialize(vuetify)
End Sub

You can also add additional pages to your app by following these steps.

Click Project > Add New Module > Class Module > BVAD3 Page / BVAD3 CRUD Page (this is the template used in this example)

This will just give you the bare minimum for you to design your app.

1662712413267.png



NB: All layouts of your page CAN ONLY be loaded on the INITIALIZE sub.

4.3 When Initialize is called in each page, it builds the HTML structure of the page.

B4X:
Sub Initialize(v As VuetifyApp)
    'establish a reference to the app
    vuetify = v
    'initialize the component
    page.Initialize(Me, name)
    page.vuetify = vuetify
    'set the page title
    page.Title = "CRUDER"
    'uncomment this line if this will be the first page
    page.StartPage
    path = page.path
    '
    'build the page html here
    banano.LoadLayout(page.Here, "cruderlayout")
    frmCRUDER = flexCRUDER.Form
   
    BuildFlexCRUDER
   
    'bind the form to the ds (OPTIONAL)
    frmCRUDER.BindState(page)
    frmCRUDER.BindDataSource(dsCRUDER)
    frmCRUDER.BindDataTable(tblCRUDER)
    '
    dsCRUDER.BaseURL = "https://localhost/wardlog/api.php"
    dsCRUDER.TableName = "categories"
   
    'initialize this on pgIndex.AddPages
    'add the route page to vuetify instance
    vuetify.AddRoute(page)
End Sub

You can set any page as the StartPage of your app. You navigate to the start page with Vuetify.NavigateTo("/"), any other page you navigate to it using its name, e.g. Vuetify.NavigateTo("/assets")

Main.AppStart will then open your app on the default browser with...

B4X:
Dim appPath As String = $"${ServerIP}/${AppName}/index.html"$
    fx.ShowExternalDocument(appPath)

The app will load and show the StartPage you specified.

Internally the VueJS life cycle events kick in, one of these is Mounted. This is when the UI is actually shown. Each time you navigate to a page, if the mounted sub is active (not commented out, it will be fired)

4.4 Mounted Life Cycle Event

B4X:
Sub mounted        'ignoreDeadCode
    vuetify.Loading(True)
    dsCRUDER.SELECT_ALL
End Sub

Here we show the loading spinner and call the BANanoDataSource SELECT_ALL sub, this executes an internal call via the REST API to get all the records on your table.
This is a GET call to this endpoint.

B4X:
http://localhost/wardlog/api.php/records/categories

When SELECT_ALL is finished executing, it calls the dsCRUDER_SelectAll callback. This receives the records from the endpoint and loads them to the data-table.

B4X:
Sub dsCRUDER_SelectAll (Success As Boolean, Response As String, Error As String, affectedRows As Int, Result As List)
    If Success And Response = dsCRUDER.RESULT_SUCCESS Then
        'reload records to the data-table
        tblCRUDER.Reload(Result)
        'turn off table loading indicator
        vuetify.Loading(False)
    Else
        Log(Error)
        vuetify.Loading(False)
        vuetify.ShowSwalError("The operation could not be executed, please try again!")
        Return
    End If
End Sub

One can execute page.CallMounted to call the mounted sub each time to refresh the data in the data-table.
 
Last edited:

Mashiane

Expert
Licensed User
Longtime User
Step 4: Explaining the Process Flow - Part 2

The toolbar on top of the table has buttons to add, clear-sort order, refresh and back.

1662714768856.png


Clicking Add on the vue-table fires the event below.

B4X:
Private Sub tblCRUDER_Add_Click (e As BANanoEvent)
    dsCRUDER.ADD
    'change the title of the dialog
    flexCRUDER.Title = $"Add ${(dsCRUDER.Singular)}"$
    'change the ok label
    flexCRUDER.YesCaption = "Save"
    'change the cancel label
    flexCRUDER.NoCaption = "Cancel"
    'show the dialog
    flexCRUDER.Show
End Sub

and the VFlexDialog is shown.

1662714974569.png


The Add Code prepares the BANanoDataSource for entry, then updates the VFlexDialog for entry also. As soon as the VFlexDialog is visible, another event is fired. This ensures that the form validation is reset.

B4X:
Private Sub flexCRUDER_Visible
    'callback fired when the from is visible event
    Try
        'determine if the dialog is vidible
        Dim bVisible As Boolean = flexCRUDER.IsVisible(page)
        'if the dialog is visible
        If bVisible Then
            'refresh the refs to link to the active page
            page.RefreshActiveRefs(vuetify)
            'reset the form validations
            frmCRUDER.ResetValidation(page)
        End If
    Catch
        'Log(LastException)
    End Try                    'ignore
End Sub

A user can click Save or Click Cancel

When a user clicks Cancel, the No_Click event is fired.

B4X:
Private Sub flexCRUDER_No_click (e As BANanoEvent)
    flexCRUDER.hide
    page.CallMounted
End Sub

This hides the dialog and calls mounted, refreshing the data-table with all records in case another user added a new record (for a multi-user environment)

If the user clicks Yes to add the entry, form validation takes place. We had ealier specified that the name and color are required and we added check rules for them.

B4X:
Private Sub flexCRUDER_Yes_Click (e As BANanoEvent)
    'perform datasource verification
    dsCRUDER.VERIFY
    'peform the form validation
    page.refs = vuetify.GetRefs
    Dim bValid As Boolean = frmCRUDER.Validate(page)
    If bValid = False Then
        'the validation has failed
        vuetify.ShowSwalToastError("Please enter all required information!", 2000)
        Return
    End If
    'show a loading indicator on the ok button
    flexCRUDER.YesLoading = True
    'get the record from form (recordsource) and CREATE / UPDATE record depending on mode
    dsCRUDER.CREATE_OR_UPDATE
End Sub

Form validation will kick in and check the name and color based on the callback defined with AddRules.

1662715277062.png


These were defined as

B4X:
Sub namecheck(v As String) As Object    'ignoredeadcode
    v = BANanoShared.ValidString(v)
    If v = "" Then
        Return "The name is required!"
    Else
        Return True
    End If
End Sub

Sub colorcheck(v As String) As Object    'ignoredeadcode
    v = BANanoShared.ValidString(v)
    If v = "" Then
        Return "The color is required!"
    Else
        Return True
    End If
End Sub

The corresponding error text is not shown under the input controls because when we created our form we said "Hide Details", this hides any hints & error messages for all controls in the form.

This dialog and form is used for both CREATE & UPDATE calls to the db, so because our mode is CREATE, the statement.

B4X:
    dsCRUDER.CREATE_OR_UPDATE

in this case will execute a CREATE callback of the data-source.

B4X:
Private Sub dsCRUDER_Create (Success As Boolean, Response As String, Error As String, affectedRows As Int, Result As List)
    'turn off the ok loading indicator
    flexCRUDER.YesLoading = False
    If Success And Response = dsCRUDER.RESULT_SUCCESS Then
        'hide the dialog
        flexCRUDER.hide
        page.CallMounted
    Else
        Log(Error)
        vuetify.ShowSwalError("The operation could not be executed, please try again!")
        Return
    End If
End Sub

The record has been added, the dialog is hidden and mounted is called to refresh the data-table records.
 

Mashiane

Expert
Licensed User
Longtime User
Step 4: Explaining the Process Flow - Part 3

Each entry in our table is listed with an Edit / Delete columns. Each edit / delete call will fire and it will receive the record on that paticular row of the table.

1662715669970.png


Clicking Edit, fires the edit event on the data-table.

B4X:
Private Sub tblCRUDER_Edit (item As Map)
    vuetify.Loading(True)
    dsCRUDER.EDIT(item)
End Sub

This calls the .EDIT sub in the data-source. This executes a READ on the data-base using REST API for that record and fires a callback.

B4X:
Private Sub dsCRUDER_Read (Success As Boolean, Response As String, Error As String, affectedRows As Int, Result As List)
    vuetify.Loading(False)
    If Success And Response = dsCRUDER.RESULT_SUCCESS Then
        'move to the first record
        dsCRUDER.MOVE_FIRST(Result)
        'change the title of the dialog
        flexCRUDER.Title = $"Edit ${(dsCRUDER.Singular)}"$
        'change the ok label
        flexCRUDER.YesCaption = "Update"
        'change the cancel label
        flexCRUDER.NoCaption = "Cancel"
        'show the dialog
        flexCRUDER.Show
    Else
        Log(Error)
        vuetify.ShowSwalError("The operation could not be executed, please try again!")
        Return
    End If
End Sub

The record is found and then the edit/upate dialog is shown.

1662716014815.png


The title of the dialog is now Edit, and the save button is Update.

When the user clicks Update, the Yes_click sub for the VFlexDialog is called.

B4X:
Private Sub flexCRUDER_Yes_Click (e As BANanoEvent)
    'perform datasource verification
    dsCRUDER.VERIFY
    'peform the form validation
    page.refs = vuetify.GetRefs
    Dim bValid As Boolean = frmCRUDER.Validate(page)
    If bValid = False Then
        'the validation has failed
        vuetify.ShowSwalToastError("Please enter all required information!", 2000)
        Return
    End If
    'show a loading indicator on the ok button
    flexCRUDER.YesLoading = True
    'get the record from form (recordsource) and CREATE / UPDATE record depending on mode
    dsCRUDER.CREATE_OR_UPDATE
End Sub

This continues to call CREATE_OR_UPDATE of the data-source, but because the mode is now UPDATE and no longer ADD, the data-source UPDATE callback is fired.

B4X:
Private Sub dsCRUDER_Update (Success As Boolean, Response As String, Error As String, affectedRows As Int, Result As List)
    'turn off the loading indicator
    flexCRUDER.YesLoading = False
    If Success And Response = dsCRUDER.RESULT_SUCCESS Then
        'hide the dialog
        flexCRUDER.hide
        'run the method to load records
        page.CallMounted
    Else
        Log(Error)
        vuetify.ShowSwalError("The operation could not be executed, please try again!")
        Return
    End If
End Sub

This hides the vflexdialog and calls mounted to refresh the data-table contents.
 

Mashiane

Expert
Licensed User
Longtime User
Step 4: Explaining the Process Flow - Part 4

Now a user decides to delete a record by clicking on the delete column for that item.

1662716326605.png



The confirm dialog we added in the abstract designer is fired.

1662716379069.png


This is raised by this event

B4X:
Private Sub tblCRUDER_Delete (item As Map)
    dsCRUDER.DELETE_CONFIRM(item)
    Dim sMsg As String = $"<h2>${dsCRUDER.DisplayValue}</h2><br>Are you sure that you want to delete this ${dsCRUDER.Singular.ToLowerCase}?"$
    confirmCRUDER.Confirm(page, "delete", "Confirm Delete", sMsg, "Yes", "No")
End Sub

Which when clicked, the row being clicked is passed to tblCRUDER_Delete, which then fires the dialog.

A user can click Yes / No to cancel the delete.

Clicking No, fires the Cancel event for the confirm dialog.

B4X:
Private Sub confirmCRUDER_Cancel_click (e As BANanoEvent)
    'hide the dialog
    confirmCRUDER.hide
End Sub

This hides the confirm dialog.

Clicking Yes however starts a new sequence of events.

This calls the data-source delete sub...

B4X:
Private Sub confirmCRUDER_Ok_Click (e As BANanoEvent)
    'show a loading indicator on ok button
    confirmCRUDER.OkLoading = True
    'execute the delete call
    dsCRUDER.DELETE
End Sub[code]

Which executes the final callback

[code]
Private Sub dsCRUDER_Delete (Success As Boolean, Response As String, Error As String, affectedRows As Int, Result As List)
    confirmCRUDER.OkLoading = False
    If Success And Response = dsCRUDER.RESULT_SUCCESS Then
        'hide the dialog
        confirmCRUDER.hide
        'load the method to load all records
        page.CallMounted
    Else
        Log(Error)
        vuetify.ShowSwalError("The operation could not be executed, please try again!")
        Return
    End If
End Sub

Which then hides the confirm dialog on success and calls mounted to refresh the data-table records.
 

Mashiane

Expert
Licensed User
Longtime User
Step 4: Explaining the Process Flow - Part 5

If a field was marked as "Filterable", the end user can execute a text search from the search box. For example, I have typed the letter E, so all the categories that meet that criteria will be listed.

1662716774579.png


If a field is marked as "Sortable", on is able to sort them in the data-table. This is done by clicking the column header in the data-table.

1662716873653.png


To clear the sort order, one can click the
1662716921967.png
button on the toolbar. This calls

B4X:
Private Sub tblCRUDER_ClearSort_Click (e As BANanoEvent)
    tblCRUDER.ClearSort
End Sub
To reload the records in the data-table, one can click
1662716960818.png


This calls

B4X:
Private Sub tblCRUDER_Refresh_Click (e As BANanoEvent)
    page.CallMounted
End Sub
 

Mashiane

Expert
Licensed User
Longtime User
The CRUD process abstracted by the BANanoDataSource

The BANanoDataSource is an abstract layer for most DB connectivity operations. If you explore its source code you will also find PHP specific code for SQLite, MSAccess, MongoDB, MsAccess, MSSQL to mention a few. The code is split between PDO and MySQLi.

Exploring the source code provided.... the CREATE process runs this code via axios

POST - creates a record. This recieves a key value pair, keys being the fields of the record to be updated.

B4X:
Dim axios As BANanoAxios
    axios.Initialize(sBaseURL)
    Dim res As Map
    Select Case sAction
    Case ACTION_CREATE
        'this is a post
        axios.SetURL($"/records/${sTableName}"$)
        If UsesApp = False Then
            Record = ParentComponent.GetData(sRecordSource)
        Else
            Record = AppComponent.GetData(sRecordSource)
        End If
        CorrectDataTypesSave(Record)
        Record.Remove(sAutoIncrement)
           axios.SetData(Record)
        Dim res1 As String = axios.PostWait
        res1 = BANanoShared.CStr(res1)
        If res1 = "200" Then
            OK = True
            Response = RESULT_SUCCESS
            affectedRows = 1
            RowCount = 1
        End If
        BANano.ReturnThen(True)
 

Mashiane

Expert
Licensed User
Longtime User
GET - reading

For reading a single record we build our URL using the primary key value of the record in question.

B4X:
Case ACTION_READ
        'get the key for the record
        If UsesApp = False Then
            Dim pkValue As String = ParentComponent.GetData(dsKey)
        Else
            Dim pkValue As String = AppComponent.GetData(dsKey)
        End If
        If pkValue = "" Then
            Error = "Primary key value is blank!"
            BANano.ReturnThen(True)
        End If
        'this is a get
        axios.SetURL($"/records/${sTableName}/${pkValue}"$)
        Record = axios.GetWait
        CorrectDataTypesSave(Record)
        Result.Add(Record)
        If UsesApp = False Then
            ParentComponent.SetData(sRecordSource, Record)
        Else
            AppComponent.SetData(sRecordSource, Record)
        End If
        OK = True
        Response = RESULT_SUCCESS
        affectedRows = Result.size
        RowCount = Result.size
        BANano.ReturnThen(True)
 

Mashiane

Expert
Licensed User
Longtime User
PUT - updating records

B4X:
Case ACTION_UPDATE
        'update an existing record by primary key
        If UsesApp = False Then
            Dim pkValue As String = ParentComponent.GetData(dsKey)
        Else
            Dim pkValue As String = AppComponent.GetData(dsKey)
        End If
        If pkValue = "" Then
            BANano.ReturnThen(True)
        End If
        'this is a post
        axios.SetURL($"/records/${sTableName}/${pkValue}"$)
        'read the record to update
        If UsesApp = False Then
            Record = ParentComponent.GetData(sRecordSource)
        Else
            Record = AppComponent.GetData(sRecordSource)
        End If
        CorrectDataTypesSave(Record)
        Record.Remove(sAutoIncrement)
        axios.SetData(Record)
        Dim res1 As String = axios.PutWait
        res1 = BANanoShared.CStr(res1)
        If res1 = "200" Then
            OK = True
            Response = RESULT_SUCCESS
            affectedRows = 1
            RowCount = 1
        End If
        BANano.ReturnThen(True)

This receives a map of the record and through a put using the primary key, it updates the record.
 

Mashiane

Expert
Licensed User
Longtime User
DELETE - removing a record.

B4X:
Case ACTION_DELETE
        'update an existing record by primary key
        If UsesApp = False Then
            Dim pkValue As String = ParentComponent.GetData(dsKey)
        Else
            Dim pkValue As String = AppComponent.GetData(dsKey)
        End If
        If pkValue = "" Then
            BANano.ReturnThen(True)
        End If
        axios.SetURL($"/records/${sTableName}/${pkValue}"$)
        Dim res1 As String = axios.DeleteWait
        res1 = BANanoShared.CStr(res1)
        If res1 = "200" Then
            OK = True
            Response = RESULT_SUCCESS
            affectedRows = 1
            RowCount = 1
        End If
        BANano.ReturnThen(True)

This will delete the record from the DB using the primary key.
 
Top