Filtering a cfgrid as you type -revisited-
Some time ago I made a post where I showed how to filter a cfgrid by typing in a input field. I received a lot of comments requesting to do the same but using an editable cfgrid. The result in this code:
<cfsavecontent variable="actionInsert">
GridData.insertRow(myGrid);
_global.backupDP.push(myGrid.getItemAt(myGrid.length -1));
</cfsavecontent>
<cfsavecontent variable="actionDelete">
var item = myGrid.selectedItem;
if(_global.backupDP != undefined)
{
for(var i = 0; i < _global.backupDP.length; i++)
{
if(_global.backupDP[i] == item)
{
_global.backupDP.removeItemAt(i);
}
}
}
GridData.deleteRow(myGrid);
</cfsavecontent>
<cfsavecontent variable="actionFilter">
if(_global.backupDP == undefined)
{
_global.backupDP = myGrid.dataProvider.slice(0);
}
var backupDP = _global.backupDP;
var fortext = forInput.text.toLowerCase();
var selected = column.selectedItem.data;
myGrid.dataProvider.removeAll();
for(var i =0; i < backupDP.length;i++)
{
var item = backupDP[i];
if(item[selected].toString().substr(0,fortext.length).toLowerCase() == fortext)
{
myGrid.dataProvider.addItem(item);
}
}
</cfsavecontent>
<cfform name="myForm" format="flash" width="400" height="370">
<cfformgroup type="panel" label="Search our Members">
<cfformgroup type="horizontal">
<cfinput type="text" name="forInput" width="120" onchange="#actionFilter#" label="Filter by:">
<cfselect name="column" label="in:" onchange="forInput.text=''" width="90">
<option value="name">Name</option>
<option value="gender">Gender</option>
<option value="age">Age</option>
</cfselect>
</cfformgroup>
<cfgrid name= "myGrid" query="memberList" height="200" selectmode="edit" >
<cfgridcolumn name="name" header="Name">
<cfgridcolumn name="gender" header="Gender">
<cfgridcolumn name="age" header="Age">
</cfgrid>
</cfformgroup>
<cfformgroup type="horizontal">
<cfinput type="submit" name="submit" value="Submit"/>
<cfinput type="button" name="inser" value="Insert" onclick="#actionInsert#"/>
<cfinput type="button" name="remove" value="Delete" onclick="#actionDelete# "/>
</cfformgroup>
</cfform>
Live Example
Download the source
Related Posts
Filtering a grid as you type in a cfform
Filtering a list as you type in a ColFusion Flash Form
Steven Ross
if(item[selected].substr(0,fortext.length).toLowerCase() == fortext)
if you replace it with this it works:
if(_global.arrMembers == undefined) _global.arrMembers = FileGrid.dataProvider.slice(0);
var arrMembers = _global.arrMembers;
var arrDisplay:Array = [];
var fortext = forInput.text;
var selected = column.selectedItem.data;
for(var i = 0; i < arrMembers.length; i++)
{
if(selected == 'NumberColumn')
{
if(arrMembers[i][selected].toString().indexOf(fortext) != -1)
{
arrDisplay.push(arrMembers[i]);
}
}
else{
if(arrMembers[i][selected].toLowerCase().indexOf(fortext.toLowerCase()) != -1)
{
arrDisplay.push(arrMembers[i]);
}
}
}
FileGrid.dataProvider = arrDisplay;
Felipe
Steven Ross
if(item[selected].substr(0,fortext.length).toLowerCase() == fortext)
to:
if(arrMembers[i][selected].toLowerCase().indexOf(fortext) != -1)
John Farrar
CAST([year] AS VARCHAR) as years
That fixed the issue I was having and should work all around if you need the functionality.
dMan
Troy Montour
now I need to pass back what customer they clicked on so I can pass this back to the Database to find this customers Orders.
Is there a way to do this?
Michael White
mafdoc
I tried to adjust your code with the original code but when I enter a number to be filtered all my data disappears. Although, I can still filter on the other columns (which are listed in the "in" select list) The adjusted code:
<cfsavecontent variable="actionFilter">
if(_global.arrMembers == undefined) _global.arrMembers = data.dataProvider.slice(0);
var arrMembers = _global.arrMembers;
var arrDisplay:Array = [];
var fortext = forInput.text.toLowerCase();
var selected = column.selectedItem.data;
for(var i = 0; i < arrMembers.length; i++)
{
if(selected == 'NumberColumn')
{
if(arrMembers[i][selected].toString().indexOf(fortext)!=-1)
{arrDisplay.push(arrMembers[i]);
}
}
else
{
if(arrMembers[i][selected].substr(0,fortext.length).toLowerCase() == fortext)
{
arrDisplay.push(arrMembers[i]);
}
}
}
data.dataProvider = arrDisplay;
</cfsavecontent>
I'll keep trying but I would appreciate any help.
mafdoc
added .toString() between the ...[selected] and .substr...
Thanks so much.
Gary Rennilson
Laura
perhaps you have the same problem described by mafdoc (when data contained numeric fields). I updated the source to contain the change.
Otherwise it should work as is.
Michael White
Gary Rennilson
John
I am talking about using tab navigator in cfform with format flash.
Lets say what show up in tab2 depends on what you select in tab 1.
So I need to refesh what tab2 is showing when someone click on tab2 based on what record the user choose in tab1.
Is this possible?
Basicaly whenever someone clicks on tab2 I want to refresh the cfgrid with new data without refreshing the entire page.
What I wish I could do is put a cfquery inside of the second cfformgroup (the one named tab2). Have that run each time some one click on tab 2.
The other thing I have recently heard about about is CF.Query() function.
Can I do this on tab change inside of my coldfusion file. If so can you send me an example of how to do this.
If I don't hear from you I am going to use this filtering code to get all the data when the page first loads and then filter what I show based on what was chosen in tab1. The problem with this is that as amont of data get huge this will become impossible.
Thanks for any help you can give me.
John
Nahuel
You have different options. You can do that with remoting and ask for a small amount of data each time like in this example:
http://www.asfusion.com/blog/entry/populating-a-cfgrid-with-flash-remoting
Or you can download everything to the client like in this example:
http://www.asfusion.com/blog/entry/quick-grid-bindings
John
1. Is there any event that get triggered when a new tab is selected.
2. Can I change the tab selected manually.
3. Is there a way to hide or disable certain tabs depending on choices the user make outside of the tabnavigator.
Thanks in advance
Kourosh
Nahuel
1. onchange
2. myTab.selectedIndex = 0; // 1,2,3,4
3. try with bindings, or use the visible property
John
I ended up having to use enabled = false
for hiding the tabs.
the visible property doesn't do anything for individual tabs. Even when you hard code set at the very start it still shows it.
Like this:
<cfformgroup label="Documents" type="page" visible="no">
mafdoc
Now I want to get more data from another datasource and table.
I can not get query of query to work. This cfoutput shows all the data I need.
Can someone help me put this into a cfquery so I can use that query in the filter?
<cfquery name="getArtists" datasource="docpers">
SELECT UserName, FirstName, LastName, WorkPhone, Division, Status
FROM tblmaster
WHERE status <> 'I'
ORDER BY UserName
</cfquery>
<cfoutput query="getArtists">
<cfquery name="getsiteinfo" datasource="Site">
SELECT *
FROM site
Where username = '#getArtists.UserName#'
</cfquery>
#getArtists.UserName#, #getArtists.FirstName#, #getArtists.LastName#, #getArtists.Division#, #getsiteinfo.site#, #getsiteinfo.location#<<br>
</cfoutput>
Thanks for any help.
mafdoc
"Element USERNAME is undefined in TBLMASTER."
<cfquery name="getArtists" datasource="docpers">
SELECT UserName, FirstName, LastName, WorkPhone, Division, Status
FROM tblmaster,
Site.site
WHERE status <> 'I'
AND username = '#tblintranetmaster.UserName#'
ORDER BY UserName
</cfquery>
David
Laura
Of course there is! All the examples with cfform use the Flex engine. In fact, it should be pretty straight forward to do it, although you will need to get the data manually (either with remoting, xml, web service, etc)
david
David
David
{
var filteredRecords = new Array();
var filterText:String = myFilter;
for(var i = 0; i < RemoteObjectName.ModelName.result.length; i++ )
{
var curRecord = RemoteObjectName.ModelName.result[i];
var name = RemoteObjectName.ModelName.result[i].DataGridColumnName;
if(name.substring(0,filterText.length).toLowerCase() == filterText.toLowerCase())
{
filteredRecords.push(curRecord);
}
}
DataGridName.dataProvider = filteredRecords;
}
Mike
Thanks
Laura
I never use cfgridupdate so I can't really help you with that. The code of the post should work with cfgridupdate if you submit the form though, but I have not tested it. Flash remoting is a different animal. cfgridupdate requires a form post, and you do not post a form post when you use remoting.
Brad Wood
Specifically this line right here:
_global.backupDP = myGrid.dataProvider.slice(0);
doesn't work when the dataProvider was populated with remoting.
Why does life hate me?
Pleas tell me there is another way around this. If I get rid of the slice(0) it copies the list over but it copies by REFERENCE not by VALUE. I can't find anywhere a source that tells me what syntax would force AS to make a backup of the dataProvider by duplicating the list in memory.
Michael White
Joseph
This is some great stuff. I'm using this to try to put together a three-level filtering system. There are three cfgrids. Selecting an item in the first one filters the second cfgrid, selecting an item in the second cfgrid filters the third cfgrid.
I was able to modify an 'actionFilter' and an 'actionFilter2' with little problem. Change all the appropriate cfgrid references, and use _global.backupDP2 instead of _global.backupDP.
In addition, however, there are SO MANY items in the third cfgrid, and they don't really make sense unless you've selected something from the second cfgrid, that it really needs to be hidden until something is chosen in actionFilter2.
My solution (I thought) was simple. Give the following propety to the cfformgroup that holds the third cfgrid.
visible="{((_global.myShowbox)?true:false)}"
In addition, make a cfsavecontent called 'actionLoad' which loads upon onLoad which looks like this:
_global.myShowbox = 0;
In actionFilter, which is called when clicking on the first cfgrid, add the same line before any of the rest of the code. In actionFilter2, which is called when clicking on the second cfgrid, use this code before anything else:
_global.myShowbox = 1;
Theoretically, this would make a variable that would toggle the visibility of the third cfgrid and its assorted controls. It would be invisible when first loading, or when selecting something from the first cfgrid, and it would become visible when making a selection from the second cfgrid.
It didn't work.
Not only does it not toggle visibility states, but it also stops ActionFilter from working at all. ActionFilter now removes everything from the second cfgrid, but does not repopulate it with any data.
What did I do wrong?
Thank you in advance for your help. Your site is one of my favorite coldfusion sites, right after the livedocs themselves.
The ScareCrow
Specifically this line right here:
_global.backupDP = myGrid.dataProvider.slice(0);
I also ran into this. You could actually get the length of the data provider, but it would not assing to the global var.
This is how I fixed it.
In the response handler onResult
//create array to hold results
var myDP:Array = [];
//loop through result set
for(var x=0; x < results.length; x++){
//add items to the array in the form
column name:column value
myDP.addItem({fld_Column1:results.getItemAt(x).fld_Column1, fld_Column2:results.getItemAt(x).fld_Column2});
}
//assign the array to the grid data provider.
myGrid.dataProvider = myDP;
The filter then works when the grid is populated by remoting
Ken
Laura
Actually, the simplest way to make it work with remoting is doing:
myGrid.dataProvider = results.items;
instead of simply:
myGrid.dataProvider = results;
This avoids having to loop over the data which might be slow for large sets.
The problem is that slice() is for arrays, not recordsets and the variables "results" holds a recordset, not an array when the component returns a query.
ramzi Chekkath
i like the look in function but i want one of the two
1 - either one input box and it searches in any given colum name (with out the user changing it in a list)
2 - or multiple input boxes each one filtering a certain colum
i personally preffer the second option but to start with first option is fine.
eg. second option.
insert 1 = ra
insert 2 = c
so it returns to me
c1 c2
--- ---
ramzi chekkath
rasberry cherry
Thomary
<cfgridcolumn name = "FirstName" textColor = "(CX EQ Peter ? blue : black)">
I can not get this to work. Has anyone tried this? I would like combine two grids if possible but I really need to identify which listing is vendor and which are employees. I thought this would be very useful. Thanks for all your help.
candlelight
As I am quite new to actionscript, what is
"RemoteObjectName.ModelName"?
Anyone has some great example(s) of how enable filtering with grid populated with flash remoting?
Stucked..
Thanks
Laura
See my reply to ScareCrow 3 comments above. That's all what you need to make it work with remoting. Use the code explained at http://www.asfusion.com/blog/entry/populating-a-cfgrid-with-flash-remoting
Gary
------
I've tried this too and have been unable to get it to work. I get no errors, but I also get no output, just a blank page.
Very frustrating ...
Neill
Joe Gutierrez
// this function removes records from the grid based on the input fields
function applyFilter():Void {
var grid:mx.controls.DataGrid = myGrid;
var filterTerm:Array = [];
var columns: Array = [];
var value:Array = [];
// array of columns that correspond to datagrid col names
columns[0] = 'lastname';
columns[1] = 'firstname';
columns[2] = 'city';
columns[3] = 'state';
columns[4] = 'phone';
// array of values from input fields where to get filter terms
filterTerm[0] = frmClient.lastname.toString().toLowerCase();
filterTerm[1] = frmClient.firstname.toString().toLowerCase();
filterTerm[2] = frmClient.city.toString().toLowerCase();
filterTerm[3] = frmClient.state.toString().toLowerCase();
filterTerm[4] = frmClient.phone.toString().toLowerCase();
if(filterTerm.length > 0) {
if(_global.unfilteredData[grid.id] == undefined){
if (_global.unfilteredData == undefined){
_global.unfilteredData = {};
}
_global.unfilteredData[grid.id] = grid.dataProvider.slice(0);
}
var filteredData:Array = [];
for(var i = 0; i< _global.unfilteredData[grid.id].length; i++) {
var item:Object = _global.unfilteredData[grid.id][i];
var added:Boolean = false;
var pushit:Boolean = true;
for (var x = 0; x < columns.length; x++) {
value[x] = item[columns[x]].toString().toLowerCase();
if (filterTerm[x] != '' && filterTerm[x] != undefined)
{
if (value[x].substr(0,filterTerm[x].length).toLowerCase() != filterTerm[x])
{
pushit = false;
}
}
}
if ( pushit == true){
filteredData.push(item);
added = true;
}
}
grid.dataProvider = filteredData;
}
else {
if(_global.unfilteredData[grid.id] != undefined) grid.dataProvider = _global.unfilteredData[grid.id];
}
}
Andy W.
I'm wondering if it has something to do with pass by value/reference situation.
Here is the code that I used for filtering the grid.
function applyFilter(grid:mx.controls.DataGrid):Void{
var arrDisplay:Array = [];
var fortext = name.text.toLowerCase();
var isApplicable:Boolean = true;
if(_global.arrMembers == undefined) _global.arrMembers = userGrd.dataProvider.slice(0);
var arrMembers = _global.arrMembers;
for(var i = 0; i < arrMembers.length; i++){
//Filter on the text
if (fortext.length > 0){
isApplicable = filterText(fortext,i,arrMembers[i].first_nm.toString(),arrMembers[i].last_nm.toString(),arrMembers[i].org_nm.toString(),arrMembers[i].user_nm.toString());
}
else{
isApplicable = true;
}
if (isApplicable == true){
arrDisplay.push(arrMembers[i]);
isApplicable = true;
}
}
userGrd.dataProvider = arrDisplay;
}
///filter for text
function filterText(fortext:String, i:Number, first:String, last:String,org:String, user:String):Boolean{
var isApplicable:Boolean;
if ((first.substr(0,fortext.length).toLowerCase() == fortext || last.substr(0,fortext.length).toLowerCase() == fortext || org.substr(0,fortext.length).toLowerCase() == fortext|| user.substr(0,fortext.length).toLowerCase() == fortext) ){
isApplicable = true;
}
return isApplicable;
}
Andy W.
I'm wondering if it has something to do with pass by value/reference situation.
Here is the code that I used for filtering the grid.
function applyFilter(grid:mx.controls.DataGrid):Void{
var arrDisplay:Array = [];
var fortext = name.text.toLowerCase();
var isApplicable:Boolean = true;
if(_global.arrMembers == undefined) _global.arrMembers = userGrd.dataProvider.slice(0);
var arrMembers = _global.arrMembers;
for(var i = 0; i < arrMembers.length; i++){
//Filter on the text
if (fortext.length > 0){
isApplicable = filterText(fortext,i,arrMembers[i].first_nm.toString(),arrMembers[i].last_nm.toString(),arrMembers[i].org_nm.toString(),arrMembers[i].user_nm.toString());
}
else{
isApplicable = true;
}
if (isApplicable == true){
arrDisplay.push(arrMembers[i]);
isApplicable = true;
}
}
userGrd.dataProvider = arrDisplay;
}
///filter for text
function filterText(fortext:String, i:Number, first:String, last:String,org:String, user:String):Boolean{
var isApplicable:Boolean;
if ((first.substr(0,fortext.length).toLowerCase() == fortext || last.substr(0,fortext.length).toLowerCase() == fortext || org.substr(0,fortext.length).toLowerCase() == fortext|| user.substr(0,fortext.length).toLowerCase() == fortext) ){
isApplicable = true;
}
return isApplicable;
}
Mark Cadle
This is only once you have filtered the grid. If you do not filter the grid then it will work. So at what point does this rename the grid? How can you keep the same grid name?
I update the grid via a cfinput that is bound to the selectedIndex of the grid and then have a button with onclick to:
gridname.dataProvider.editField(gridname.selectedIndex, 'COLUMN', textname.text);
This works great and the cfgridupdate works great even when you filter the grid, but once you filter the grid, you can not run the cfgridupdate.
I hope this makes sense.
Joel Watson
Currently, I can insert, update and delete when no filter is applied no problem (not an ideal solution), but when I try to filter, the functionality disappears and the submission sends a blank entry.
Any ideas on this? I have read through the comments here and I cannot find anything that solves my dilemma. I really appreciate the help!
Joel
Dysfunction erectile pills
Sexual Stimulant
good luck all
Ryan - Smurf
file: filter.cfm
<cfsilent>
<cfsavecontent variable="onLoad">
_global.loaded = true;
<cfoutput>
<!---create connection--->
var connection:mx.remoting.Connection = mx.remoting.NetServices.createGatewayConnection("http://#cgi.HTTP_HOST#/flashservices/gateway");
<!---declare service--->
var netService:mx.remoting.NetServiceProxy;
<!---declare component--->
var epmComp = "xEPM.authenticated.components.empCFC";
</cfoutput>
<!--- includes actionscript file for form --->
#include "actionscript/as_dataManagement.as"
</cfsavecontent>
</cfsilent>
<cfform format="flash" name="empForm" wmode="transparent" onload="#onLoad#" style="#formStyle#" preservedata="yes" >
<cfformgroup type="horizontal" style="marginTop:0; horizontalGap:15;marginRight:15;horizontalAlign:right">
<cfinput type="text" name="filterProjectInput" size="12" label="Filter: " onChange="_global.findProject({})" enabled="{gridCustomer.selectedItem != undefined}"/>
</cfformgroup>
</cfform>
file: as_dataManagement.as
_global.findProject = function():Void {
//fields are passed to component as a variable
var Parameter = {};
Parameter.filterText = _root.filterProjectInput.text.toLowerCase();
mx.managers.CursorManager.setBusyCursor();
Parameter.onStatus = function( stat: Object ):Void {
//if there is any error, show an alert
alert("Error while calling cfc:" + stat.description);
}
//get service
netService = connection.getService(epmComp, Parameter);
//find data with remoting
netService.findProject(Parameter);
//put the controls in scope to avoid calling _root
var gridProject = _root.gridProject;
Parameter.onResult = function( results: Object ):Void {
//when results are back, populate the cfgrid
gridProject.dataProvider = results;
mx.managers.CursorManager.removeAllCursors();
}
}
file: epmCFC.cfc
<cffunction access="remote" name="findProject" output="false" returntype="Any">
<cfargument name="filterText" type="Any" required="yes">
<cfquery name="findProject" datasource="#session.datasource#">
SELECT *
FROM PROJECTS
WHERE projectName LIKE <cfqueryparam cfsqltype="cf_sql_varchar" value="%#lcase(arguments.filterText)#%">
</cfquery>
<cfreturn findProject/>
</cffunction>
Michael White
Mark Cadle
Nahuel
Kevin Hunkovic
V Fusion
"Currently, I can insert, update and delete when no filter is applied no problem (not an ideal solution), but when I try to filter, the functionality disappears and the submission sends a blank entry."
I'm having the same problem. Does anyone know/have a fix? Thanks!
Joe
Mark Cadle
Joe, the answer is yes! There are multiple ways of doing this. If you are wanting to do this as you type and only look for the characters you type in a specific point in the string, you merely need to modify the substring section where I have all caps, you would use 0 to start at the beginning or the number for your start point and then how many characters to look at. So to basically do w%rd it would be 1,1.
CODE:
if(item[selected].toString().substr(START_POINT_INTEGER,NUMBER_OF_CHARACTERS_TO_SEARCH_INTEGER).toLowerCase() == fortext)
If you are wanting to search for a predefined list of characters in a predefined location, you would still use substr but would need to do it in a different function. Same goes for other variations but you can use search or match function.
Lastly, if you are not doing this in actionscript you could use the ColdFusion function REFind which leverages Regular Expressions to match the pattern or just Find or do it in your query.
Hope this helps.
V Fusion
<cfsavecontent variable="submitIfClear">
//forInput is the name of a textbox
// deleteEntry and save are button names
// everything is cAsE sensitive
if(forInput.text != '')
{
deleteEntry.enabled = false;
save.enabled = false;
}
else
{
deleteEntry.enabled = true;
save.enabled = true;
}
</cfsavecontent>
V Fusion
<cfinput type="text" name="forInput"
width="120" label="Filter by:"
onchange="#actionFilter#; #submitIfClear#" >
Mike
and I am having some success. Basically I have a grid
with a cfselect drop-down menu that has columns from my table that is connected to the grid. I use this drop down to search by these columns. When I initially execute a search in the datagrid and then I apply my filter on the dataset search results the filter works great using 'results.items' as stated in above posts in the response handler. The problem is that when I execute another search and then apply the filter to the next dataset results the filter fails. Instead of the filter working on the newly returned dataset that came back to the datagrid from my subsequent executed search query the filter is still filtering the original dataset results from the 'first' time the search was executed. And all the data is being returned from a cfc through flash remoting on every search executed. So the filter works great on the first search run dataset but then after I get a new search dataset result it seems like the dataprovider or recordset object is not getting updated with the new dataset thus the filter is still only working on the original dataset result. Anyone run into this problem or have any ideas?
Mike
Thanks,
Mike