Jump to content

[TOPIC: topicViewTemplate]
[GLOBAL: userSmallPhoto]
Photo

How to update a displayed table after deleting some rows? Also have a problem with table not finding the right properties after removing objects.
Started by Rodayan Jan 06 2019 09:17 PM

6 replies to this topic
table reload remove widget

Best Answer Rob Miracle , 08 January 2019 - 05:16 PM

A table can mean different things. In Excel, a table is a grid of rows and columns. On an HTML based web page, a table can also be represented as a series of rows and columns. But in computer terms, this can either be represented as either a 2-dimensional array of rows and columns or it can be expressed in more of a database structure, where each column could be represented by what content is in that Column. Consider:

Row  Name   Age    Occupation
1    Bill    35    Farmer
2    Sandy   18    CEO
3    Andy    36    Comedian

In this case, a record is made up of the data points or fields called, Name, Age, Occupation. In a spreadsheet, these might be column headers, but in code, these would actually be Column_B, Column_C, and Column_D if you could rename the columns.

In a database, you would have multiple rows of these records. A typical spreadsheet can be viewed in similar rows if you don't think of the columns as columns but the fields in the record.

 

Now many computer programs will have a special data type, from 'C's "struct" to an associative array in PHP or a hash table in Perl or a dictionary in Objective C. But in generic terms, these can be called objects and each of the fields an object's members.

 

Computers also have a data type known as an Array. An array is simply a way to store multiple objects together. These are frequently indexed by numbers, so an array of objects, each array entry would be your spreadsheet row, which contains a single object. If you had 10 rows of data in your spreadsheet, your array would have 10 entries.

 

Now jump to Lua. The language creators decided (for better or worse) to have a type called table. A table can be indexed by numbers, making it behave like an array. Or it can be indexed by names, making it act like a struct/dictionary/object. And to compound matters, Lua gives you two different ways to index by names. Thus you can have:

local myObjectTable = {}
myObjectTable.name = "Andy"
myObjectTable.age = 36
myObjectTable["occupation"] = "Comedian"

Note how I used the .attribute for some and the ["attribute"] for another. In Lua these are completely interchangeable. PHP people would prefer the ["name"] version where C struct people like the dot notation. There is one exception. If the name has special characters in like hypens, spaces, etc. you have to use the ["name"] version.

 

Tables also get used for arrays:

local myObjectTable = {}
myObjectTable.name = "Andy"
myObjectTable.age = 36
myObjectTable["occupation"] = "Comedian"

local myObjectTableSandy = {}
myObjectTableSandy.name = "Sandy"
myObjectTableSandy.age = 18
myObjectTable["occupation"] = "CEO"

local myArrayOfPeople = {}
myArrayOfPeople[1] = myObjectTable
myArrayOfPeople[2] = myObjectTableSandy

In the array version, the table indexes are numbers. In spreadsheet terms, myArrayOfPeople are your rows, the "name", "age" and "occupation" are your columns.

 

To reference an individual data point:

local sallysAge = myArrayOfPeople[2].age

Hopefully, all of that will help you understand Lua tables a bit better. Now let me throw you a complete change of direction.

 

When Apple created iOS, they created a special UI element called a "Table View". A tableView is an object with many rows of data but just a single column of data. What goes in that single column of data can contain various items like labels (text/number display), buttons and other controls making a single row in a tableView look like it has multiple data items, but internally it's a single item... a row.

 

Corona's widget.newTableView() is designed to mimic that basic UI item (Android and other OS's have similar features). So a tableView really isn't a table data structure. Frequently a table data structure is used to provide the data for a tableView to show, ergo them sharing the name "table".

 

In Corona you create a tableView object by calling widget.newTableView(). After it's created and you have a handle/variable name to the table, you can then loop over your table of data and insert view rows into the table view. This is done with something like:

for i = 1, #myArrayOfPeople do
     myTableView:insertRow( { various parameters for that row in the tableView} )
end

If you want to delete an individual row, you can call myTableView:deleteRows( { indexOfRowToDelete } ) such as:

myTableView:deleteRows( { 2 } ) -- delete's Sandy

or you can delete them all in one action:

myTableView:deleteAllRows()

(see: http://docs.coronalabs.com/api/type/TableViewWidget/deleteAllRows.html)

 

If you do this, you simply loop over your data and call :insertRow() just like you did when you first populated the tableView, this time without the deleted rows.

 

Deleting rows isn't a simple task. There are many, many options involved. For instance, how do you want to remove the data from your data table or do you just want to hide that row? Keep in mind your data table and your tableView should be kept separate. I would suggest you read about "Model-View-Controller" to help understand why it's important that you tableView is just a display or view or your data and not actually your data.  Do you want to have a delete button that slides in and out on a swipe?  You have to code all of it, not of it comes for free. Do you really want to blow away and re-insert all the data? If so and you have more rows than fit on the screen, is it important to jump back to where the user was? If they deleted the first visible row, do you move everything up by one, or jump back to the row that was just off screen?  There is a lot to think about and it's not novice level concept.

 

If you want to get the basics of tableViews down, there are several of our SampleCode apps in your Corona folder that use tableViews. Almost none of these support deleting rows. If you want to see something that's closer to a fully functional "Let me delete rows" tableview, you can visit: https://github.com/coronalabs-samples/CoronaWeather

 

And look in the scenes/locations.lua file. In here I use a couple of different tableViews. One to hold the selected locations the user wants and they can delete them unless I set a noDelete flag (basically to prevent them from removing "Current Location"). It also uses a second tableView along with a native.newTextField() to read locations from a database based on what they've typed in. Pretty cool overall functionality, but it's a bit of code to do all of that.

 

I don't want to discourage you, but as the saying goes, you have to learn to walk before you can learn to run.

 

Rob

[TOPIC CONTROLS]
[/TOPIC CONTROLS]
[modOptionsDropdown]
[/modOptionsDropdown]
[reputationFilter]
[TOPIC: post.html]
#1

Rodayan

[GLOBAL: userInfoPane.html]
Rodayan
  • Observer

  • 9 posts
  • Corona SDK

I am using the widget.newTableView to create a display of my table. This works fine.

I have ID, name, type all displayed in fake columns (by manipulating the x and y coordinates).

 

So I move an object to a new table. I remove them in the widget table. Now I want to reload the displayed data (or, failing that, to rewrite the widget table).

 

One problem I had writing the code is that the id of the plant is not the same as the table row (because I have a category row). That is why I had to use newIDt (table) and newIDd (display) rather than just one newID. Even so if I add a second object, the print strings (and thus any property yanking I do from the table) match up the wrong name for the picture. Not sure how to deal with this.

 

The error I get  when trying to reload is: for line 121 of this code: attempt to index a nil value in function 'reloadData'

 

My code is below. I hope I got the formatting right for the forum and that some people can help me!

--table of objects: original set
tableplants= {}
tableplants[1] = {bpid="ID", bpname="Names", bptype="Type", }
tableplants[2] = {bpid="01", bpname="banacharis", bptype="water", }
tableplants[3] = {bpid="02", bpname="bazsword", bptype="water",}
tableplants[4] = {bpid="03", bpname="breeds", bptype="water", }
tableplants[5] = {bpid="04", bpname="bwaterlily", bptype="water", }
tableplants[6] = {bpid="05", bpname="bbirdsnestfern", bptype="land", }
tableplants[7] = {bpid="06", bpname="bbromeliad", bptype="land", }
tableplants[8] = {bpid="07", bpname="bdutchmanspipe", bptype="land",}
tableplants[9] = {bpid="08", bpname="bfern", bptype="land", }
tableplants[10] = {bpid="09", bpname="bleaves", bptype="land", }

--rendering function
function onRowRender( event )
 
   --Set up the localized variables to be passed via the event table
   local row = event.row
   local id = row.index
   local params = event.row.params
 
   row.bg = display.newRect( 0, 0, display.contentWidth, 60 )
   row.bg.anchorX = 0
   row.bg.anchorY = 0
   row.bg:setFillColor( 1, 1, 1 )
   row:insert( row.bg )
 
   if ( event.row.params ) then    
      row.bpidText = display.newText( params.bpid, 12, 0, native.systemFont, 30 )
      row.bpidText.anchorX = 0
      row.bpidText.anchorY = 0.5
      row.bpidText:setFillColor( 0,0,.5)
      row.bpidText.y = 20
      row.bpidText.x = 30
 
      row.bpnameText = display.newText( params.bpname, 12, 0, native.systemFont, 30 )
      row.bpnameText.anchorX = 0
      row.bpnameText.anchorY = 0.5
      row.bpnameText:setFillColor( 0,0,0 )
      row.bpnameText.y = 20
      row.bpnameText.x = 100
            
      row.bptypeText = display.newText( params.bptype, 12, 0, native.systemFont, 30 )
      row.bptypeText.anchorX = 0
      row.bptypeText.anchorY = 0.5
      if params.bptype=="water" then
        row.bptypeText:setFillColor( 0,.5,1)
      else
        row.bptypeText:setFillColor(0,.5,0)
      end
      row.bptypeText.y = 20
      row.bptypeText.x = 350  
 
      row:insert( row.bpidText )
      row:insert( row.bpnameText )
      row:insert( row.bptypeText )
   end
   return true
end

--Small plants table: this is in the section scene:show( event ) phase=="did" area.
 smallPlantsTable = widget.newTableView(
	{
    left = 730,
    top = 20,
    height = 500,
    width = 650,
    onRowRender = onRowRender,
    onRowTouch = onRowTouch,
    backgroundColor = { .66, .65, .77 },
    listener = scrollListener
	})
    mainGroup:insert(smallPlantsTable)
    
    -- Insert as many rows as the table "rowText"
for i = 1, #tableplants do
   smallPlantsTable:insertRow{
      rowHeight = 60,
      isCategory = false,
      rowColor = { 1, 1, 1 },
      lineColor = { 0.80, 0.80, 0.80 },
      params = {
        bpid = tableplants[i].bpid,
        bpname = tableplants[i].bpname,
        bptype = tableplants[i].bptype,
      }
    }
      -- Make some rows categories
    if ( i == 1 ) then
        isCategory = true
        rowHeight = 40
        rowColor = { default={0.8,0.8,0.8} }
        lineColor = { 1, 0, 0 }
        text=native.systemFontBold, 40 -- This doesn't seem to do anything! I want the categories to be bold.
      end

    end
  end
end

  tablehandplants= {}  --this is the new table to move plants into
  
  local function createPlant1()
    local newIDt = math.random(2,#tableplants)
    newIDd=newIDt-1
    local newPlant1 = display.newImageRect( mainGroup, objectSheet1, newIDd, 100, 100 )
    newPlant1.x, newPlant1.y = 100, 120
    newPlant1.id = ( "Plant "..newIDd.."is "..newIDt.." in table")
    newPlant1.name= (tableplants[newIDt].bpname)
    print("Plant "..newIDd..", "..newPlant1.name.." was displayed")
    table.insert( tablehandplants, newIDd) 
    print("Entry Added. TableHandPlants now has " .. #tablehandplants .. " entries")
    print ("TablePlants has " ..#tableplants .. " entries")
    table.remove(tableplants, newIDt)
    print ("Entry removed. TablePlants now has " ..#tableplants .. " entries")
  end
  
   --this is the game start. Clicking on a button called StartButton calls this function:
  local function startGame() 
      createPlant1()
      --refresh table
      smallPlantsTable.reloadData()
      print ("Reloaded table")   
  end


[TOPIC: post.html]
#2

XeduR @Spyric

[GLOBAL: userInfoPane.html]
XeduR @Spyric
  • Contributor

  • 424 posts
  • Corona SDK

Well, that code cannot be run as is since it is missing the widget library, setting up the display group, etc. Also, row 121 seems to be pointing at a commented line, so I'm not sure if the error you pointed to refers to this line:

smallPlantsTable.reloadData()

If it does, then perhaps you are receiving the error because you used "." instead of ":", as in "smallPlantsTable:reloadData()" if you are using this function https://docs.coronalabs.com/api/type/TableViewWidget/reloadData.html.



[TOPIC: post.html]
#3

Rodayan

[GLOBAL: userInfoPane.html]
Rodayan
  • Observer

  • 9 posts
  • Corona SDK

Hi XeduR,

I heavily edited that code so you didn't have to look at the over a thousand lines of code in the original dealing with other things.

At the beginning I used local widget = require( "widget" ) to call the widget library. In the scene:create area I have displayed the background and other objects as well as setting up display groups (backGroup, mainGroup, uiGroup). Is there anything else I should be aware of?

Yes you are correct that the error points to 122 in this code version, I wonder why it changed from what I had onscreen!

 

I have just tried using a colon : which sort of works. At least it doesn't cause an error, but It still doesn't refresh my table. The print on the console confirms that the table is changing (the number of entries is decreasing) but it just doesn't change the display. I know that part of the function is being called because it prints "Reloaded table" after that line like I told it to at line 123.  Do I have to set up something that will call onRowRender somehow? I can't just call it since if I add a line smallPlantsTable:onRowRender() it just says method 'onRowRender' is a nil value.

 

Thanks for your continued answerage,

Rodayan



[TOPIC: post.html]
#4

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 25,133 posts
  • Corona SDK

Unless you have hundreds of rows of data, it's frankly a one frame update to delete the rows and re-insert them in most cases.

 

Rob



[TOPIC: post.html]
#5

Rodayan

[GLOBAL: userInfoPane.html]
Rodayan
  • Observer

  • 9 posts
  • Corona SDK

Hi Rob,

Awesome. How do I write this magical delete and reinsert code? This probably seems trivial to you, but I have no idea.  Do I somehow call the original writing of the table 62-99, which was not actually a function as far as I know? I saw an "explanation" saying that you just had to pull in the new table data, delete, and remake - but no idea how to actually CODE that, especially the getting new data bit.

 

My background is in using Microsoft Excel where what you see in a grid is what you get, so I don't really understand the Lua "table" concept.

 

Thanks, Rodayan



[TOPIC: post.html]
#6

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 25,133 posts
  • Corona SDK

  Best Answer

A table can mean different things. In Excel, a table is a grid of rows and columns. On an HTML based web page, a table can also be represented as a series of rows and columns. But in computer terms, this can either be represented as either a 2-dimensional array of rows and columns or it can be expressed in more of a database structure, where each column could be represented by what content is in that Column. Consider:

Row  Name   Age    Occupation
1    Bill    35    Farmer
2    Sandy   18    CEO
3    Andy    36    Comedian

In this case, a record is made up of the data points or fields called, Name, Age, Occupation. In a spreadsheet, these might be column headers, but in code, these would actually be Column_B, Column_C, and Column_D if you could rename the columns.

In a database, you would have multiple rows of these records. A typical spreadsheet can be viewed in similar rows if you don't think of the columns as columns but the fields in the record.

 

Now many computer programs will have a special data type, from 'C's "struct" to an associative array in PHP or a hash table in Perl or a dictionary in Objective C. But in generic terms, these can be called objects and each of the fields an object's members.

 

Computers also have a data type known as an Array. An array is simply a way to store multiple objects together. These are frequently indexed by numbers, so an array of objects, each array entry would be your spreadsheet row, which contains a single object. If you had 10 rows of data in your spreadsheet, your array would have 10 entries.

 

Now jump to Lua. The language creators decided (for better or worse) to have a type called table. A table can be indexed by numbers, making it behave like an array. Or it can be indexed by names, making it act like a struct/dictionary/object. And to compound matters, Lua gives you two different ways to index by names. Thus you can have:

local myObjectTable = {}
myObjectTable.name = "Andy"
myObjectTable.age = 36
myObjectTable["occupation"] = "Comedian"

Note how I used the .attribute for some and the ["attribute"] for another. In Lua these are completely interchangeable. PHP people would prefer the ["name"] version where C struct people like the dot notation. There is one exception. If the name has special characters in like hypens, spaces, etc. you have to use the ["name"] version.

 

Tables also get used for arrays:

local myObjectTable = {}
myObjectTable.name = "Andy"
myObjectTable.age = 36
myObjectTable["occupation"] = "Comedian"

local myObjectTableSandy = {}
myObjectTableSandy.name = "Sandy"
myObjectTableSandy.age = 18
myObjectTable["occupation"] = "CEO"

local myArrayOfPeople = {}
myArrayOfPeople[1] = myObjectTable
myArrayOfPeople[2] = myObjectTableSandy

In the array version, the table indexes are numbers. In spreadsheet terms, myArrayOfPeople are your rows, the "name", "age" and "occupation" are your columns.

 

To reference an individual data point:

local sallysAge = myArrayOfPeople[2].age

Hopefully, all of that will help you understand Lua tables a bit better. Now let me throw you a complete change of direction.

 

When Apple created iOS, they created a special UI element called a "Table View". A tableView is an object with many rows of data but just a single column of data. What goes in that single column of data can contain various items like labels (text/number display), buttons and other controls making a single row in a tableView look like it has multiple data items, but internally it's a single item... a row.

 

Corona's widget.newTableView() is designed to mimic that basic UI item (Android and other OS's have similar features). So a tableView really isn't a table data structure. Frequently a table data structure is used to provide the data for a tableView to show, ergo them sharing the name "table".

 

In Corona you create a tableView object by calling widget.newTableView(). After it's created and you have a handle/variable name to the table, you can then loop over your table of data and insert view rows into the table view. This is done with something like:

for i = 1, #myArrayOfPeople do
     myTableView:insertRow( { various parameters for that row in the tableView} )
end

If you want to delete an individual row, you can call myTableView:deleteRows( { indexOfRowToDelete } ) such as:

myTableView:deleteRows( { 2 } ) -- delete's Sandy

or you can delete them all in one action:

myTableView:deleteAllRows()

(see: http://docs.coronalabs.com/api/type/TableViewWidget/deleteAllRows.html)

 

If you do this, you simply loop over your data and call :insertRow() just like you did when you first populated the tableView, this time without the deleted rows.

 

Deleting rows isn't a simple task. There are many, many options involved. For instance, how do you want to remove the data from your data table or do you just want to hide that row? Keep in mind your data table and your tableView should be kept separate. I would suggest you read about "Model-View-Controller" to help understand why it's important that you tableView is just a display or view or your data and not actually your data.  Do you want to have a delete button that slides in and out on a swipe?  You have to code all of it, not of it comes for free. Do you really want to blow away and re-insert all the data? If so and you have more rows than fit on the screen, is it important to jump back to where the user was? If they deleted the first visible row, do you move everything up by one, or jump back to the row that was just off screen?  There is a lot to think about and it's not novice level concept.

 

If you want to get the basics of tableViews down, there are several of our SampleCode apps in your Corona folder that use tableViews. Almost none of these support deleting rows. If you want to see something that's closer to a fully functional "Let me delete rows" tableview, you can visit: https://github.com/coronalabs-samples/CoronaWeather

 

And look in the scenes/locations.lua file. In here I use a couple of different tableViews. One to hold the selected locations the user wants and they can delete them unless I set a noDelete flag (basically to prevent them from removing "Current Location"). It also uses a second tableView along with a native.newTextField() to read locations from a database based on what they've typed in. Pretty cool overall functionality, but it's a bit of code to do all of that.

 

I don't want to discourage you, but as the saying goes, you have to learn to walk before you can learn to run.

 

Rob



[TOPIC: post.html]
#7

Rodayan

[GLOBAL: userInfoPane.html]
Rodayan
  • Observer

  • 9 posts
  • Corona SDK

Dear Rob,

Thank you, that was a wonderful answer and just what I needed (and not necessarily wanted, but definitely needed) to hear. Top marks.




[topic_controls]
[/topic_controls]

Also tagged with one or more of these keywords: table, reload, remove, widget