Problems / understanding - buttons and callbacks

Hi!

This is my first post here, so a super brief intro… I’m a web developer in the UK, and am trying to build my first app using Corona - I like the language (LUA), I like that it can be compiled for ios and droid… and look and feel like a native app - correct me if i’m wrong? :slight_smile:

Anyway, today is my first solid day working with Corona SDK, and I keep getting stumped by two issues

  1. onPress for a button widget… can I not pass parameters?

e.g. 

local somebutton = widget.newButton {

        label=“test”,

        top = 220,

        left = 50,

        onPress = testfunc(“some test string”)

    };

function testfunc(var) 

      print(var)

end

  1. Handling network.request Callbacks elegantly

I have a function, lets call it “handler”. I pass it some parameters - lets call them p1,p2, p3, and p4 which tell it which methods from a REST API I have built and specific details like an id. p3 is an object (‘table’) in corona with some values - other parameters for the API request.

Once it has built up a URL for the service based on the parameters, my ‘handler’ function calls network.request (myurl,“GET”,callbackhander) 

where callbackhandler is another function and myurl is the url for my REST action.

Problem: I have lots of callback functions for doing different things. The callback function I need to run depends on p1 and p2.
 

I was expecting to get the response inside “handler” and pass it to the right callback function based on logic around p1 and p2. 

It seems I can only send the resultant response of my request to a single callback function? 
and i loose scope for the original params in “handler” - how i handle the response and process it and what I do with it depends on those params!

Short of creating 2 functions for each REST function - a caller (network.request…) and a specific callback, I cant see what I can do here that would be neat!

Can anyone help??

There are about 20 functions that the REST API provides that I need to make calls to in my app.

Thanks for your help / any help given!! Really appreciated.

 

Hi there,

First of all, welcome to Corona and to the forums.  :-)

On your first question, this is a common source of confusion.  The solution is to use what’s called a Lua closure, which means wrapping your function call with arguments in an anonymous function without arguments, like this:

[lua]

local function testfunc(var) 

      print(var)

end

 

local somebutton = widget.newButton {

        label=“test”,

        top = 220,

        left = 50,

        onPress = function() testfunc(“some test string”) end

    }

[/lua]

I’ve also put testfunc above the button declaration so that it’s available to be referred to when creating the button, and I declared it as local, which is always a good practice.

On your second question, the callback for network.request(), or any of the network APIs, won’t have the original query string included, so your callback won’t know what p1 and p2 were.  If you want to have different behavior for network.request() depending on p1 and p2, you have a few options.

  1. You could echo back that information as part of the response (which would be possible if you’re requesting something from a script on a server you control)
  2. You could provide a different callback to network.request() depending on what p1 and p2 are
  3. You could use the same callback every time, but when you make the network.request(), store the handle and the fact that the request involved p1 and p2 (you could use a table with the handle as the key and the values of p1 and p2 as items).  Part of the information your callback will receives is a requestId (http://docs.coronalabs.com/api/event/networkRequest/requestId.html).  You can then lookup in your table the values of p1 and p2 associated with the original call, and act accordingly

Hope this helps.

  • Andrew

Hi Chris and welcome.

Your questions are good questions.  Let me address them.

0.  Yes you will be building native apps using Lua instead of Java or Objective C.

1.  Andrew’s solution will work, but since you come from a web background, Lua Closures, at least how they are being used here is basically the same concept as an Anonymous function in JavaScript.  As to why yours was not working.  Whenever there is a parameter that expects some kind of function, it’s looking for the “Address of/pointer to the Function”. 

When the trigger (in this case a button press happens), it then calls the function at the passed address.  It knows nothing about your parameters and will make up ones of its own.  Lets look at an example:

local function handleButton(event)       return "Fred Flintstone" end   ... widget.newButton({       onPress = handleButton(someParam) })

When you include parans and parameters like this, Lua will execute your function **FIRST** and immediatly then pass any returned value to the caller.  In other words, the result of this would be this:

  Of course this won't work, because there is no function at memory address "Fred Flintstone" to rexec

takes the memory address of the handleButton function and sticks that value into onPress.  Then when the code for widget.newButton gets a press, it calls the function handleButton then and passes it’s own parameters in.  In this case it’s the table named “event”.

Thanks for the replies here! 
Button press parameter handling, now not an issue - so that’s great!

However, I am still battling with a neat way to handle my networkRequests…

Here’s what I’m ideally trying to achieve:

I have several different scenes.
On a particular scene, lets call it “Sender”, before the scene is rendered I want to fire a request to my php scripts to get some data. 

I have a function that now looks a bit like this code below.

The issue I’m having now, is I have *no idea* how to get “data” back to a function in my scene that will use it to build some interface elements (data is a list of entities, each with various properties)

The skeleton code below is working fine (note: i’ve re-typed it here so if it doesn’t compile, my bad! It works fine in my app)
and is located in a module “myfunctions.lua” that I import so  that it’s globally available to all scenes (10 or more scenes in my app).

I’m guessing that in my scene, after calling requester(), I need to listen somehow for a response from handlerNumber1 so that I can retrieve ‘data’ and use it in my scene.

I’m trying to keep the code nice and modular, and wherever possible, minimal - but perhaps I am approaching it all wrong?

Really appreciate any help, it’s driving me nuts - I just cant ‘see’ the pattern I need to apply here.
 

function requester({params},another param,handler) --do some stuff with the params, --build a url local requesturl = "http://someurl.com/somestuff/?someotherstuff=something" if (handler == "handlerNumber1") then network.request( requesturl,"GET",handlerNumber1) elseif (handler == "handlerNumber2") then network.request( requesturl,"GET",handlerNumber2) else network.request( requesturl,"GET",saySomething("handler " .. handler .. " not implemented")) end end function handlerNumber1(event) print( "handlerNumber1 Called...") if ( event.isError ) then print( "Network error!") return false else local data = json.decode(event.response) return data end end function handlerNumber2(event) print( "handlerNumber2 Called...") if ( event.isError ) then print( "Network error!") return false else local data = json.decode(event.response) return data end end function saySomething(something) print(something) end

 

If I understand you right, I think what you’re trying to do is something like this:

In myfunctions.lua:

[lua]

local json = require(“json”)

local function requester(params, handler) 

   – Using params, build a requesturl…

   local requesturl = “http://someurl.com/somestuff/?someotherstuff=something

   – Local function to handle the common response to all network requests

   local function requestCallback(event)

      if event.isError then

         print(“Network error!”)

         if handler then handler(false) else print(“Handler not provided”) end            – If it exists, call the handler with ‘false’, indicating an error

      else

         local data = json.decode(event.response)

         if handler then handler(data) else print(“Handler not provided”) end            – If it exists, call the handler with the json decoded data

      end

   end

   – Launch the network request

   network.request( requesturl,“GET”, requestCallback)

end

[/lua]

In each of your scenes, you can do this:

[lua]

local function handler(data)

   – Do things in your scene based on the data

end

– Launch the request to get the data we need with some parameters

myfunctions.requester({p1=1,p2=2,p3=3}, handler)

[/lua]

  • Andrew

Thanks Andrew! This seemed to work OK, and makes sense!
I was thinking about it a bit backwards in some ways… your example was fantastic!

Having solved this, I’ve thought of another web-world --> corona / apps scenario that’s going to prove fun, to do with referencing objects on the display that are related (eg a list of text fields, each with a button that increments their value by “1” when pressed. In jQuery et al, I can use parent… closest… and classes / IDs to target things… without the DOM, I have no clue. 

Anyway, Ill put a separate topic up about that I think!

Thanks again!

Chris

 

Ah… Hopefully last question on this topic!!
Gaving got the result from my HTTP request, Im still a bit stuck! 

The data that I received from my request is to be used to create a dynamic list - so in my scene I have code like this:

Got to say, I’m really impressed with the support people are giving me - hopefully I can stop bugging you all with this soon.
This scenario is pretty key to what i’m building, so lots of rinse-and-repeat once it’s working!
 

function scene:createScene( event ) local group = self.view requester({},handler) --note: requester and handler are in "myFunctions.lua" and working great! --how do I get the data that handler has received into my list below? --i'm guessing I need to do something like data=function() requester({},handler) end local mydata = {} for i=1, 20 do mydata[i] = "List item ".. i end myList = tableView.newList{ data=mydata, default="listItemBg\_white.png", over="listItemBg\_over.png", onRelease=function() end, top=topBoundary, bottom=bottomBoundary, callback=function(row) local rowgroup = display.newGroup() local t1 = display.newText(row, 0, 0, native.systemFontBold, 16) t1.x = math.floor(t1.width/2) + 12 t1.y = 46 rowgroup:insert(t1) end } group:insert(myList) end

Hi Chris,

No problem, happy to help.

I think you need to do something like this.  (I haven’t checked the table insertion code itself, just the general setup of how you make the request.)  Note that I’ve created the handler here, in the scene.  The reference to that handler gets passed to requester, so requester calls it once the network request completes.

[lua]

function scene:createScene( event )

    local group = self.view

    local function handler(data)

     – Assumption that the json decoded data that was passed to this handler is suitable for loading into a tableView

      myList = tableView.newList{

        data=data,

        default=“listItemBg_white.png”,

        over=“listItemBg_over.png”,

        onRelease=function() end,

        top=topBoundary,

        bottom=bottomBoundary,

        callback =

           function(row) 

                local rowgroup = display.newGroup()

                local t1 = display.newText(row, 0, 0, native.systemFontBold, 16)

                t1.x = math.floor(t1.width/2) + 12

                t1.y = 46 

                rowgroup:insert(t1)

          end

      }

      group:insert(myList)

    end

   requester({}, handler) 

   --note: requester is in “myFunctions.lua” and working great!

[/lua]

end

So very nearly sorted now!..

There must be some kind of scoping problem, or perhaps a race condition? Since the final “group:insert(mylist)” part doesnt seem to work if I put it at the end - everything fires (i get console output from various print statements showing that my data was read into the list correctly) but the list is not rendered :frowning:

If i remove the group:insert(mylist) line, the list is rendered, but is not part of the scene’s display group any more, so doesnt transition in nicely - which does work with data being provided with a static local table… 
 

Ignore that, I am blind.

Other elements in the scene - including BG image - were being added ontop of everything else, because I had mistakenly removed *their* group:insert’s.

IT WORKS!!

Thank you so much, you’ve opened epic doors for me here. If anyone on this thread finds themselves in the North West of the UK any time, demand beers from me.

 

Great, glad you got it working!

[oops, replied to an earlier part of the thread instead of the bottom]

  • Andrew

Hi all,

Sorry to ask, what is probably, an obvious question but I am struggling with this - I need to pass ‘event’ to a function that controls which picture to show depending upon which button is pressed.

function startPictureViewer() local function back(backButton, galleryIndex) backButton:removeSelf() backButton = nil local i = 1 while i\< 20 do if galleryIndex[i] ~= nil then galleryIndex[i]:removeSelf() galleryIndex[i] = nil end i = i + 1 end mainMenu.startMainMenu() end local function viewPicture(galleryIndex, backButton) local function closePicture() end --local buttonPressed = event.target.id for k, v in pairs(event) do print(k, v) end backButton.onRelease = function() closePicture() end local picture = display.newImage(galleryIndex[buttonPressed]) picture.x = 100 picture.y = 0 end settingsIndex = splashScreen.returnSettingsIndex() local galleryIndex = {} local filesToOpen = {} local backButton = widget.newButton { id = 1, style = "backLarge", label = "Back", fontSize = settingsIndex.fontSize, width = 120, } backButton.onRelease = function() back(backButton, galleryIndex) end backButton.x = 70 backButton.y = 55 local title = display.newText("Image Gallery", 70, 100, native.systemFont, 30) title:setTextColor(0, 0, 0) local jsonPath = system.pathForFile("DownloadedGalleryIndex.json", system.DocumentsDirectory) local f = io.open(jsonPath, "r") if f == nil then f = io.open(jsonPath, "w") local jsonTestArray = {} jsonTestArray["records"] = {} c = json.encode(jsonTestArray) f:write(c) end f:close() f = io.open(jsonPath, "r") local c = f:read ("\*a") local jsonFile = json.decode(c) local j = 1 local k = 0 local i = 1 while i \< 20 do if jsonFile["records"][i] ~= nil then galleryIndex[i] = widget.newButton { id = i, style = "redLarge", label = jsonFile["records"][i], fontSize = settingsIndex.fontSize, width = 300 } filesToOpen[i] = jsonFile["records"][i] galleryIndex[i].x = 300\*k+160 galleryIndex[i].y = j\*50 + 140 galleryIndex[i].onRelease = function() viewPicture(galleryIndex, backButton) end end if j == 10 then k = k + 1 j = 1 end j = j + 1 i = i + 1 end end

Anybody got any ideas?

Hi there,

First of all, welcome to Corona and to the forums.  :-)

On your first question, this is a common source of confusion.  The solution is to use what’s called a Lua closure, which means wrapping your function call with arguments in an anonymous function without arguments, like this:

[lua]

local function testfunc(var) 

      print(var)

end

 

local somebutton = widget.newButton {

        label=“test”,

        top = 220,

        left = 50,

        onPress = function() testfunc(“some test string”) end

    }

[/lua]

I’ve also put testfunc above the button declaration so that it’s available to be referred to when creating the button, and I declared it as local, which is always a good practice.

On your second question, the callback for network.request(), or any of the network APIs, won’t have the original query string included, so your callback won’t know what p1 and p2 were.  If you want to have different behavior for network.request() depending on p1 and p2, you have a few options.

  1. You could echo back that information as part of the response (which would be possible if you’re requesting something from a script on a server you control)
  2. You could provide a different callback to network.request() depending on what p1 and p2 are
  3. You could use the same callback every time, but when you make the network.request(), store the handle and the fact that the request involved p1 and p2 (you could use a table with the handle as the key and the values of p1 and p2 as items).  Part of the information your callback will receives is a requestId (http://docs.coronalabs.com/api/event/networkRequest/requestId.html).  You can then lookup in your table the values of p1 and p2 associated with the original call, and act accordingly

Hope this helps.

  • Andrew

Hi Chris and welcome.

Your questions are good questions.  Let me address them.

0.  Yes you will be building native apps using Lua instead of Java or Objective C.

1.  Andrew’s solution will work, but since you come from a web background, Lua Closures, at least how they are being used here is basically the same concept as an Anonymous function in JavaScript.  As to why yours was not working.  Whenever there is a parameter that expects some kind of function, it’s looking for the “Address of/pointer to the Function”. 

When the trigger (in this case a button press happens), it then calls the function at the passed address.  It knows nothing about your parameters and will make up ones of its own.  Lets look at an example:

local function handleButton(event) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "Fred Flintstone" end &nbsp; ... widget.newButton({ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; onPress = handleButton(someParam) })

When you include parans and parameters like this, Lua will execute your function **FIRST** and immediatly then pass any returned value to the caller.  In other words, the result of this would be this:

&nbsp; Of course this won't work, because there is no function at memory address "Fred Flintstone" to rexec

takes the memory address of the handleButton function and sticks that value into onPress.  Then when the code for widget.newButton gets a press, it calls the function handleButton then and passes it’s own parameters in.  In this case it’s the table named “event”.

Thanks for the replies here! 
Button press parameter handling, now not an issue - so that’s great!

However, I am still battling with a neat way to handle my networkRequests…

Here’s what I’m ideally trying to achieve:

I have several different scenes.
On a particular scene, lets call it “Sender”, before the scene is rendered I want to fire a request to my php scripts to get some data. 

I have a function that now looks a bit like this code below.

The issue I’m having now, is I have *no idea* how to get “data” back to a function in my scene that will use it to build some interface elements (data is a list of entities, each with various properties)

The skeleton code below is working fine (note: i’ve re-typed it here so if it doesn’t compile, my bad! It works fine in my app)
and is located in a module “myfunctions.lua” that I import so  that it’s globally available to all scenes (10 or more scenes in my app).

I’m guessing that in my scene, after calling requester(), I need to listen somehow for a response from handlerNumber1 so that I can retrieve ‘data’ and use it in my scene.

I’m trying to keep the code nice and modular, and wherever possible, minimal - but perhaps I am approaching it all wrong?

Really appreciate any help, it’s driving me nuts - I just cant ‘see’ the pattern I need to apply here.
 

function requester({params},another param,handler) --do some stuff with the params, --build a url local requesturl = "http://someurl.com/somestuff/?someotherstuff=something" if (handler == "handlerNumber1") then network.request( requesturl,"GET",handlerNumber1) elseif (handler == "handlerNumber2") then network.request( requesturl,"GET",handlerNumber2) else network.request( requesturl,"GET",saySomething("handler " .. handler .. " not implemented")) end end function handlerNumber1(event) print( "handlerNumber1 Called...") if ( event.isError ) then print( "Network error!") return false else local data = json.decode(event.response) return data end end function handlerNumber2(event) print( "handlerNumber2 Called...") if ( event.isError ) then print( "Network error!") return false else local data = json.decode(event.response) return data end end function saySomething(something) print(something) end

 

If I understand you right, I think what you’re trying to do is something like this:

In myfunctions.lua:

[lua]

local json = require(“json”)

local function requester(params, handler) 

   – Using params, build a requesturl…

   local requesturl = “http://someurl.com/somestuff/?someotherstuff=something

   – Local function to handle the common response to all network requests

   local function requestCallback(event)

      if event.isError then

         print(“Network error!”)

         if handler then handler(false) else print(“Handler not provided”) end            – If it exists, call the handler with ‘false’, indicating an error

      else

         local data = json.decode(event.response)

         if handler then handler(data) else print(“Handler not provided”) end            – If it exists, call the handler with the json decoded data

      end

   end

   – Launch the network request

   network.request( requesturl,“GET”, requestCallback)

end

[/lua]

In each of your scenes, you can do this:

[lua]

local function handler(data)

   – Do things in your scene based on the data

end

– Launch the request to get the data we need with some parameters

myfunctions.requester({p1=1,p2=2,p3=3}, handler)

[/lua]

  • Andrew

Thanks Andrew! This seemed to work OK, and makes sense!
I was thinking about it a bit backwards in some ways… your example was fantastic!

Having solved this, I’ve thought of another web-world --> corona / apps scenario that’s going to prove fun, to do with referencing objects on the display that are related (eg a list of text fields, each with a button that increments their value by “1” when pressed. In jQuery et al, I can use parent… closest… and classes / IDs to target things… without the DOM, I have no clue. 

Anyway, Ill put a separate topic up about that I think!

Thanks again!

Chris

 

Ah… Hopefully last question on this topic!!
Gaving got the result from my HTTP request, Im still a bit stuck! 

The data that I received from my request is to be used to create a dynamic list - so in my scene I have code like this:

Got to say, I’m really impressed with the support people are giving me - hopefully I can stop bugging you all with this soon.
This scenario is pretty key to what i’m building, so lots of rinse-and-repeat once it’s working!
 

function scene:createScene( event ) local group = self.view requester({},handler) --note: requester and handler are in "myFunctions.lua" and working great! --how do I get the data that handler has received into my list below? --i'm guessing I need to do something like data=function() requester({},handler) end local mydata = {} for i=1, 20 do mydata[i] = "List item ".. i end myList = tableView.newList{ data=mydata, default="listItemBg\_white.png", over="listItemBg\_over.png", onRelease=function() end, top=topBoundary, bottom=bottomBoundary, callback=function(row) local rowgroup = display.newGroup() local t1 = display.newText(row, 0, 0, native.systemFontBold, 16) t1.x = math.floor(t1.width/2) + 12 t1.y = 46 rowgroup:insert(t1) end } group:insert(myList) end

Hi Chris,

No problem, happy to help.

I think you need to do something like this.  (I haven’t checked the table insertion code itself, just the general setup of how you make the request.)  Note that I’ve created the handler here, in the scene.  The reference to that handler gets passed to requester, so requester calls it once the network request completes.

[lua]

function scene:createScene( event )

    local group = self.view

    local function handler(data)

     – Assumption that the json decoded data that was passed to this handler is suitable for loading into a tableView

      myList = tableView.newList{

        data=data,

        default=“listItemBg_white.png”,

        over=“listItemBg_over.png”,

        onRelease=function() end,

        top=topBoundary,

        bottom=bottomBoundary,

        callback =

           function(row) 

                local rowgroup = display.newGroup()

                local t1 = display.newText(row, 0, 0, native.systemFontBold, 16)

                t1.x = math.floor(t1.width/2) + 12

                t1.y = 46 

                rowgroup:insert(t1)

          end

      }

      group:insert(myList)

    end

   requester({}, handler) 

   --note: requester is in “myFunctions.lua” and working great!

[/lua]

end

So very nearly sorted now!..

There must be some kind of scoping problem, or perhaps a race condition? Since the final “group:insert(mylist)” part doesnt seem to work if I put it at the end - everything fires (i get console output from various print statements showing that my data was read into the list correctly) but the list is not rendered :frowning:

If i remove the group:insert(mylist) line, the list is rendered, but is not part of the scene’s display group any more, so doesnt transition in nicely - which does work with data being provided with a static local table…