PDA

View Full Version : How to filter a remote data ListView with typeahead?



niblettes
24 Nov 2010, 1:27 PM
I have a ListView displaying remotely hosted data. I want to provide a textfield that allows users to type in a search string (like a first or last name) which will automatically filter the ListView's contents. The behaviour should be similar to a combobox autocomplete, but would act on a ListView.

Does anyone know of any tutorials to help me learn how to accomplish this?

(this is a pretty common behaviour -- even the API documentation does it with its contents tree -- so I'm a little surprised at how hard it has been to find any good information on this topic).

(Here is a similar example (http://www.sk-typo3.de/index.php?id=345) on a Grid, but it requires the user to hit "return" before filtering -- so its not quite what I'm after)

Animal
24 Nov 2010, 10:10 PM
Well, it's not that difficult really.

Use an input field with a keyup listener.

In the handler, filter the Store on the field's value.

That's it.

niblettes
26 Nov 2010, 12:58 PM
Animal,

That doesn't quite do it -- at least not the way I've coded it:



listeners : {
keyup : function(field){
field_value = field.getValue();
jsonStore.filter([
{
property : 'lastname',
value : field_value
}
]);
}
My remote db has 2000 records and the ListView displays 50 per page, so on. This code only filters the data currently displayed.

Is there something I need to do beyond merely passing a filter value to the store to get the code to filter the entire db according to the search term?

Animal
26 Nov 2010, 2:38 PM
You need to reload the Store based upon the current value of the field on keyup instead of filtering it.

Hint: Use the buffer option.

niblettes
30 Nov 2010, 12:06 PM
Thanks this definitely got me a couple steps closer -- but I'm still not quite there yet.

I'm finding the API documents, Google and my two books little help in better understanding the syntax for passing a user defined constraint to the Store.

I realize these are kind of annoying newby questions -- but I am kind of stumped by this simple matter and all these other resources I have don't seem to have any answers.

Here is the code I have right now for the search field that acts on my JSON store called jsonStore:



/* *************************** */
/* DEFINE FORM
/* *************************** */

var search_field = new Ext.form.TextField({
allowBlank : false,
enableKeyEvents : true,
listeners : {
keyup : {
fn: function(field){
field_value = field.getValue();
console.log(field_value);
jsonStore.reload({
params : { lastname: field_value }
});
}
}
}
});

// Form and field with listen for keyup events to trigger data filter
var person_search_form ={
xtype : 'form',
id : 'search_form',
header : false,
frame : false,
border : false,
items : search_field
};



the results, before and after a key click, are below:

2360423605

Animal
30 Nov 2010, 12:35 PM
And is that wrong? You pass the param, and it returns those results.

But use the buffer option. You don't want an Ajax request for EVERY key. You want one to fire off when they STOP typing.

niblettes
30 Nov 2010, 1:56 PM
Wow! that might be the fastest forum response I've ever gotten. Thanks.

To see what's wrong, notice I typed a "c" in the field. Well the reload params specify that the store should be filtered only by 'lastname'. In the results you can see in the screenshots only one record has a "c" in the lastname column. Indeed the third record contain no 'c' in any column. Furthermore, I know there are many other records beginning with 'c' in the db that are not being displayed.

Also if i change the 'lastname' param to 'firstname' or any other column the same 3 records are returned. And regardless of what value 'field_value' is, again these same 3 records are returned.

This all suggests that something is wrong, and I'm stumped as to what it might be.

Here is a how I've defined the grid -- just in case the problem lies outside the form code I displayed above:



/* *************************** */
/* DEFINE GRID
/* *************************** */

// to map the field names in the json object with the column names here
var recordFields = [
{ name : 'id', mapping : 'zip' },
{ name : 'firstname', mapping : 'firstname' },
{ name : 'lastname', mapping : 'lastname' },
{ name : 'city', mapping : 'city' },
{ name : 'occupation', mapping : 'department' },
{ name : 'lastAppt', mapping : 'datehired' },
{ name : 'dob', mapping : 'dob' }
];

// to get the json data
var remoteProxy = new Ext.data.ScriptTagProxy({
url : '<?php echo $data_url; ?>'
});

// to read and store the json data
var jsonStore = new Ext.data.JsonStore({
proxy : remoteProxy,
fields : recordFields,
totalProperty : 'totalCount',
root : 'records',
id : 'ourJsonStore',
autoLoad : false,
remoteSort : true
});

// to define how each column looks and behaves
var columnModel = [{
header : 'Person ID',
dataIndex : 'id',
sortable : true,
},{
header : 'Last Name',
dataIndex : 'lastname',
sortable : true,
},{
header : 'First Name',
dataIndex : 'firstname',
sortable : true,
hideable : false
},{
header : 'Date of Birth',
dataIndex : 'dob',
sortable : true
},{
header : 'City',
dataIndex : 'city',
sortable : true
},{
header : 'Occupation',
dataIndex : 'occupation',
sortable : true
},{
header : 'Last Appointment',
dataIndex : 'lastAppt',
sortable : true
}];

// to define the paging toolbar
var pagingToolbar = {
xtype : 'paging',
store : jsonStore,
pageSize : 50,
displayInfo : true
};

// to assemble all the peices into a single grid
var grid = {
xtype : 'grid',
columns : columnModel,
store : jsonStore,
loadMask : true,
bbar : pagingToolbar,
frame : false,
border : false,
};

niblettes
6 Dec 2010, 2:47 PM
I'm still stumped by this.

Does anyone here have any ideas about what I'm doing wrong (ie, why the store isn't returning a list based on the textfield's value)??

Condor
7 Dec 2010, 1:02 AM
This isn't an Ext problem. It only does a server request with the typed search text as a parameter.

You'll have to look at your server code to see why it doesn't return the expected records based on this parameter.

ps. I see you are using a pagingtoolbar. Instead of reloading, you should store the search parameter in the store baseParams and load the store with params:{start:0,limit:50}.

niblettes
7 Dec 2010, 6:52 PM
Alright, that did it! Thanks so much.

I didn't have to change my JS or ExtJS code. But I did change my PHP code. If any readers are using something other than PHP then code may not help you but hopefully the comments will. Here's the code:



<?php
// CONFIG DB
include("../data_access/db_config_local.php");

// SETUP VARIABLES
// if start is undefined set it to 0
// if limit is undefined set it to 3
// if a search pattern has been entered then create the where clause, else don't
// use $sql_for_count to determine the total number of record before setting start and limit
$callback = $_REQUEST['callback'];
$search_pattern = $_REQUEST['search_pattern'];
$start = ($_REQUEST['start'] != '') ? $_REQUEST['start'] : 0;
$limit = ($_REQUEST['limit'] != '') ? $_REQUEST['limit'] : 3;
$sql_criteria = '';
if (isset($search_pattern) || $search_pattern !=''){
$sql_criteria = " where (lastname LIKE '".$search_pattern."%') OR (firstname LIKE '".$search_pattern."%')";
}
$sql_for_count = "SELECT * from ".$db_table_name.$sql_criteria;
$sql = $sql_for_count." LIMIT ".$start.", ".$limit;

// OPEN
$conn = mysql_connect($dbhost, $dbuser, $dbpass) or die ('Error connecting to mysql');
mysql_select_db($db_name, $conn);

// GET RECORDS
// RETURN JSON
// if a callback was specified then return JSON in a function as security work around
// otherwise just return JSON
$arr = array();
If (!$rs = mysql_query($sql)) {
Echo "{success:false}";
}else{
$rs_count = mysql_query($sql_for_count);
$results = mysql_num_rows($rs_count);
while($obj = mysql_fetch_object($rs)){
$arr[] = $obj;
}
$json = "{ \"totalCount\":\"".$results."\",\"records\":".json_encode($arr)."}";
if ($callback){
header('Content-Type: text/javascript');
echo $callback . '(' . $json . ');';
} else {
header('Content-Type: application/x-json');
echo $json;
}
}

// CLOSE
mysql_close($conn);
?>