11.2. Adding Autocomplete to a ControlWeb applications are becoming more and more like desktop applications, and the use of Ajax technologies has fueled this trend. One feature that desktop applications have, but web sites usually don't, is the autocomplete feature: whenever you enter something in a text box, the application looks up data suitable for the field (for instance, within most browsers, a list of previously entered data in similar fields) and offers to autofill the field for you. One of the first well-known web applications to support such a feature is Google Suggest (http://www.google.com/webhp?complete=1&hl=en). Whenever you start typing in the text field, the web page not only suggests popular search terms, but also shows approximately how many results this search may turn up, as shown in Figure 11-3. Of course, by now you know how this is done: an XMLHttpRequest is sent to a web service, which returns search terms and the estimated number of results. Figure 11-3. Google SuggestAtlas provides a control extender called AutoCompleteExtender that serves just this purposeit looks up data in the background and then suggests this data for a form element. One of the issues in implementing this is coding the CSS and JavaScript necessary to display the suggestions, make 225hem keyboard-navigable, and so on. With Atlas, this work has already been done, and you just have to apply this feature. Note, though, that some of the more tricky bits of Google Suggest (including the keyboard navigation) are not fully implemented in the Atlas extender. From a web control point of view, the only element for which autocompletion makes sense is TextBox. So, here is the element: <asp:TextBox runat="server"></asp:TextBox> Then, the Atlas control must be included: AutoCompleteExtender. Within this element, the AutoCompleteProperties element is used to configure the autocompletion effect. The following element attributes are supported:
Here is the appropriate markup for this example: <atlas:AutoCompleteExtender runat="server"> <atlas:AutoCompleteProperties Enabled="true" ServicePath="Vendors.asmx" ServiceMethod="GetVendors" TargetControl /> </atlas:AutoCompleteExtender> Example 11-2 shows how to create an ASP.NET page with a text box that supports autocompletion. Example 11-2. Adding autocompletion to a text box
Finally, you need to implement the web service that retrieves the data. To do so, you must be aware of the method signature of the method to call. Here is the signature: public string[] <MethodName>(string PrefixText, int count) So the method gets two parameters with rather obvious meanings:
The return data must be an array of string, so unfortunately, you cannot use a dataset or something similar here.
Figure 11-4. Tools such as Live HTTP headers reveal the signatureIn the example web service that we will use for the autocompletion data, the AdventureWorks database is queried; to be exact, the company names of all vendors are returned. As usual, you may have to adapt the connection string to your local systemin the code, we assume that the SQL Server 2005 Express Edition is available using Windows authentication at (local)\SQLEXPRESS. To begin, the web service code checks the search term. For the sake of simplicity, only letters from a to z (both upper- and lowercase) are allowed. This data check is mandatory to avoid SQL injection, because the code must execute a search query with LIKE. As an alternative, you could use a parametrized query.
If you use a regular expression to validate input data, consider allowing foreign-language characters such as German umlauted letters or French accented letters. The characters you do not want to accept, because they are "dangerous" from a security point of view, are single quotes, double quotes, square brackets, underscore characters, double hyphens, semicolons, and percent characters. These characters (and their encoded versions) all have a special meaning within the query. That's why it's better to validate user input using a whitelist approach (allow a predefined set of valid input) rather than a blacklist approach (disallow a predefined set of invalid input). The web service also checks the count parameter provided to the method to make sure it is a positive number and not greater than 100, so as not to provide an easy way to launch denial-of-service (DoS) attacks. This is what the web service code looks like that performs these validations: using System.Text.RegularExpressions; ... [WebMethod] public string[] GetVendors(string PrefixText, int count) { Regex regex = new Regex("^[a-zA-Z ]*$"); if (!regex.IsMatch(PrefixText) || count < 1 || count > 100) { return null; } After the data is validated, the SQL query is dynamically assembled and sent to the database. A typical query would look like this: SELECT TOP 10 Name FROM Purchasing.Vendor WHERE NAME LIKE 'Int%' This assumes that count has the value 10 (which is, coincidentally, the value Atlas sends by default) and the user typed Int into the text field. Here is the complete code for the database query, including filling the results into a dataset: SqlConnection conn = new SqlConnection( "server=(local)\\SQLEXPRESS; Integrated Security=true; Initial Catalog=AdventureWorks"); conn.Open(); SqlCommand comm = new SqlCommand( "SELECT TOP " + count + " Name FROM Purchasing.Vendor WHERE Name LIKE '" + PrefixText + "%'", conn); SqlDataAdapter adap = new SqlDataAdapter(comm); DataSet ds = new DataSet(); adap.Fill(ds); Then the data must be transformed into a string array. This array must not contain more than count elements (a call to Math.Min() will ensure that only count elements are returned if the database contains more elements). This can easily be achieved using a for loop: string[] vendors = new string[Math.Min(count, ds.Tables[0].Rows.Count)]; for (int i = 0; i < Math.Min(count, ds.Tables[0].Rows.Count); i++) { vendors[i] = ds.Tables[0].Rows[i].ItemArray[0].ToString(); } return vendors; } Example 11-3 shows the complete code for implementing this web service. Example 11-3. A web service that retrieves possible matches
And now it is time to try this in the browser. Load the page and enter a few letters, at least threewith two or fewer letters, Atlas does not issue a web service call. If some matches are found, they are displayed with little delay in the text box.
If you do not get any results, try this: there are several companies whose name begins with the word "International", so entering that word should get you a rewarding number of matches. Figure 11-5 shows you some typical results. Figure 11-5. Atlas is suggesting vendor names
Note that the URL of the web service providing the autocompletion data is put in a property called serviceURL, whereas the AutoCompleteExtender control used a property called ServicePath. Generally, using the extender is more intuitive andsince you have IntelliSense support in Visual Studio/Visual Web Developerless error-prone. |