B4J Question Cannot get Data from a form which was working

Peter Lewis

Well-Known Member
Licensed User
Longtime User
Hi,
I have been looking for a few hours why this worm will not save data.
I checked on the HTML side and it looks perfect. How I did this was to past the form to https://httpbin.org/post
The result is
{
"args": {},
"data": "",
"files": {},
"form": {
"addressInput": "95 Ridgeside Road, Durban North",
"contactInput": "Peter Lewis",
"descriptionTextarea": "description test",
"emailInput": "peter@praxos-software.co.za",
"mallnameInput": "Gateway",
"municipalitySelect": "Ethekwini",
"ownerSelect": "9f8ff690-5034-46a0-bc0c-c4a3b299c6ca",
"phoneInput": "0724533919",
"postalcodeInput": "4051",
"postersInput": "20",
"provinceSelect": "KWAZULU-NATAL",
"retailshopsInput": "100",
"suburbSelect": "Durban NU",
"websiteInput": "https://www.home.co.za"
},
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"Accept-Encoding": "gzip, deflate, br, zstd",
"Accept-Language": "en-US,en;q=0.9,en-ZA;q=0.8",
"Cache-Control": "max-age=0",
"Content-Length": "423",
"Content-Type": "application/x-www-form-urlencoded",
"Dnt": "1",
"Host": "httpbin.org",
"Origin": "http://127.0.0.1:5000",
"Priority": "u=0, i",
"Referer": "http://127.0.0.1:5000/",
"Sec-Ch-Ua": "\"Not:A-Brand\";v=\"99\", \"Google Chrome\";v=\"145\", \"Chromium\";v=\"145\"",
"Sec-Ch-Ua-Mobile": "?0",
"Sec-Ch-Ua-Platform": "\"Windows\"",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "cross-site",
"Sec-Fetch-User": "?1",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36",
"X-Amzn-Trace-Id": "Root=1-69b6dd10-5a79734210704ebc37600f25"
},
"json": null,
"origin": "169.1.***.***",
"url": "https://httpbin.org/post"
}

When I post to the server, I can see the length is different depending on what I type

clsMallInfoAdd
length 396
Type application/x-www-form-urlencoded
Full Request URL http://127.0.0.1:5000/malladd
Input Stream (HttpInput) HttpInput@148891937 cs=HttpChannelState@503fbbc6{s=HANDLING rs=BLOCKING os=OPEN is=IDLE awp=false se=false i=true al=0} cp=org.eclipse.jetty.server.BlockingContentProducer@55f45b92 eof=false
Methods POST
Request URI /malladd
Secure ? false
(MyMap) {}
Methods POST

But the map is not filled. I used this code to read it before and populate the DB

B4X:
    Dim query As String = utils.prepareInsertQuery($"
    INSERT INTO [ledposter].[dbo].[malls](
     [mallID]
    ,[mallName]
    ,[province]
    ,[city]
    ,[postalcode]
    ,[phone]
    ,[email]
    ,[ownerID]
    ,[suburb]
    ,[description]
    ,[retailshops]
    ,[posters]
    ,[status]
      ,[mtraffic]
 
            )"$)
    
    Dim guidST As JavaObject
    guidST.InitializeStatic("java.util.UUID")
    Dim guid As JavaObject = guidST.RunMethod("randomUUID",Null)
    Dim guidStr As String = guid.RunMethod("toString",Null)
    
        Dim params(query.Length - query.Replace("?","").Length) As String
    params(0) = guidStr
    params(1) = req.GetParameter("mallnameInput")
    params(2) = req.GetParameter("provinceSelect")
    params(3) = req.GetParameter("municipalitySelect")
    params(4) = req.GetParameter("postalcodeInput")
    params(5) = req.GetParameter("phoneInput")
    params(6) = req.GetParameter("emailInput")
    params(7) = req.GetParameter("ownerSelect")
    params(8) = req.GetParameter("suburbSelect")
    params(9) = req.GetParameter("descriptionTextarea")
    params(10) = req.GetParameter("retailshopsInput")
    params(11) =req.GetParameter("postersInput")
    params(12) = "unknown"
    params(13) ="0"
 
    sql.execnonquery2(query,params)

It seems like the data is coming through but I cannot find the method of extracting it

<form method="POST" action="/malladd">
</form>

Maybe it is a Sunday problem.

I am using HTMX to swap out the Select Areas and also to SWAP out the Searchable Fields.. I tried Choices.js but found it was not working for me as I had more than one field to use the plugin and it would not support that. So I eventually used datatables



B4X:
                                    <div class="col-lg-4">
                                                                            <label for="provinceSelect">Province</label>
                                                                            <br>
                                            <input list="provinceSelectlist" name = "provinceSelect" autocomplete="nope" hx-get="/muni"  hx-target="#municipalitySelectlist" placeholder="Search Province">               
                                                                            <datalist id="provinceSelectlist">
                                                                                  <#list  provincetable as provincetables>
                                                                                        <option value="${provincetables.province}" >
                                                                                  </#list>
                                                                            </datalist>
                                                                                    
                                                                            
                                                                        </div>
                                                                                            
                                                        <!-- State -->
                                                                        <div class="col-lg-4">
                                                                        <label for="municipalitySelect">Municipality</label>
                                                                        <br>
                                                                        <input list="municipalitySelectlist" name = "municipalitySelect"  autocomplete="nope" hx-get="/suburb"  hx-target="#suburbSelectlist" placeholder="Search Municipality">
                                                                                
                                                                            <datalist id="municipalitySelectlist">
                                                                                    <#list  municipalitytable as municipalitytables>
                                                                                        <option value="${municipalitytables.city}">
                                                                                    </#list>
                                                                            </datalist>
                                                                                
                                                                            
                                                                        </div>
                                                                        
                                                                        <div class="col-lg-4">
                                                                            <label for="suburbSelect">Suburb</label>
                                                                            <br>
                                                                            <input list="suburbSelectlist" name = "suburbSelect" autocomplete="nope" placeholder="Search Suburb">
                                                                                <datalist id="suburbSelectlist">
                                                                                            <#list  suburbtable as suburbtables>
                                                                                                <option value="${suburbtables.suburb}" >
                                                                                            </#list>
                                                                                </datalist>
                                                                        </div>



Thank you
 
Last edited:
Solution
Thank you to @aeric who spent time with me trying to find the solution
I have narrowed it down to the culprit which I still do not understand why but here it goes.

We added this line at the begin of the handle

B4X:
    Dim params As Map = Req.ParameterMap

It solved the problem which is crazy because we are not using the params variable

1773677000541.png
1773677023903.png

If I comment out this line then it saving fields of the form to the database does not work

I really hope there is an explanation because I like to know why, it improves my thought process for future problems.

aeric

Expert
Licensed User
Longtime User
Can you explain whether you are using B4J for the client app or server?

I have a server template using HTMX for web front end. It is also a Web API server.

If you want to use B4J client, I also have a client template support B4X but making request through Web API (not HTMX) because HTMX is for web.
 
Upvote 0

aeric

Expert
Licensed User
Longtime User
It is important to specify what framework you are using.
Are you using a template engine such as FreeMarker?
FreeMarker is more of a backend or server rendering library while mixing with frontend library like HTMX may cause conflict.

B4X:
<datalist id="provinceSelectlist">
      <#list provincetable as provincetables>
            <option value="${provincetables.province}">
      </#list>
</datalist>

Let say FreeMarker is swapping the elements for code on line #2 and #4 and HTMX is swapping the outer element using the id on code line #1 and #5.
 
Upvote 0

Peter Lewis

Well-Known Member
Licensed User
Longtime User
Hi, Yes I am using freemaker, sorry forgot to mention it.

I have HTMX only swapping the inner, Below is the code of the swap which is been filled with freemaker correctly

B4X:
                                                      <#list  provincetable as provincetables>
                                                            <option value="${provincetables.province}">
                                                      </#list>

I changed something along the way and it stopped saving. What I am confused about is the server received data length 333 but I cannot access that data

and if you look at m first post and first INFO is data from a website that sees all the data posted. Which everything is there, I am just having an issue for the b4j server to extract it
 
Last edited:
Upvote 0

aeric

Expert
Licensed User
Longtime User
B4X:
<form method="POST" action="/malladd">
</form>
How do you submit the form?
Do you have a button? Is the button having hx-post or normal submit?
 
Upvote 0

Peter Lewis

Well-Known Member
Licensed User
Longtime User
I don't understand the code. How is the SQL related to the http request?
Hi,
I have thew b4j server running freemaker with MS SQL, Then on the webpage side I have HTMX just swapping out the code on the webpage that has changed so we do not reload the whole page everytime.

In this case I am loading up details about a shipping mall


HTML just swaps out the code for a Select which is the Owner and populates the Municipality and Suburb dependent on the previous selection. All the information for these are coming from a single SQL table.

Then I am trying to save it into a different sql table.

When I was using Select dropdowns , everything was working perfectly, I have a video of that. But because the drop downs were large one had 765 records, I needed a search which I tried multiple options and the best result was 'datatables' which I have running , Choices.js worked only on one Select, multiple would not work, I went into the .js and .css to see if I could make more that one work but I could not find the references do duplicate with different reference names and also the popup list from the select was transparent and I could not find a way thru css to solve that . The datatables does not look like the rest of the page. But i was going to make everything look nice once the code was working properly.

The program is for my use as I am looking to add LED Posters to malls around South Africa and sell advertising space on them with full reporting. I have already written the Player code which controls the screen at the correct resolution and runs on a Raspberry Pi with transitions sending back details of displayed time thru MQTT.

The system will also take the clients media and reformat to the correct size before it is avail for use. Mall owners also have to approve each advert.

One area which these are targeted for is SMALL business who cannot afford mainstream adverts. They also might be in only certain areas, So area targeting is important. I would also like to add the facility for AI to generate the adverts at a small cost , either static of video based on a wizard for information to provide the initial AI request which then can be edited.
.
Hopefully this give you an idea where I am going with this. Thank you



 
Upvote 0

Peter Lewis

Well-Known Member
Licensed User
Longtime User
B4X:
<form method="POST" action="/malladd">
</form>
How do you submit the form?
Do you have a button? Is the button having hx-post or normal submit?
Yes, I do

B4X:
        <button type="submit"  class="btn btn-success">Add Shopping Mall</button>

I also tried this code in the Button

B4X:
<button type="submit" formaction="/malladd" class="btn btn-success">
 
Upvote 0

aeric

Expert
Licensed User
Longtime User
1. The SQL seems wrong. You are missing VALUES (?, ?, ? , ...)
B4X:
Dim query As String = utils.prepareInsertQuery($"
    INSERT INTO [ledposter].[dbo].[malls](
     [mallID]
    ,[mallName]
    ,[province]
    ,[city]
    ,[postalcode]
    ,[phone]
    ,[email]
    ,[ownerID]
    ,[suburb]
    ,[description]
    ,[retailshops]
    ,[posters]
    ,[status]
      ,[mtraffic]
 
            )"$)

2. If you try to add Log(req.GetParameter("provinceSelect"))
Do you get any value?
 
Upvote 0

aeric

Expert
Licensed User
Longtime User
Dim params(query.Length - query.Replace("?","").Length) As String
I am not sure why are you replacing the placeholder.

If you confirm there are 14 items, then just put the exact size.
B4X:
Dim params(14) As String
 
Upvote 0

Peter Lewis

Well-Known Member
Licensed User
Longtime User
I am not sure why are you replcaing the placeholder.
Have you never worked before with datatable (dt) ? This code works for saving to file , I have used it for over 3 years.

The return from the Webpage shows 333 char size but I cannot get the data , this is the only problem


As can be seen above I put in a LOG to vide the data but it is not showing any information


So even before I get to the SQL part, the data is not extracting from the Page. It is arriving ie 333 bytes
 
Upvote 0

Peter Lewis

Well-Known Member
Licensed User
Longtime User
1. The SQL seems wrong. You are missing VALUES (?, ?, ? , ...)
B4X:
Dim query As String = utils.prepareInsertQuery($"
    INSERT INTO [ledposter].[dbo].[malls](
     [mallID]
    ,[mallName]
    ,[province]
    ,[city]
    ,[postalcode]
    ,[phone]
    ,[email]
    ,[ownerID]
    ,[suburb]
    ,[description]
    ,[retailshops]
    ,[posters]
    ,[status]
      ,[mtraffic]
 
            )"$)

2. If you try to add Log(req.GetParameter("provinceSelect"))
Do you get any value?
No Value at all, I even tried at the top of the handler, which was working before , even before it got to the SQL
 
Upvote 0

aeric

Expert
Licensed User
Longtime User
Have you never worked before with datatable (dt) ? This code works for saving to file , I have used it for over 3 years.
1. I am not sure you mean datatable (jQuery table), datalist element or database table.
If datalist with FreeMarker, Yes, I never work with it.

What I understand is FreeMarker renders the web page on full page refresh. HTMX makes AJAX request which I think will overwrite the generated content.
If I am going to prove I am correct, I will first try to hardcode a "pre-selected" value and submit it as it is. If there is a value sent to the server means something (probably HTMX) has altered the child elements of the list.

B4X:
Dim params(query.Length - query.Replace("?","").Length) As String
The above code is on B4X side, nothing to do with datatable or datalist which are web elements.

2. Can you add a Log to see what is the final SQL query?
B4X:
Log(query)
sql.execnonquery2(query,params)
 
Upvote 0

Peter Lewis

Well-Known Member
Licensed User
Longtime User
1. I am not sure you mean datatable (jQuery table), datalist element or database table.
If datalist with FreeMarker, Yes, I never work with it.

What I understand is FreeMarker renders the web page on full page refresh. HTMX makes AJAX request which I think will overwrite the generated content.
If I am going to prove I am correct, I will first try to hardcode a "pre-selected" value and submit it as it is. If there is a value sent to the server means something (probably HTMX) has altered the child elements of the list.

B4X:
Dim params(query.Length - query.Replace("?","").Length) As String
The above code is on B4X side, nothing to do with datatable or datalist which are web elements.

2. Can you add a Log to see what is the final SQL query?
B4X:
Log(query)
sql.execnonquery2(query,params)
 
Upvote 0

Peter Lewis

Well-Known Member
Licensed User
Longtime User
1. I am not sure you mean datatable (jQuery table), datalist element or database table.
If datalist with FreeMarker, Yes, I never work with it.

What I understand is FreeMarker renders the web page on full page refresh. HTMX makes AJAX request which I think will overwrite the generated content.
If I am going to prove I am correct, I will first try to hardcode a "pre-selected" value and submit it as it is. If there is a value sent to the server means something (probably HTMX) has altered the child elements of the list.

B4X:
Dim params(query.Length - query.Replace("?","").Length) As String
The above code is on B4X side, nothing to do with datatable or datalist which are web elements.

2. Can you add a Log to see what is the final SQL query?
B4X:
Log(query)
sql.execnonquery2(query,params)
datatable is a class that I got from @EnriqueGonzalez a while ago. It really speeds up dev with sql .

 
Upvote 0

aeric

Expert
Licensed User
Longtime User
What do you get if you open from the browser (GET request)?
B4X:
http://127.0.0.1:5000/muni
 
Upvote 0

Peter Lewis

Well-Known Member
Licensed User
Longtime User
What do you get if you open from the browser (GET request)?
B4X:
http://127.0.0.1:5000/muni
the code at the muni handler just returns the municipalities swap to the page , as there is NO incoming Province it gices a NULL error
Methods GET
Request URI /malladd
Secure ? false
(MyMap) {}
Mall Name
Methods GET
GET
Muni Page
length -1
Type
Full Request URL http://127.0.0.1:5000/muni
Input Stream (HttpInput) HttpInput@1121072891 cs=HttpChannelState@1ce93c18{s=HANDLING rs=BLOCKING os=OPEN is=IDLE awp=false se=false i=true al=0} cp=org.eclipse.jetty.server.BlockingContentProducer@19f21b6b eof=false
Method GET
Request URI /muni
Secure ? false
(MyMap) {}
Error occurred on line: 30 (clsmunicipality)
java.lang.NullPointerException
at b4j.example.clsmunicipality._handle(clsmunicipality.java:99)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:632)
at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:237)
at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:167)
at jdk.internal.reflect.GeneratedMethodAccessor6.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:117)
at anywheresoftware.b4a.shell.ShellBA.raiseEvent2(ShellBA.java:100)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:104)
at anywheresoftware.b4j.object.JServlet$Handle.run(JServlet.java:146)
at anywheresoftware.b4a.keywords.SimpleMessageLoop.runMessageLoop(SimpleMessageLoop.java:47)
at anywheresoftware.b4a.StandardBA.startMessageLoop(StandardBA.java:43)
at anywheresoftware.b4a.shell.ShellBA.startMessageLoop(ShellBA.java:121)
at anywheresoftware.b4a.keywords.Common.StartMessageLoop(Common.java:183)
at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:309)
at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:167)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 
Upvote 0

Peter Lewis

Well-Known Member
Licensed User
Longtime User
As I have mentioned before , the Web Page works, the interaction up to saving the data works.
I tested the webpage and all the other interactions and they work, the output of the form is correct when sent to another site for display. IT IS PERFECT

The only issue is getting the 333 bytes of data into actual fields. Or just looking at the RAW data , it is not in the MAP (it is empty) so where is the 333 bytes of data ? Which changes value depending on what I type into the fields. So it is not just field names

This is what comes out of the webpage when I click on the POST button

{
"args": {},
"data": "",
"files": {},
"form": {
"addressInput": "95 Ridgeside Road, Durban North",
"contactInput": "Peter Lewis",
"descriptionTextarea": "description test",
"emailInput": "peter@praxos-software.co.za",
"mallnameInput": "Gateway",
"municipalitySelect": "Ethekwini",
"ownerSelect": "9f8ff690-5034-46a0-bc0c-c4a3b299c6ca",
"phoneInput": "0724533919",
"postalcodeInput": "4051",
"postersInput": "20",
"provinceSelect": "KWAZULU-NATAL",
"retailshopsInput": "100",
"suburbSelect": "Durban NU",
"websiteInput": "https://www.home.co.za"
},
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"Accept-Encoding": "gzip, deflate, br, zstd",
"Accept-Language": "en-US,en;q=0.9,en-ZA;q=0.8",
"Cache-Control": "max-age=0",
"Content-Length": "423",
"Content-Type": "application/x-www-form-urlencoded",
"Dnt": "1",
"Host": "httpbin.org",
"Origin": "http://127.0.0.1:5000",
"Priority": "u=0, i",
"Referer": "http://127.0.0.1:5000/",
"Sec-Ch-Ua": "\"Not:A-Brand\";v=\"99\", \"Google Chrome\";v=\"145\", \"Chromium\";v=\"145\"",
"Sec-Ch-Ua-Mobile": "?0",
"Sec-Ch-Ua-Platform": "\"Windows\"",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "cross-site",
"Sec-Fetch-User": "?1",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36",
"X-Amzn-Trace-Id": "Root=1-69b6dd10-5a79734210704ebc37600f25"
},
"json": null,
"origin": "169.1.***.***",
"url": "https://httpbin.org/post"
}

which is 100% CORRECT, So why does b4j server not recognise it ? it shows it has the 333 bytes of data, THIS IS THE PROBLEM
 
Upvote 0

aeric

Expert
Licensed User
Longtime User
the code at the muni handler just returns the municipalities swap to the page
1. If we try to call /muni using hx-get, it should make a GET request and the server should return a html text response such as:
B4X:
<input list="provinceSelectlist" name="provinceSelect" autocomplete="nope"
      hx-get="/muni"
      hx-target="#municipalitySelectlist" placeholder="Search Province">
<datalist id="provinceSelectlist">
      <option value="province 1">
      <option value="province 2">
      <option value="province 3">
</datalist>
But you said the web page works then you can ignore this part.

2. What is the code at line 30?
B4X:
Error occurred on line: 30 (clsmunicipality)

3. Make a test
I will first try to hardcode a "pre-selected" value and submit it as it is.
B4X:
<form method="POST" action="/malladd">
      <input type="hidden" name="province" id="provinceInput" value="TEST">
      <button type="submit" class="btn btn-primary">Submit</button>
</form>

B4X:
Sub Handle (req As ServletRequest, resp As ServletResponse)
    Request = req
    Response = resp
    Method = req.Method
    Log($"${Request.Method}: ${Request.RequestURI}"$)
    If Method.EqualsIgnoreCase("POST") Then
        Dim province As String = Request.GetParameter("province")
        Log($"province=[${province}]"$)

4. Can you temporary commented the redirect line?
B4X:
resp.SendRedirect("/malladd")
It seems this is a GET request and share the same URI with POST.
 
Last edited:
Upvote 0
Top