March 2009 - Posts
In part 2 of my local search series, I briefly went over how the JSON data gets from the server to the client. In this post, I plan to detail how the data from the client gets displayed to the user. JavaScript has capabilities to dynamically render content within the browser, and we'll use those capabilities here. The method that does the work is shown below.
function displayResults(yahooResults, parent) {
while (parent.hasChildNodes())
parent.removeChild(parent.firstChild);
var resultSet = yahooResults.ResultSet;
for (var resultIndex in resultSet.Result) {
var result = resultSet.Result[resultIndex];
var newItem = document.createElement("SPAN");
newItem.innerHTML = result.Title + "<br/>" +
result.Address + "<br/>" +
result.City + "<br/>" +
result.State + "<br/><br/>";
parent.appendChild(newItem);
parent.appendChild(document.createElement("BR"));
}
}
When the web service results come back to the client, the results are passed to the yahooResults parameter. So the yahooLocalSearchResults variable that the YahooProvider defined in the return string (or whatever the actual name was) is passed direclty to this method. This object has a single property, ResultSet, which I pass to a variable reference. This object, now assigned to the resultSet variable, has the following properties:
- Result - an array of the actual results based on the number of results allowed.
- firstResultPosition - The index of the first result
- totalResultsReturned - The number of results returned with the call
- totalResultsAvailable - Total number of records found, but not returned to the client
- resultSetMapUrl - The URL to the map to display these results
Looping through each result gets to the core data, which has a title, address, city, state, latitude, longitude, map URL, business information, destination information, and much more available from the web service. For simplicity, I bring back name and address information, separated by new lines.clef
Every time a search occurs, the results are cleared at the top of the method; every DOM element has a hasChildNodes() method that checks for children. The parent is checked for children in this case, and cleared if there is any. So the search results start afresh every time. The screen can be viewed here: http://www.flickr.com/photos/36885451@N06/3394395353/
The next step is to make it more dynamic, and make it look better.
In an ASPX page, I setup a simple form, as shown below. The form simply grabs a category/zip, and submits them to the server. But no postback occurs; rather, this kicks off a web service, which will eventually query Yahoo.
<span id="searchform">
Category:
<asp:TextBox ID="txtCategory" runat="server"></asp:TextBox>
 :
Zip Code:
<asp:TextBox ID="txtZipCode" runat="server"></asp:TextBox>
<asp:Button ID="btnSubmit" runat="server" UseSubmitBehavior="false" OnClientClick="searchClick(this);return false;"
Text="Search" />
<br />
<hr />
<br />
<span id="searchresults"></span>
</span>
The button's searchClick method fires. This proceeds to contact the server, and expects results from the Yahoo local search provider. It doesn't do much in regards to validation; I didn't attempt to set up validation yet.
function searchClick(sender) {
var category = $get("<%= txtCategory.ClientID %>").value;
var zipCode = $get("<%= txtZipCode.ClientID %>").value;
if (!isNaN(zipCode))
zipCode = Number.parseInvariant(zipCode);
else
zipCode = null;
PageMethods.GetYahooJSONForClient(category, zipCode, localSearchSucceeded, localSearchFailed, $get("searchresults"));
}
The PageMethods.GetYahooJSONForClient method is a method defined a static method in the page class, rather than going the full web service route.
public partial class yahoolocal : System.Web.UI.Page
{
[System.Web.Services.WebMethod]
public static string GetYahooJSONForClient(string category, int zipCode)
{
if (string.IsNullOrEmpty(category))
category = "pizza";
if (zipCode <= 0 || zipCode.ToString().Length != 5)
zipCode = 15239;
YahooLocalSearchProvider provider = new YahooLocalSearchProvider();
string json = provider.GetYahooJSONFromService(category, zipCode);
return json;
}
}
The web service does basic validation too, and simply supplies defaults if the parameters are incorrect. Otherwise, it calls my custom YahooLocalSearchProvider object. This is an object I built, and is not a proxy. The value returned is a JSON string, which the JSON string gets passed back to the client-side.
The YahooLocalSearchProvider uses the Sys.Net namespace to get a HTTP request and response, as outlined in part 1 of this series. Because of that, I won't go into detail; here is the provider as a whole.
public class YahooLocalSearchProvider
{
public string GetYahooJSONFromService(string category, int zipCode)
{
string url = "http://local.yahooapis.com/LocalSearchService/V3/localSearch?" +
string.Format("appid=YahooDemo&query={0}&zip={1}&results=10&output=json",
category, zipCode);
WebRequest request = HttpWebRequest.Create(url);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream());
return "var yahooLocalSearchResults = " + reader.ReadToEnd();
}
}
Notice that the returned value sets up a search results variable. When the eval() statement on the client-side runs, it will create a variable that contains all of the Yahoo information. Notice how the URL is constructed here too. All of the parameters get passed values. THis is a simplified version of the query too, there are many more options available to the consumer.
In the next post, we'll look at how the JS contents get rendered to the client.
Today I'm beginning a series of posts on using the Yahoo local search service and embedding it within an ASPX page. The Yahoo local search service finds businesses within a local community based on city/state or zip code lookups. This service also takes a range of other parameters to specify the range to use, the result set limits, the type of response (JSON, XML), and so on. More information can be found here: http://developer.yahoo.com/search/local/V3/localSearch.html.
Yahoo, as most other REST and web service API's, require the use of an AppID that specifies the application querying it. This is a common way to track usage. However, Yahoo uses the YahooDemo ID for demo purposes, and that's what we'll do here. Not only will I illustrate how to query and get the data, but I'll try to show a few alternatives, plus I'll display it in a way that's more "eye candy" so to speak.
For REST calls, the approach we can take is to use the WebRequest and WebResponse objects to process the HTTP request and response. Posting and retrieving requests can be done using:
WebRequest request = HttpWebRequest.Create(url);
WebResponse response = request.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream());
return reader.ReadToEnd();
Using the specified REST url, the downloaded response is retrieved using a HttpWebResponse, along with the StreamReader that extracts the information from the response stream. And thus, the content (which this series will use JSON) gets extracted as a string via the ReadToEnd() method.
How the JSON gets to the client for JavaScript to process doesn't matter at the moment; at this moment, we're focused solely on the process reading the JSON data. The URL we'll extract the data from is:
http://local.yahooapis.com/LocalSearchService/V3/localSearch?appid=YahooDemo&query=barber&zip=15239&results=10&output=json
And from this, we'll begin to look at how we're going to process the data.
The .NET charting control is cool, but I've run into a variety of unique situations, and because of the newness of the control and lack of documentation, there wasn't a really good answer. The Y Axis labels became slanted for one of my bar charts. The issue was my InnerPlotPosition was too big for the chart, something like:
<InnerPlotPosition X="5" Y="5" Height="90" Width="90" />
If you don't know where this is defined, this property is a child of the ChartArea object that defines the region of the chart, where the chart will live. Because of this specific chart and the way the legend was rendering, this was a little too wide, forcing my labels to be crooked. The solution was to increase this to a bigger size of X/Y to 10 and Height/Width to 85. By making it bigger, it straightened out the Y axis labels.
I thought this may be a setting, because you can control the X and Y axis labels in the AxisX and AxisY settings, through the LabelStyle.Angle property (LabelStyle exists underneath the AxisX and AxisY objects, which sit underneath the ChartArea.