• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 215
  • Last Modified:

Keeping track of checked check boxes from page to page on multi-page results

Using Coldfusion 9.  I've got a results page that dynamically breaks up into pages with 30 records on each page.  I want my users to be able to select records on each page via a checkbox and submit to an INSERT function when they have selected the records they want.  The problem is, I need some way to keep track of which records have been checked when the user navigates from page to page of the results.  I was thinking I could somehow keep submitting the form to itself and appending the values from page to page.  Is this the best way to do this?
0
MFredin
Asked:
MFredin
  • 9
  • 8
  • 5
2 Solutions
 
gdemariaCommented:
> I was thinking I could somehow keep submitting the form to itself and appending the values from page to page

I think that is a good approach.  I like submitting a page to itself for a simple, yet powerful approach.

You may also want to consider storing the values in the database.  Not sure if there is any longer term use for the values or if by the end of the session the values can be removed, but the database can also provide you with some good control and it will not slow things down.
0
 
gdemariaCommented:
>  breaks up into pages with 30 records on each page

Every page looks the same, correct?  It's just a listing with page 1, 2, 3, etc...

If so, I would definitely use just one page with a paging algorithm.  You can easily keep track of the checked records using a simple hidden form field.

The database is still a good option if you are eventually going to use the results, you can store them earlier than you may have been planning.



0
 
dwkdCommented:
have you tried using sessions? that is a good way to store variables.
hidden form fields are vulnerable to cross browser scripting

0
Upgrade your Question Security!

Your question, your audience. Choose who sees your identity—and your question—with question security.

 
MFredinAuthor Commented:
Cool.  I think submitting to the same page with a hidden form field is going to work.  The only problem I'm running into is I'm getting a bunch of duplicates in my list when I go back to a page that I have checked boxes and then submit to another page.... basically it adds duplicates to the hidden form field.  What is the easiest way to keep the duplicates out of my list?
0
 
gdemariaCommented:
Store the items that are already checked on that page in a second hidden field.   This allow your update logic to know that the item doesn't need to be added again, it also gives you the added benefit of knowing which fields have been unchecked.  That is in case the user navigates back to a previous page and unchecks some boxes.  It's an easy way to see what needs to be removed from the list as well as added to the list.

So to recap, one hidden field for all currently checked items
Another hidden field for only the items already checked on the page being drawn

0
 
MFredinAuthor Commented:
gdemaria, sounds like this is what I want to do but I'm having a tough time wrapping my head around it.  

The second hidden input is confusing.  How will I know which items have already been checked on that page?  Compare my carrier_id list to the carrier_ids returned from the query for that page?  I'm confused here.  I'm not actually adding anything to my database until the user has checked all the boxes they wanted and selected the insert button.  Until then, I'm just keeping a running list of which boxes they have checked.  But you are right, I need to be able to account for times when they go back to a page and uncheck boxes.  
0
 
gdemariaCommented:
I understand, give me a few minutes..
0
 
gdemariaCommented:
let me know if this outline make sense...


<!---- this happens only the very first time the page is loaded, not on any form posts ---->
<cfif NOT isDefined("form.hiddenIDs")>
  <!---- initally populate your hiddenIDs form field will all IDs currently checked in the database ---->
  <cfquery name="getAllChecked" datasource="#request.datasource#">
     select ID
      from table...
  </cfquery>
  <cfset form.hiddenIDs = valueList(getAllChecked.ID)>
</cfif>


<!---- this code will fetch your page full of data ---->
<cfquery name="getPageList" datasource="#request.datasource#">
  select ID, Name, etc..
   from table
  where ... for items of this page...
</cfquery>

<!---- show the items with a checkbox ----->

<cfloop query="getPageList">
 <cfif listFind(form.hiddenIDs,getpageList.ID)> <!--- is it currently checked? ---->
   <input type="Hidden" name="saveIDs" value="#getPageList.ID#"> <!--- remember that it is checked for use in post action ---->
   <cfset status="checked='checked'">
 <cfelse>
   <cfset status=""> <!--- not checked ---->
 </cfif>
 <input type="checkbox" name="pageIDs" value="#getPageList.ID#" #status#> #getPageList.itemName# <br>
</cfloop>

<input type="Hidden" name="hiddenIDs" value="#form.hiddenIDs#">





The action...  

<cfparam name="form.saveIDs" default=""> <!--- this page's items that were checked when the page was drawn ---->
<cfparam name="form.pageIDs" default=""> <!--- this page's items, that are checked when the page was SUBMITTED ---->
<cfparam name="form.hiddenIDs" default=""> <!--- master list of all items checked from all pages ---->

<!--- newly checked IDs to add ---->
<cfloop index="anID" list="#form.pageIDs#">
  <cfif listFind(form.hiddenIDs,anID) eq 0>
    <cfset form.hiddenIDs = listAppend(form.hiddenIDs,anID)>
  </cfif>
</cfloop>

<!--- items that were UNchecked on this submit ---->
<cfloop index="anID" list="#form.saveIDs#">
  <cfif listFind(form.pageIDs,anID)> <!---- it used to be checked, not anymore ---->
     <cfset pos = listFind(form.hiddenIDs,anID)> <!--- find it on the master list ---->
     <cfif pos> <!---- make sure it's not on the master list, it shouldn't be ---->
        <cfset form.hiddenIDs = listDeleteAt(form.hiddenIDs,pos,anID)>
     </cfif>
  </cfif>
</cfloop>

Open in new window

0
 
MFredinAuthor Commented:
Here is what I have.... might help understand what I'm doing.  I'm still unclear how to integrate your thought into this.


<cfparam name="country" default="US">
<cfparam name="state" default="MN">
<cfparam name="city" default="Springfield">
<cfparam name="distance" default="5">
<cfparam name="carrier_id" default="">

<cfinvoke component="CFC.loads" method="getCoordinates" state="#state#" city="#city#" returnvariable="coordinates" >

<cfquery name="fmcsa">
SELECT census_num
FROM fmcsa LEFT JOIN fmcsa_licensed_link ON fmcsa.CENSUS_NUM = fmcsa_licensed_link.dot_id 
WHERE PHY_NATN = '#country#'
AND PHY_ST = '#state#'
AND 3963.191 * ACOS((SIN(PI() * #coordinates.latitude# / 180) * SIN(PI() * fmcsa.latitude / 180)) + (COS(PI() * #coordinates.latitude# /180) * cos(PI() * fmcsa.latitude / 180) * COS(PI() * fmcsa.longitude / 180 - PI() * #coordinates.longitude# / 180)) ) <= #distance#
GROUP BY CENSUS_NUM
</cfquery>

<!--- Break into Pages --->
<cfparam name="PageIndex" default="0">
<cfset RecordsPerPage = 20>
<cfset TotalPages = ROUND((fmcsa.Recordcount/RecordsPerPage)-1)>
<cfset StartRow = (PageIndex*RecordsPerPage)+1> 
<cfset EndRow = StartRow+RecordsPerPage-1> 

<cfform action="test2.cfm" method="post">

<table>
	<tr>
      <td>
      <!--- Change Pages --->
      <cfloop index="Pages" from="0" to="#TotalPages#">
      <cfoutput>
         <cfset DisplayPgNo = Pages>
         <cfif PageIndex eq pages>
            <input type="submit" name="PageIndex" value="#DisplaypgNo#" style="color:white;background-color:blue;">
         <cfelse>
            <input type="submit" name="PageIndex" value="#DisplaypgNo#">
         </cfif>
        <cfif #pages# neq #totalpages#>|</cfif>
      </cfoutput>
      </cfloop>
      </td>
   </tr>	
<cfoutput>

<!--- Hidden Form Fields --->
<cfinput type="hidden" name="country" value="#country#">
<cfinput type="hidden" name="state" value="#state#">
<cfinput type="hidden" name="city" value="#city#">
<cfinput type="hidden" name="carrier_id" value="#carrier_id#">

<cfloop query="fmcsa">	
<cfif CurrentRow gte StartRow >

	<tr>
		<td>
		<input type="checkbox" name="carrier_id" value="#census_num#" <cfif listcontainsnocase(#carrier_id#, #census_num#)>checked="checked"</cfif>> #fmcsa.CurrentRow#</td>
	</tr>
</cfif>
<cfif CurrentRow eq EndRow>
      <cfbreak>
   </cfif>	
</cfloop>

	<tr>
	<td> 
	<input type="submit" name="submit" value="Save Now" />	
	</td>
	</tr>	
	
   </cfoutput>
</table>
</cfform>

Open in new window

0
 
MFredinAuthor Commented:
My plan was then to insert the Insert code when <isdefined("FORM.submit")> which means the user has clicked the Save Now button at the bottom.
0
 
dwkdCommented:
and why not use sessions variables to store which form fields are checked on every page? :)
0
 
MFredinAuthor Commented:
dwkd, would you happen to have an example of how I could make this work with session variables?  
0
 
dwkdCommented:
first, you have to stop naming all your checkboxes input fields with the same name
instead of carrier_id (which is what you have now) put carrier_id#fmcsa.CurrentRow# that way you get unique dynamic names which gives you the ability to inspect the fields individually later on(in code snippet)
<cflock scope="Session" timeout="30" type="Exclusive">
<cfif isDefined("form.PageIndex")>
<cfset "session.chkbtns.storeChkd_Page#session.chkbtns.previousPage#" = "">
<cfloop query="fmcsa">
<cfif isDefined("form.carrier_id#fmcsa.CurrentRow#")>
<cfset "session.chkbtns.storeChkd_Page#session.chkbtns.previousPage#" = ListAppend(Evaluate("session.chkbtns.storeChkd_Page#session.chkbtns.previousPage#"),"form.carrier_id#fmcsa.CurrentRow#",",")>
</cfif>
</cfloop>
<cfset session.chkbtns.previousPage = form.PageIndex>
<cfelse> 
<cfset session.chkbtns.previousPage = 0> 
</cfif>
</cflock>

Open in new window

0
 
dwkdCommented:
oh, i forgot
make sure you have sessions enabled inside the nearest application.cfm
it should only be a few lines:

<cfapplication name="GIVE_IT_A_NAME"
  SessionManagement="Yes"
  SessionTimeout="#CreateTimeSpan(0,0,30,0)#">
0
 
dwkdCommented:
and at the end
you'll find all the checked fields results in these variables:

for page 0
session.chkbtns.storeChkd_Page0

for page 1
session.chkbtns.storeChkd_Page1
.
.
.
.
for page n
session.chkbtns.storeChkd_Pagen


0
 
gdemariaCommented:

I don't understand what value your checkbox is supposed to have.    In this line, you are setting a value of census_num for a checkbox named carrier_id and you're display just the row counter 1,2,3..  

<input type="checkbox" name="carrier_id" value="#census_num#" <cfif listcontainsnocase(#carrier_id#, #census_num#)>checked="checked"</cfif>> #fmcsa.CurrentRow#

I would think they should all be consistent?

<input type="checkbox" name="census_num" value="#fmcsa.census_num#" <cfif listcontainsnocase(mastListOfCensusNumbers, #fmcsa.census_num#)>checked="checked"</cfif>> #fmcsa.census_num#


currently you have the carrier_id in a hidden field (second line below) and within the loop for the checkboxes, so it repeats..  

What value and name should the checkbox really have?

<cfinput type="hidden" name="city" value="#city#">
<cfinput type="hidden" name="carrier_id" value="#carrier_id#">

<cfloop query="fmcsa">  
<cfif CurrentRow gte StartRow >

        <tr>
          <td>
          <input type="checkbox" name="carrier_id" value="#census_num#" <cfif listcontainsnocase(#carrier_id#, #census_num#)>checked="checked"</cfif>> #fmcsa.CurrentRow#
          </td>
        </tr>
</cfif>

Open in new window

0
 
MFredinAuthor Commented:
dwkd,

Thanks, I'm going to try your solution later today.


gdmaria,  

I just named that field carrier_id for no real reason.  I guess it just made more sense in my head at the time.  I will rename it to census_num to be consistent.  So assume census_num where carrier_id is.
0
 
MFredinAuthor Commented:
dwkd, is there any way I can check if the current form name is in the session.chkbtns.storeChkd_Page#PageIndex#, and if so, check the check box.  This is in the cases when a user goes back to a page instead of forward, so the check boxes he has already previously selected will remain checked.  

Here is what i have and isn't working but can hopefully give you and idea of what I mean:

<input type="checkbox" name="#census_num#" value="#census_num#" class="carriers" <cfif listcontainsnocase('session.chkbtns.storeChkd_Page#PageIndex#', '#census_num#')>checked="checked"</cfif> >


I changed the check box name to #census_num#.  Could I just use the session variables with my id's and dump them all into a list I could submit to the action page when I click the "Save now" button?  If so, how would I do that?  This way I can use session variables to keep track of which id's are checked on each page and get the list of the checked values to use when inserting on the action page.  Is this possible?  
0
 
gdemariaCommented:
I would not try to store your values using a session variable for each page.   I believe it would become confusing quickly.   If you change your page size from 30 records on a page to 25, then the items that were on each page are not all shifted around and you will have to look in multiple session variables for your census_num.

I also think you should keep the name for all checkboxes the same.  That way the browser will be responsible for collecting the values and putting them in a nice list for you.   If you have 500 records and 25 of them are checked off, how can you easily find which records are checked if all the form names are different?

0
 
gdemariaCommented:
I've put my example into your code to make it easier to understand.

The approach uses one master hidden form field called form.allCensusNums.  This is a list of all censusNums that have been checked.

There are two other form variables involved.   Form.census_num after posting will be a list of all checked items.   Form.startCensusNums is a list of all items that are checked when the page is loaded.

Having the before list and the after list for each page makes it very easy for you to loop through and see what changes where made on that page, then you update the master list of form.allCensusNums to reflect the checked and unchecked values.

Please note, towards the top there is a note for you to change a query so that you get the starting list of census nums that should be checked.  

<cfparam name="country" default="US">
<cfparam name="state" default="MN">
<cfparam name="city" default="Springfield">
<cfparam name="distance" default="5">
<cfparam name="carrier_id" default="">

<cfinvoke component="CFC.loads" method="getCoordinates" state="#state#" city="#city#" returnvariable="coordinates" >

<!--- form.allCensusNums is the master list of all items checked from all pages ---->
<cfif NOT isDefined("form.allCensusNums")>
    <!--- since our master list is not yet defined, fetched from the database a list of all 
          censusNums that should be checked when the page loads for the first time
          This list could be empty or have some items already checked
          ---->

    <!---- 
      ****** ALERT ******
      Change this query to fetch the census_num of only the records that should be checked
      ---->
    <cfquery name="getCheckedValues">
     SELECT census_num
      FROM fmcsa 
      LEFT JOIN fmcsa_licensed_link ON fmcsa.CENSUS_NUM = fmcsa_licensed_link.dot_id 
     WHERE PHY_NATN = '#country#'
     AND PHY_ST = '#state#'
     AND 3963.191 * ACOS((SIN(PI() * #coordinates.latitude# / 180) * SIN(PI() * fmcsa.latitude / 180)) + (COS(PI() * #coordinates.latitude# /180) * cos(PI() * fmcsa.latitude / 180) * COS(PI() * fmcsa.longitude / 180 - PI() * #coordinates.longitude# / 180)) ) <= #distance#
     GROUP BY CENSUS_NUM
    </cfquery>
    <cfset form.allCensusNums = valueList(getCheckedValues.census_num)>
</cfif>


<cfparam name="form.startCensusNums" default=""> <!--- this page's census_num that were checked when the page was LOADED ---->
<cfparam name="form.census_num" default="">      <!--- this page's census_num that are checked when the page was SUBMITTED ---->


<!---- ACTION HANDLER 
       Enter this area if the pageindex was clicked or if the save button was pressed
       This area will add/remove checked census_num values to the master list  form.allcensusNums
       ----->
<cfif isDefined("form.PageIndex") or isDefined("form.doSave")>

    <!--- Find newly checked census_num that need to be added to the allCensusNums list
          The are items that are checked NOW but were not checked when the page was drawn first 
          So they will be on the census_num list, but not on the allCensusNum List---->
    <cfloop index="anID" list="#form.census_num#">
      <cfif listFind(form.allCensusNums,anID) eq 0>
        <cfset form.allCensusNums = listAppend(form.allCensusNums,anID)>
      </cfif>
    </cfloop>
    
    <!--- items that were UNchecked on this submit
          These items were on the startCensusNumbs list (they were checked when the page was drawn)
          But are not on the census_num list which means they are not checked when the page was posted
          Thus they were unchecked and need to be removed from the allCensusNums master list
          ---->
    <cfloop index="anID" list="#form.startCensusNums#">
      <cfif listFind(form.census_num,anID)> <!---- it used to be checked, not anymore ---->
         <cfset pos = listFind(form.allCensusNums,anID)> <!--- find it on the master list ---->
         <cfif pos> <!---- make sure it's not on the master list, it shouldn't be ---->
            <cfset form.allCensusNums = listDeleteAt(form.allCensusNums,pos,anID)>
         </cfif>
      </cfif>
    </cfloop>

    <cfif isDefined("form.doSave")>
        <!----
        
          Put code here to save to the database
        
          You want to save any census_num that is on the list :  form.allcensusNums
          
          ------>
    </cfif>
</cfif> <!----- end if form post action  ---->



<!---- this query gets all records for display, regardless whether they are checked or not ---->
<cfquery name="fmcsa">
  SELECT census_num
    FROM fmcsa 
    LEFT JOIN fmcsa_licensed_link ON fmcsa.CENSUS_NUM = fmcsa_licensed_link.dot_id 
   WHERE PHY_NATN = '#country#'
   AND PHY_ST = '#state#'
   AND 3963.191 * ACOS((SIN(PI() * #coordinates.latitude# / 180) * SIN(PI() * fmcsa.latitude / 180)) + (COS(PI() * #coordinates.latitude# /180) * cos(PI() * fmcsa.latitude / 180) * COS(PI() * fmcsa.longitude / 180 - PI() * #coordinates.longitude# / 180)) ) <= #distance#
   GROUP BY CENSUS_NUM
</cfquery>

    
<!--- Break into Pages --->
<cfparam name="PageIndex" default="0">
<cfset RecordsPerPage = 20>
<cfset TotalPages = ROUND((fmcsa.Recordcount/RecordsPerPage)-1)>
<cfset StartRow = (PageIndex*RecordsPerPage)+1> 
<cfset EndRow = StartRow+RecordsPerPage-1> 

<cfform action="test2.cfm" method="post">

<table>
   <tr>
      <td>
      <!--- Change Pages --->
      <cfloop index="Pages" from="0" to="#TotalPages#">
      <cfoutput>
         <cfset DisplayPgNo = Pages>
         <cfif PageIndex eq pages>
            <input type="submit" name="PageIndex" value="#DisplaypgNo#" style="color:white;background-color:blue;">
         <cfelse>
            <input type="submit" name="PageIndex" value="#DisplaypgNo#">
         </cfif>
        <cfif pages neq totalpages>|</cfif>
      </cfoutput>
      </cfloop>
      </td>
   </tr>        


<cfoutput>
  <cfloop query="fmcsa" startrow="#StartRow#" endrow="#EndRow#">  
     <tr>
         <td>
         <cfif listFind(form.allCensusNums,fmcsa.census_num)> <!--- is this one currently checked? ---->
           <input type="Hidden" name="startCensusNums" value="#fmcsa.census_num#"> <!--- remebers the items that were checked when the page was first loaded (so we know what was unchecked) ---->
           <cfset status="checked='checked'">
         <cfelse>
           <cfset status=""> <!--- not checked ---->
         </cfif>
         <input type="checkbox" name="census_num" value="#fmcsa.census_num#" #status#> #fmcsa.CurrentRow#
         </td>
     </tr>
  </cfloop>
   <tr>
    <td> 
     <!--- Hidden Form Fields --->
     <input type="hidden" name="country" value="#country#">
     <input type="hidden" name="state" value="#state#">
     <input type="hidden" name="city" value="#city#">
     <input type="hidden" name="carrier_id" value="#carrier_id#">
     <input type="Hidden" name="allCensusNums" value="#form.allCensusNums#">
     <input type="submit" name="doSave" value="Save Changes" />  
    </td>
  </tr>   
 </cfoutput>
</table>
</cfform>

Open in new window

0
 
MFredinAuthor Commented:
Wow, gdemara, you went way above and beyond on this one.  THANK YOU SO MUCH!  I wish I could award you 10,000 points for this.  Your solution works perfectly!
0
 
gdemariaCommented:
Super, glad to hear it !
0

Featured Post

[Webinar] Kill tickets & tabs using PowerShell

Are you tired of cycling through the same browser tabs everyday to close the same repetitive tickets? In this webinar JumpCloud will show how you can leverage RESTful APIs to build your own PowerShell modules to kill tickets & tabs using the PowerShell command Invoke-RestMethod.

  • 9
  • 8
  • 5
Tackle projects and never again get stuck behind a technical roadblock.
Join Now