Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

How-To Tutorials - Full-Stack Web Development

52 Articles
article-image-data-manipulation-silverlight-4-data-grid
Packt
26 Apr 2010
9 min read
Save for later

Data Manipulation in Silverlight 4 Data Grid

Packt
26 Apr 2010
9 min read
Displaying data in a customized DataGrid Displaying data is probably the most straightforward task we can ask the DataGrid to do for us. In this recipe, we'll create a collection of data and hand it over to the DataGrid for display. While the DataGrid may seem to have a rather fixed layout, there are many options available on this control that we can use to customize it. In this recipe, we'll focus on getting the data to show up in the DataGrid and customize it to our likings. Getting ready In this recipe, we'll start from an empty Silverlight application. The finished solution for this recipe can be found in the Chapter04/Datagrid_Displaying_Data_Completed folder in the code bundle that is available on the Packt website. How to do it... We'll create a collection of Book objects and display this collection in a DataGrid. However,we want to customize the DataGrid. More specifically, we want to make the DataGridfixed. In other words, we don't want the user to make any changes to the bound data or move the columns around. Also, we want to change the visual representation of the DataGrid by changing the background color of the rows. We also want the vertical column separators to be hidden and the horizontal ones to get a different color. Finally, we'll hook into the LoadingRow event, which will give us access to the values that are bound to a row and based on that value, the LoadingRow event will allow us to make changes to the visual appearance of the row. To create this DataGrid, you'll need to carry out the following steps: Start a new Silverlight solution called DatagridDisplayingData in Visual Studio. We'll start by creating the Book class. Add a new class to the Silverlight project in the solution and name this class as Book. Note that this class uses two enumerations—one for the Category and the other for the Language. These can be found in the sample code. The following is the code for the Book class: public class Book { public string Title { get; set; } public string Author { get; set; } public int PageCount { get; set; } public DateTime PurchaseDate { get; set; } public Category Category { get; set; } public string Publisher { get; set; } public Languages Language { get; set; } public string ImageName { get; set; } public bool AlreadyRead { get; set; } } In the code-behind of the generated MainPage.xaml file, we need to create a generic list of Book instances (List) and load data into this collection.This is shown in the following code: private List<Book> bookCollection; public MainPage() { InitializeComponent(); LoadBooks(); } private void LoadBooks() { bookCollection = new List<Book>(); Book b1 = new Book(); b1.Title = "Book AAA"; b1.Author = "Author AAA"; b1.Language = Languages.English; b1.PageCount = 350; b1.Publisher = "Publisher BBB"; b1.PurchaseDate = new DateTime(2009, 3, 10); b1.ImageName = "AAA.png"; b1.AlreadyRead = true; b1.Category = Category.Computing; bookCollection.Add(b1); ... } Next, we'll add a DataGrid to the MainPage.xaml file. For now, we won't add any extra properties on the DataGrid. It's advisable to add it to the page by dragging it from the toolbox, so that Visual Studio adds the correct references to the required assemblies in the project, as well as adds the namespace mapping in the XAML code. Remove the AutoGenerateColumns="False" for now so that we'll see all the properties of the Book class appear in the DataGrid. The following line of code shows a default DataGrid with its name set to BookDataGrid: <sdk:DataGrid x_Name="BookDataGrid"></sdk:DataGrid> Currently, no data is bound to the DataGrid. To make the DataGrid show the book collection, we set the ItemsSource property from the code-behind in the constructor. This is shown in the following code: public MainPage() { InitializeComponent(); LoadBooks(); BookDataGrid.ItemsSource = bookCollection; } Running the code now shows a default DataGrid that generates a column for each public property of the Book type. This happens because the AutoGenerateColumns property is True by default. Let's continue by making the DataGrid look the way we want it to look. By default, the DataGrid is user-editable, so we may want to change this feature. Setting the IsReadOnly property to True will make it impossible for a user to edit the data in the control. We can lock the display even further by setting both the CanUserResizeColumns and the CanUserReorderColumns properties to False. This will prohibit the user from resizing and reordering the columns inside the DataGrid, which are enabled by default. This is shown in the following code: <sdk:DataGrid x_Name="BookDataGrid" AutoGenerateColumns="True" CanUserReorderColumns="False" CanUserResizeColumns="False" IsReadOnly="True"> </sdk:DataGrid> The DataGrid also offers quite an impressive list of properties that we can use to change its appearance. By adding the following code, we specify alternating the background colors (the RowBackground and AlternatingRowBackground properties), column widths (the ColumnWidth property), and row heights (the RowHeight property). We also specify how the gridlines should be displayed (the GridLinesVisibility and HorizontalGridLinesBrushs properties). Finally, we specify that we also want a row header to be added (the HeadersVisibility property ). <sdk:DataGrid x_Name="BookDataGrid" AutoGenerateColumns="True" CanUserReorderColumns="False" CanUserResizeColumns="False" RowBackground="#999999" AlternatingRowBackground="#CCCCCC" ColumnWidth="90" RowHeight="30" GridLinesVisibility="Horizontal" HeadersVisibility="All" HorizontalGridLinesBrush="Blue"> </sdk:DataGrid> We can also get a hook into the loading of the rows. For this, the LoadingRow event has to be used. This event is triggered when each row gets loaded. Using this event, we can get access to a row and change its properties based on custom code. In the following code, we are specifying that if the book is a thriller, we want the row to have a red background: private void BookDataGrid_LoadingRow(object sender, DataGridRowEventArgs e) { Book loadedBook = e.Row.DataContext as Book; if (loadedBook.Category == Category.Thriller) { e.Row.Background = new SolidColorBrush(Colors.Red); //It's a thriller! e.Row.Height = 40; } else { e.Row.Background = null; } } After completing these steps, we have the DataGrid that we wanted. It displays the data (including headers), fixes the columns and makes it impossible for the user to edit the data. Also, the color of the rows and alternating rows is changed, the vertical grid lines are hidden, and a different color is applied to the horizontal grid lines. Using the LoadingRow event, we have checked whether the book being added is of the "Thriller" category, and if so, a red color is applied as the background color for the row. The result can be seen in the following screenshot: How it works... The DataGrid allows us to display the data easily, while still offering us many customization options to format the control as needed. The DataGrid is defined in the System.Windows.Controls namespace, which is located in the System.Windows.Controls.Data assembly. By default, this assembly is not referenced while creating a new Silverlight application. Therefore, the following extra references are added while dragging the control from the toolbox for the first time: System.ComponentModel.DataAnnotations System.Windows.Controls.Data System.Windows.Controls.Data.Input System.Windows.Data While compiling the application, the corresponding assemblies are added to the XAP file (as can be seen in the following screenshot, which shows the contents of the XAP file). These assemblies need to be added because while installing the Silverlight plugin, they aren't installed as a part of the CLR. This is done in order to keep the plugin size small. However, when we use them in our application, they are embedded as part of the application. This results in an increase of the download size of the XAP file. In most circumstances, this is not a problem. However, if the file size is an important requirement, then it is essential to keep an eye on this. Also, Visual Studio will include the following namespace mapping into the XAML file: From then on, we can use the control as shown in the following line of code: <sdk:DataGrid x_Name="BookDataGrid"> </sdk:DataGrid> Once the control is added on the page, we can use it in a data binding scenario. To do so, we can point the ItemsSource property to any IEnumerable implementation. Each row in the DataGrid will correspond to an object in the collection. When AutoGenerateColumns is set to True (the default), the DataGrid uses a refl ection on the type of objects bound to it. For each public property it encounters, it generates a corresponding column. Out of the box, the DataGrid includes a text column, a checkbox column, and a template column. For all the types that can't be displayed, it uses the ToString method and a text column. If we want the DataGrid to feature automatic synchronization, the collection should implement the INotifyCollectionChanged interface. If changes to the objects are to be refl ected in the DataGrid, then the objects in the collection should themselves implement the INotifyPropertyChanged interface. There's more While loading large amounts of data into the DataGrid, the performance will still be very good. This is the result of the DataGrid implementing UI virtualization, which is enabled by default. Let's assume that the DataGrid is bound to a collection of 1,000,000 items (whether or not this is useful is another question). Loading all of these items into memory would be a time-consuming task as well as a big performance hit. Due to UI virtualization, the control loads only the rows it's currently displaying. (It will actually load a few more to improve the scrolling experience.) While scrolling, a small lag appears when the control is loading the new items. Since Silverlight 3, the ListBox also features UI virtualization. Inserting, updating, and deleting data in a DataGrid The DataGrid is an outstanding control to use while working with large amounts of data at the same time. Through its Excel-like interface, not only can we easily view the data, but also add new records or update and delete existing ones. In this recipe, we'll take a look at how to build a DataGrid that supports all of the above actions on a collection of items. Getting ready This recipe builds on the code that was created in the previous recipe. To follow along with this recipe, you can keep using your code or use the starter solution located in the Chapter04/Datagrid_Editing_Data_Starter folder in the code bundle available on the Packt website. The finished solution for this recipe can be found in the Chapter04/Datagrid_Editing_Data_Completed folder.
Read more
  • 0
  • 0
  • 2019

article-image-enhancing-your-site-php-and-jquery
Packt
29 Dec 2010
12 min read
Save for later

Enhancing your Site with PHP and jQuery

Packt
29 Dec 2010
12 min read
  PHP jQuery Cookbook Over 60 simple but highly effective recipes to create interactive web applications using PHP with jQuery Create rich and interactive web applications with PHP and jQuery Debug and execute jQuery code on a live site Design interactive forms and menus Another title in the Packt Cookbook range, which will help you get to grips with PHP as well as jQuery         Read more about this book       (For more resources on this subject, see here.) Introduction In this article, we will look at some advanced techniques that can be used to enhance the functionality of web applications. We will create a few examples where we will search for images the from Flickr and videos from YouTube using their respective APIs. We will parse a RSS feed XML using jQuery and learn to create an endless scrolling page like Google reader or the new interface of Twitter. Besides this, you will also learn to create a jQuery plugin, which you can use independently in your applications. Sending cross-domain requests using server proxy Browsers do not allow scripts to send cross-domain requests due to security reasons. This means a script at domain http://www.abc.com cannot send AJAX requests to http://www.xyz.com. This recipe will show how you can overcome this limitation by using a PHP script on the server side. We will create an example that will search Flickr for images. Flickr will return a JSON, which will be parsed by jQuery and images will be displayed on the page. The following screenshot shows a JSON response from Flickr: Getting ready Create a directory for this article and name it as Article9. In this directory, create a folder named Recipe1. Also get an API key from Flickr by signing up at http://www.flickr.com/services/api/keys/. How to do it... Create a file inside the Recipe1 folder and name it as index.html. Write the HTML code to create a form with three fields: tag, number of images, and image size. Also create an ul element inside which the results will be displayed. <html> <head> <title>Flickr Image Search</title> <style type="text/css"> body { font-family:"Trebuchet MS",verdana,arial;width:900px; } fieldset { width:333px; } ul{ margin:0;padding:0;list-style:none; } li{ padding:5px; } span{ display:block;float:left;width:150px; } #results li{ float:left; } .error{ font-weight:bold; color:#ff0000; } </style> </head> <body> <form id="searchForm"> <fieldset> <legend>Search Criteria</legend> <ul> <li> <span>Tag</span> <input type="text" name="tag" id="tag"/> </li> <li> <span>Number of images</span> <select name="numImages" id="numImages"> <option value="20">20</option> <option value="30">30</option> <option value="40">40</option> <option value="50">50</option> </select> </li> <li> <span>Select a size</span> <select id="size"> <option value="s">Small</option> <option value="t">Thumbnail</option> <option value="-">Medium</option> <option value="b">Large</option> <option value="o">Original</option> </select> </li> <li> <input type="button" value="Search" id="search"/> </li> </ul> </fieldset> </form> <ul id="results"> </ul> </body> </html> The following screenshot shows the form created: Include the jquery.js file. Then, enter the jQuery code that will send the AJAX request to a PHP file search.php. Values of form elements will be posted with an AJAX request. A callback function showImages is also defined that actually reads the JSON response and displays the images on the page. <script type="text/javascript" src="../jquery.js"></script> <script type="text/javascript"> $(document).ready(function() { $('#search').click(function() { if($.trim($('#tag').val()) == '') { $('#results').html('<li class="error">Please provide search criteria</li>'); return; } $.post( 'search.php', $('#searchForm').serialize(), showImages, 'json' ); }); function showImages(response) { if(response['stat'] == 'ok') { var photos = response.photos.photo; var str= ''; $.each(photos, function(index,value) { var farmId = value.farm; var serverId = value.server; var id = value.id; var secret = value.secret; var size = $('#size').val(); var title = value.title; var imageUrl = 'http://farm' + farmId + '.static.flickr.com/' + serverId + '/' + id + '_' + secret + '_' + size + '.jpg'; str+= '<li>'; str+= '<img src="' + imageUrl + '" alt="' + title + '" />'; str+= '</li>'; }); $('#results').html(str); } else { $('#results').html('<li class="error">an error occured</li>'); } } }); </script> Create another file named search.php. The PHP code in this file will contact the Flickr API with specified search criteria. Flickr will return a JSON that will be sent back to the browser where jQuery will display it on the page. <?php define('API_KEY', 'your-API-key-here'); $url = 'http://api.flickr.com/services/rest/?method=flickr. photos.search'; $url.= '&api_key='.API_KEY; $url.= '&tags='.$_POST['tag']; $url.= '&per_page='.$_POST['numImages']; $url.= '&format=json'; $url.= '&nojsoncallback=1'; header('Content-Type:text/json;'); echo file_get_contents($url); ?> Now, run the index.html file in your browser, enter a tag to search in the form, and select the number of images to be retrieved and image size. Click on the Search button. A few seconds later you will see the images from Flickr displayed on the page: <html> <head> <title>Youtube Video Search</title> <style type="text/css"> body { font-family:"Trebuchet MS",verdana,arial;width:900px; } fieldset { width:333px; } ul{ margin:0;padding:0;list-style:none; } li{ padding:5px; } span{ display:block;float:left;width:150px; } #results ul li{ float:left; background-color:#483D8B; color:#fff;margin:5px; width:120px; } .error{ font-weight:bold; color:#ff0000; } img{ border:0} </style> </head> <body> <form id="searchForm"> <fieldset> <legend>Search Criteria</legend> <ul> <li> <span>Enter query</span> <input type="text" id="query"/> </li> <li> <input type="button" value="Search" id="search"/> </li> </ul> </fieldset> </form> <div id="results"> </div> </body> </html> How it works... On clicking the Search button, form values are sent to the PHP file search.php. Now, we have to contact Flickr and search for images. Flickr API provides several methods for accessing images. We will use the method flickr.photos.search to search by tag name. Along with method name we will have to send the following parameters in the URL: api_key: An API key is mandatory. You can get one from: http://www.flickr.com/services/api/keys/. tags: The tags to search for. These can be comma-separated. This value will be the value of textbox tag. per_page: Number of images in a page. This can be a maximum of 99. Its value will be the value of select box numImages. format: It can be JSON, XML, and so on. For this example, we will use JSON. nojsoncallback: Its value will be set to 1 if we don't want Flickr to wrap the JSON in a function wrapper. Once the URL is complete we can contact Flickr to get results. To get the results' we will use the PHP function file_get_contents, which will get the results JSON from the specified URL. This JSON will be echoed to the browser. jQuery will receive the JSON in callback function showImages. This function first checks the status of the response. If the response is OK, we get the photo elements from the response and we can iterate over them using jQuery's $.each method. To display an image, we will have to get its URL first, which will be created by combining different values of the photo object. According to Flickr API specification, an image URL can be constructed in the following manner: http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}_[size].jpg So we get the farmId, serverId, id, and secret from the photo element. The size can be one of the following: s (small square) t (thumbnail) - (medium) b (large) o (original image) We have already selected the image size from the select box in the form. By combining all these values, we now have the Flickr image URL. We wrap it in a li element and repeat the process for all images. Finally, we insert the constructed images into the results li. Making cross-domain requests with jQuery The previous recipe demonstrated the use of a PHP file as a proxy for querying cross-domain URLs. This recipe will show the use of JSONP to query cross-domain URLs from jQuery itself. We will create an example that will search for the videos from YouTube and will display them in a list. Clicking on a video thumbnail will open a new window that will take the user to the YouTube website to show that video. The following screenshot shows a sample JSON response from YouTube: Getting ready Create a folder named Recipe2 inside the Article9 directory. How to do it... Create a file inside the Recipe2 folder and name it as index.html. Write the HTML code to create a form with a single field query and a DIV with results ID inside which the search results will be displayed. <script type="text/javascript" src="../jquery.js"></script> <script type="text/javascript"> $(document).ready(function() { $('#search').click(function() { var query = $.trim($('#query').val()); if(query == '') { $('#results').html('<li class="error">Please enter a query.</li>'); return; } $.get( 'http://gdata.youtube.com/feeds/api/videos?q=' + query + '&alt=json-in-script', {}, showVideoList, 'jsonp' ); }); }); function showVideoList(response) { var totalResults = response['feed']['openSearch$totalResults']['$t']; if(parseInt(totalResults,10) > 0) { var entries = response.feed.entry; var str = '<ul>'; for(var i=1; i< entries.length; i++) { var value = entries[i]; var title = value['title']['$t']; var mediaGroup = value['media$group']; var videoURL = mediaGroup['media$player'][0]['url']; var thumbnail = mediaGroup['media$thumbnail'][0]['url']; var thumbnailWidth = mediaGroup['media$thumbnail'][0]['width']; var thumbnailHeight = mediaGroup['media$thumbnail'][0]['height']; var numComments = value['gd$comments']['gd$feedLink']['countHint']; var rating = parseFloat(value['gd$rating']['average']).toFixed(2); str+= '<li>'; str+= '<a href="' + videoURL + '" target="_blank">'; str+= '<img src="'+thumbNail+'" width="'+thumbNailWidth+'" height="'+thumbNailWidth+'" title="' + title + '" />'; str+= '</a>'; str+= '<hr>'; str+= '<p style="width: 120px; font-size: 12px;">Comments: ' + numComments + ''; str+= '<br/>'; str+= 'Rating: ' + rating; str+= '</p>'; str+= '</li>'; } str+= '</ul>'; $('#results').html(str); } else { $('#results').html('<li class="error">No results.</li>'); } } </script> Include the jquery.js file before closing the &ltbody> tag. Now, write the jQuery code that will take the search query from the textbox and will try to retrieve the results from YouTube. A callback function called showVideoList will get the response and will create a list of videos from the response. http://gdata.youtube.com/feeds/api/videos?q=' + query + '&alt=json-in-script All done, and we are now ready to search YouTube. Run the index.html file in your browser and enter a search query. Click on the Search button and you will see a list of videos with a number of comments and a rating for each video. How it works... script tags are an exception to cross-browser origin policy. We can take advantage of this by requesting the URL from the src attribute of a script tag and by wrapping the raw response in a callback function. In this way the response becomes JavaScript code instead of data. This code can now be executed on the browser. The URL for YouTube video search is as follows: http://gdata.youtube.com/feeds/api/videos?q=' + query + '&alt=json-in-script Parameter q is the query that we entered in the textbox and alt is the type of response we want. Since we are using JSONP instead of JSON, the value for alt is defined as json-in-script as per YouTube API specification. On getting the response, the callback function showVideoList executes. It checks whether any results are available or not. If none are found, an error message is displayed. Otherwise, we get all the entry elements and iterate over them using a for loop. For each video entry, we get the videoURL, thumbnail, thumbnailWidth, thumbnailHeight, numComments, and rating. Then we create the HTML from these variables with a list item for each video. For each video an anchor is created with href set to videoURL. The video thumbnail is put inside the anchor and a p tag is created where we display the number of comments and rating for a particular video. After the HTML has been created, it is inserted in the DIV with ID results. There's more... About JSONP You can read more about JSONP at the following websites: http://remysharp.com/2007/10/08/what-is-jsonp/ http://en.wikipedia.org/wiki/JSON#JSONP
Read more
  • 0
  • 0
  • 2004

article-image-aspnet-social-networks-making-friends-part-1
Packt
28 Oct 2009
19 min read
Save for later

ASP.NET Social Networks—Making Friends (Part 1)

Packt
28 Oct 2009
19 min read
Problem There are many aspects to building relationships in any community—real or virtual. First and foremost is initiating contact with the people whom you will eventually call your friends. We will do this in a few ways. First, we will provide a way for our users to search the site for friends who are also members. Second, we will create a form that allows you to enter your friends' email IDs and invite them directly. Third, we will create a form that allows you to import all of your contacts from Outlook. All of these methods of inviting a friend into the system would of course generate an email invite. The user would have the ability to then follow the link into the system and either sign up or log in to accept the request. The preceding screenshot shows a sample email that the user would receive in their inbox. And following is the message that would be seen: Once the user has clicked on the link in their email, he/she will be taken to a page displaying the request. Once we have a way for our users to attach friends to their profile, we need to start integrating the concept of friends into the fabric of our site. We will need a way for our users to view all of their friends. We will also need a way for our users to remove the relationships (for those users who are no longer friends!). Then we will need to add friends to our user's public profile. While this is a good first pass at integrating the concept of friends into our site, there are a couple more steps for true integration. We need to add friend request and friend confirm alerts. We also need to modify the alert system so that when users modify their profile, change their avatar, or any other alert that is triggered by users of our system, all of their friends are notified on The Filter. Once this is done we have one final topic to cover—which sort of fits in the realm of friends—the concept of Status Updates. This is a form of a micro blog. It allows users to post something about: What they are currently doing Where they are or What they are thinking about This is then added to their profile and sent out to their friends' filters. The box in the preceding screenshot is where the user can enter his/herStatus Updates. Each of these updates will also be shown on the updates view and in their filter views. This really helps to keep The Filter busy and helps people feel involved with their friends. Design Now let's talk about the design of these features. Friends This article is an attempt to throw light on the infrastructure needs and more heavily focused on the UI side for creating and managing relationships. That being said, there is always some form of groundwork that has to be in place prior to adding new features. In this case we need to add the concept of a friend prior to having the ability to create friendships. This concept is a relatively simple one as it is really only defining a relationship between two accounts. We have the account that requested the relationship and the account that accepted the relationship. This allows an account to be linked to as many other accounts as they wish. Finding friends Like in life, it is very difficult to create friends without first locating and meeting people. For that reason the various ways to locate and invite someone to be your friend is our first topic. Searching for a friend The easiest way to locate friends who might be interested in the same site that you are is to search through the existing user base. For that reason we will need to create a simple keyword search box that is accessible from any page on the site. This search feature should take a look at several fields of data pertaining to an account and return all possible users. From the search results page we should be able to initiate a friend request. Inviting a friend The next best thing to locating friends who are already members of the site is to invite people who you know out of the site. The quickest way to implement this is to allow a user to manually enter an email address or many email addresses, type a message, and then submit. This would be implemented with a simple form that generates a quick email to the recipient list. In the body of the email will be a link that allows the recipients to come in to our site. Importing friends from external sources An obvious extension of the last topic is to somehow automate the importing process of contacts from an email management tool. We will create a toolset that allows the user to export their contacts from Outlook and import them via a web form. The user should then be able to select the contacts that they want to invite. Sending an invitation With all the three of the above methods we will end up sending out an invitation email. We could simply send out an email with a link to the site. However, we need to maintain: Who has been invited Who initiated the invitation and When this occurred Then in the email, rather than just invite people in, we want to assign the user a key so that we can easily identify them on their way in. We will use a system-generated GUID to do this. In the case of inviting an existing user, we will allow him/her to log in to acknowledge the new friendship. In the case of a non-member user who was invited, we will allow him/her to create a new account. In both cases we will populate the invitation with the invited user's Account ID so that we have some history about the relationship. Adding friend alerts to the filter Once we have the framework in place for inviting and accepting friendship requests, we need to extend our existing system with alerts. These alerts should show up on existing user's Filters to show that they sent an invitation. We should also have alerts showing that a user has been invited. Once a user has accepted a friendship we should also have an alert. Interacting with your friends Now let's discuss some of the features that we need to interact with our friends. Viewing your friends Friends are only good if a user can interact with them. The first stop along this train of thought is to provide a page that allows a user to see all the friends he/she has. This is a jumping off point for a user to view the profile of friends. Also, as the concept of a user's profile grows, more data can be shown about each friend in an at-a-glance format. In addition to an all Friends page, we can add friends' views to a user's public profile so that other users can see the relationships. Managing your friends Now that we can see into all the relationships, we can finally provide the users with the ability to remove a relationship. In our initial pass this will be a permanent deletion of the relationship. Following your friends Now, we can extend the alert system so that when alerts are generated for a common user, such as updating their profile information, uploading a new photo, or any other user specific task, all the user's friends are automatically notified via their Filter. Providing status updates to your friends Somewhat related to friend-oriented relationships and The Filter is the concept of micro blogs. We need to add a way for a user to send a quick blurb about what they are doing, what they are thinking, and so on. This would also show up on the Filters of all the user's friends. This feature creates a lot of dynamic content on an end user's homepage, which keeps things interesting. Solution Now let's take a look at our solution. Implementing the database Let's look at the tables that are needed to support these new features. The Friends Table As the concept of friends is our base discussion for this article, we will immediately dive in and start creating the tables around this subject. As you have seen previously this is very straightforward table structure that simply links one account to the other. Friend Invitations This table is responsible for keeping track of who has been invited to the site, by whom, and when. It also holds the key (GUID) that is sent to the friends so that they can get back into the system under the appropriate invitation. Once a friend has accepted the relationship, their AccountID is stored here too, so that we can see how relationships were created in the past. Status Updates Status Updates allow a user to tell their friends what they are doing at that time. This is a micro blog so to speak. A micro blog allows a user to write small blurbs about anything. Examples of this are Twitter and Yammer. For more information take a look here: http://en.wikipedia.org/wiki/Micro-blogging The table needed for this is also simple. It tracks who said what, what was said, and when. Creating the Relationships Here are the relationships that we need for the tables we just discussed: Friends and Accounts via the owning account Friends and Accounts via the friends account FriendInvitations and Accounts StatusUpdates and Accounts Setting up the data access layer Let's extend the data access layer now to handle these new tables. Open your Fisharoo.dbml file and drag in these three new tables. We are not allowing LINQ to manage these relationships for us. So go ahead and remove the relationships from the surrounding tables. Once you hit Save we should have three new classes to work with! Building repositories As always, with these new tables will come new repositories. The following repositories will be created: FriendRepository FriendInvitationRepository StatusUpdateRepository In addition to the creation of the above repositories, we will also need to modify the AccountRepository. FriendRepository Most of our repositories will always follow the same design. They provide a way to get at one record, many records by a parent ID, save a record, and delete a record. This repository differs slightly from the norm when it is time to retrieve a list of friends in that it has two sides of the relationship to look at—on one side where it is the owning Account of the Friend relationship and on the other side where the relationship is owned by another account. Here is that method: public List<Friend> GetFriendsByAccountID(Int32 AccountID){ List<Friend> result = new List<Friend>(); using(FisharooDataContext dc = conn.GetContext()) { //Get my friends direct relationship IEnumerable<Friend> friends = (from f in dc.Friends where f.AccountID == AccountID && f.MyFriendsAccountID AccountID select f).Distinct(); result = friends.ToList(); //Getmy friends indirect relationship var friends2 = (from f in dc.Friends where f.MyFriendsAccountID == AccountID && f.AccountID != AccountID select new { FriendID = f.FriendID, AccountID = f.MyFriendsAccountID, MyFriendsAccountID = f.AccountID, CreateDate = f.CreateDate, Timestamp = f.Timestamp }).Distinct(); foreach (object o in friends2) { Friend friend = o as Friend; if(friend != null) result.Add(friend); } } return result;} This method queries for all friends that are owned by this account. It then queries for the reverse relationship where this account is owned by another account. Then it adds the second query to the first and returns that result. Here is the method that gets the Accounts of our Friends: public List<Account> GetFriendsAccountsByAccountID(Int32 AccountID){ List<Friend> friends = GetFriendsByAccountID(AccountID); List<int> accountIDs = new List<int>(); foreach (Friend friend in friends) { accountIDs.Add(friend.MyFriendsAccountID); } List<Account> result = new List<Account>(); using(FisharooDataContext dc = conn.GetContext()) { IEnumerable<Account> accounts = from a in dc.Accounts where accountIDs.Contains(a.AccountID) select a; result = accounts.ToList(); } return result;} This method first gathers all the friends (via the first method we discussed) and then queries for all the related accounts. It then returns the result. FriendInvitationRepository Like the other repositories this one has the standard methods. In addition to those we also need to be able to retrieve an invitation by GUID or the invitation key that was sent to the friend. public FriendInvitation GetFriendInvitationByGUID(Guid guid){ FriendInvitation friendInvitation; using(FisharooDataContext dc = conn.GetContext()) { friendInvitation = dc.FriendInvitations.Where(fi => fi.GUID == guid).FirstOrDefault(); } return friendInvitation;} This is a very straightforward query matching the GUID values. In addition to the above method we will also need a way for invitations to be cleaned up. For this reason we will also have a method named CleanUpFriendInvitations(). public void CleanUpFriendInvitationsForThisEmail(FriendInvitation friendInvitation){ using (FisharooDataContext dc = conn.GetContext()) { IEnumerable<FriendInvitation> friendInvitations = from fi in dc.FriendInvitations where fi.Email == friendInvitation.Email && fi.BecameAccountID == 0 && fi.AccountID == friendInvitation.AccountID select fi; foreach (FriendInvitation invitation in friendInvitations) { dc.FriendInvitations.DeleteOnSubmit(invitation); } dc.SubmitChanges(); }} This method is responsible for clearing out any invitations in the system that are sent from account A to account B and have not been activated (account B never did anything with the invite). Rather than checking if the invitation already exists when it is created, we will allow them to be created time and again (checking each invite during the import process of 500 contacts could really slow things down!). When account B finally accepts one of the invitations all of the others will be cleared. Also, in case account B never does anything with the invites, we will need a database process that periodically cleans out old invitations. StatusUpdateRepository Other than the norm, this repository has a method that gets topN StatusUpdates for use on the profile page. public List<StatusUpdate> GetTopNStatusUpdatesByAccountID(Int32 AccountID, Int32 Number){ List<StatusUpdate> result = new List<StatusUpdate>(); using (FisharooDataContext dc = conn.GetContext()) { IEnumerable<StatusUpdate> statusUpdates = (from su in dc.StatusUpdates where su.AccountID == AccountID orderby su.CreateDate descending select su).Take(Number); result = statusUpdates.ToList(); } return result;} This is done with a standard query with the addition of the Take() method, which translates into a TOP statement in the resulting SQL. AccountRepository With the addition of our search capabilities we will require a new method in our AccountRepository. This method will be the key for searching accounts. public List<Account> SearchAccounts(string SearchText){ List<Account> result = new List<Account>(); using (FisharooDataContext dc = conn.GetContext()) { IEnumerable<Account> accounts = from a in dc.Accounts where(a.FirstName + " " + a.LastName).Contains(SearchText) || a.Email.Contains(SearchText) || a.Username.Contains(SearchText) select a; result = accounts.ToList(); } return result;} This method currently searches through a user's first name, last name, email address, and username. This could of course be extended to their profile data and many other data points (all in good time!). Implementing the services/application layer Now that we have the repositories in place, we can begin to create the services that sit on top of those repositories. We will be creating the following services: FriendService In addition to that we will also be extending these services: AlertService PrivacyService FriendService The FriendService currently has a couple of duties. We will need it to tell us whether or not a user is a Friend, so that we can extend the PrivacyService to consider friends (recall that we currently only understand public and private settings!). In addition to that we need our FriendService to be able to handle creating Friends from a FriendInvitation. public bool IsFriend(Account account, Account accountBeingViewed){ if(account == null) return false; if(accountBeingViewed == null) return false; if(account.AccountID == accountBeingViewed.AccountID) return true; else { Friend friend = _friendRepository.GetFriendsByAccountID (accountBeingViewed.AccountID). Where(f => f.MyFriendsAccountID == account.AccountID).FirstOrDefault(); if(friend != null) return true; } return false;} This method needs to know who is making the request as well as who it is making the request about. It then verifies that both accounts are not null so that we can use them down the road and returns false if either of them are null. We then check to see if the user that is doing the viewing is the same user as is being viewed. If so we can safely return true. Then comes the fun part—currently we are using the GetFriendsByAccountID method found in the FriendRepository. We iterate through that list to see if our friend is there in the list or not. If we locate it, we return true. Otherwise the whole method has failed to locate a result and returns false. Keep in mind that this way of doing things could quickly become a major performance issue. If you are checking security around several data points frequently in the same page, this is a large query and moves a lot of data around. If someone had 500 friends this would not be acceptable. As our goal is for people to have lots of friends, we generally would not want to follow this way. Your best bet then is to create a LINQ query in the FriendsRepository to handle this logic directly only returning true or false. Now comes our CreateFriendFromFriendInvitation method, which as the name suggests, creates a friend from a friend invitation! public void CreateFriendFromFriendInvitation(Guid InvitationKey, Account InvitationTo){ //update friend invitation request FriendInvitation friendInvitation = _friendInvitationRepository. GetFriendInvitationByGUID(InvitationKey); friendInvitation.BecameAccountID = InvitationTo.AccountID; _friendInvitationRepository.SaveFriendInvitation(friendInvitation); _friendInvitationRepository.CleanUpFriendInvitationsForThisEmail(frie ndInvitation); //create friendship Friend friend = new Friend(); friend.AccountID = friendInvitation.AccountID; friend.MyFriendsAccountID = InvitationTo.AccountID; _friendRepository.SaveFriend(friend); Account InvitationFrom = _accountRepository.GetAccountByID (friendInvitation.AccountID); _alertService.AddFriendAddedAlert(InvitationFrom, InvitationTo); //TODO: MESSAGING - Add message to inbox regarding new friendship!} This method expects the InvitationKey (in the form of a system generated GUID) and the Account that is wishing to create the relationship. It then gets the FriendInvitation and updates the BecameAccountID property of the new friend. We then make a call to flush any other friend invites between these two users. Once we have everything cleaned up, we add a new alert to the system letting the account that initiated this invitation know that the invitation was accepted. AlertService The alert service is essentially a wrapper to post an alert to the user's profile on The Filter. Go through the following methods. They do not need much explanation! public void AddStatusUpdateAlert(StatusUpdate statusUpdate){ alert = new Alert(); alert.CreateDate = DateTime.Now; alert.AccountID = _userSession.CurrentUser.AccountID; alert.AlertTypeID = (int)AlertType.AlertTypes.StatusUpdate; alertMessage = "<div class="AlertHeader">" + GetProfileImage(_userSession.CurrentUser.AccountID) + GetProfileUrl(_userSession.CurrentUser.Username) + " " + statusUpdate.Status + "</div>"; alert.Message = alertMessage; SaveAlert(alert); SendAlertToFriends(alert);}public void AddFriendRequestAlert(Account FriendRequestFrom, Account FriendRequestTo, Guid requestGuid, string Message){ alert = new Alert(); alert.CreateDate = DateTime.Now; alert.AccountID = FriendRequestTo.AccountID; alertMessage = "<div class="AlertHeader">" + GetProfileImage(FriendRequestFrom.AccountID) + GetProfileUrl(FriendRequestFrom.Username) + " would like to be friends!</div>"; alertMessage += "<div class="AlertRow">"; alertMessage += FriendRequestFrom.FirstName + " " + FriendRequestFrom.LastName + " would like to be friends with you! Click this link to add this user as a friend: "; alertMessage += "<a href="" + _configuration.RootURL + "Friends/ConfirmFriendshipRequest.aspx?InvitationKey=" + requestGuid.ToString() + "">" + _configuration.RootURL + "Friends/ConfirmFriendshipRequest.aspx?InvitationKey=" + requestGuid.ToString() + "</a><HR>" + Message + "</div>"; alert.Message = alertMessage; alert.AlertTypeID = (int) AlertType.AlertTypes.FriendRequest; SaveAlert(alert);}public void AddFriendAddedAlert(Account FriendRequestFrom, Account FriendRequestTo){ alert = new Alert(); alert.CreateDate = DateTime.Now; alert.AccountID = FriendRequestFrom.AccountID; alertMessage = "<div class="AlertHeader">" + GetProfileImage(FriendRequestTo.AccountID) + GetProfileUrl(FriendRequestTo.Username) + " is now your friend!</div>"; alertMessage += "<div class="AlertRow">" + GetSendMessageUrl(FriendRequestTo.AccountID) + "</div>"; alert.Message = alertMessage; alert.AlertTypeID = (int)AlertType.AlertTypes.FriendAdded; SaveAlert(alert); alert = new Alert(); alert.CreateDate = DateTime.Now; alert.AccountID = FriendRequestTo.AccountID; alertMessage = "<div class="AlertHeader">" + GetProfileImage(FriendRequestFrom.AccountID) + GetProfileUrl(FriendRequestFrom.Username) + " is now your friend!</div>"; alertMessage += "<div class="AlertRow">" + GetSendMessageUrl(FriendRequestFrom.AccountID) + "</div>"; alert.Message = alertMessage; alert.AlertTypeID = (int)AlertType.AlertTypes.FriendAdded; SaveAlert(alert); alert = new Alert(); alert.CreateDate = DateTime.Now; alert.AlertTypeID = (int) AlertType.AlertTypes.FriendAdded; alertMessage = "<div class="AlertHeader">" + GetProfileUrl(FriendRequestFrom.Username) + " and " + GetProfileUrl(FriendRequestTo.Username) + " are now friends!</div>"; alert.Message = alertMessage; alert.AccountID = FriendRequestFrom.AccountID; SendAlertToFriends(alert); alert.AccountID = FriendRequestTo.AccountID; SendAlertToFriends(alert);} PrivacyService Now that we have a method to check if two people are friends or not, we can finally extend our PrivacyService to account for friends. Up to this point we are only interrogating whether something is marked as private or public. Friends is marked false by default! public bool ShouldShow(Int32 PrivacyFlagTypeID, Account AccountBeingViewed, Account Account, List<PrivacyFlag> Flags){ bool result; bool isFriend = _friendService.IsFriend(Account,AccountBeingViewed); //flag marked as private test if(Flags.Where(f => f.PrivacyFlagTypeID == PrivacyFlagTypeID && f.VisibilityLevelID == (int)VisibilityLevel.VisibilityLevels.Private) .FirstOrDefault() != null) result = false; //flag marked as friends only test else if (Flags.Where(f => f.PrivacyFlagTypeID == PrivacyFlagTypeID && f.VisibilityLevelID == (int)VisibilityLevel.VisibilityLevels.Friends) .FirstOrDefault() != null && isFriend) result = true; else if (Flags.Where(f => f.PrivacyFlagTypeID == PrivacyFlagTypeID && f.VisibilityLevelID == (int)VisibilityLevel.VisibilityLevels.Public) .FirstOrDefault() != null) result = true; else result = false; return result;} Summary The article started with the thought process of how we can apply the concept of Friends to our community site. We tried to figure out what we need to do to implement the concept, we then finalized our requirements, and finally we began implementing the features. In the next part of this article we will continue with the implementation process.  
Read more
  • 0
  • 0
  • 1983
Visually different images

article-image-ajax-form-validation-part-2
Packt
19 Feb 2010
11 min read
Save for later

AJAX Form Validation: Part 2

Packt
19 Feb 2010
11 min read
How to implement AJAX form validation In this article, we redesigned the code for making AJAX requests when creating the XmlHttp class. The AJAX form validation application makes use of these techniques. The application contains three pages: One page renders the form to be validated Another page validates the input The third page is displayed if the validation is successful The application will have a standard structure, composed of these files: index.php: It is the file loaded initially by the user. It contains references to the necessary JavaScript files and makes asynchronous requests for validation to validate.php. index_top.php: It is a helper file loaded by index.php and contains several objects for rendering the HTML form. validate.css: It is the file containing the CSS styles for the application. json2.js: It is the JavaScript file used for handling JSON objects. xhr.js: It is the JavaScript file that contains our XmlHttp object used for making AJAX requests. validate.js: It is the JavaScript file loaded together with index.php on the client side. It makes asynchronous requests to a PHP script called validate.php to perform the AJAX validation. validate.php: It is a PHP script residing on the same server as index.php, and it offers the server-side functionality requested asynchronously by the JavaScript code in index.php. validate.class.php: It is a PHP script that contains a class called Validate, which contains the business logic and database operations to support the functionality of validate.php. config.php: It will be used to store global configuration options for your application, such as database connection data, and so on. error_handler.php: It contains the error-handling mechanism that changes the text of an error message into a human-readable format. allok.php: It is the page to be displayed if the validation is successful. Time for action – AJAX form validation Connect to the ajax database and create a table named users with the following code: CREATE TABLE users ( user_id INT UNSIGNED NOT NULL AUTO_INCREMENT, user_name VARCHAR(32) NOT NULL, PRIMARY KEY (user_id) ); Execute the following INSERT commands to populate your users table with some sample data: INSERT INTO users (user_name) VALUES ('bogdan'); INSERT INTO users (user_name) VALUES ('audra'); INSERT INTO users (user_name) VALUES ('cristian'); Let's start writing the code with the presentation tier. We'll define the styles for our form by creating a file named validate.css, and adding the following code to it: body { font-family: Arial, Helvetica, sans-serif; font-size: 0.8em; color: #000000; } label { float: left; width: 150px; font-weight: bold; } input, select { margin-bottom: 3px; } .button { font-size: 2em; } .left { margin-left: 150px; } .txtFormLegend { color: #777777; font-weight: bold; font-size: large; } .txtSmall { color: #999999; font-size: smaller; } .hidden { display: none; } .error { display: block; margin-left: 150px; color: #ff0000; } Now create a new file named index_top.php, and add the following code to it. This script will be loaded from the main page index.php. <?php // enable PHP session session_start(); // Build HTML <option> tags function buildOptions($options, $selectedOption) { foreach ($options as $value => $text) { if ($value == $selectedOption) { echo '<option value="' . $value . '" selected="selected">' . $text . '</option>'; } else { echo '<option value="' . $value . '">' . $text . '</option>'; } } } // initialize gender options array $genderOptions = array("0" => "[Select]", "1" => "Male", "2" => "Female"); // initialize month options array $monthOptions = array("0" => "[Select]", "1" => "January", "2" => "February", "3" => "March", "4" => "April", "5" => "May", "6" => "June", "7" => "July", "8" => "August", "9" => "September", "10" => "October", "11" => "November", "12" => "December"); // initialize some session variables to prevent PHP throwing // Notices if (!isset($_SESSION['values'])) { $_SESSION['values']['txtUsername'] = ''; $_SESSION['values']['txtName'] = ''; $_SESSION['values']['selGender'] = ''; $_SESSION['values']['selBthMonth'] = ''; $_SESSION['values']['txtBthDay'] = ''; $_SESSION['values']['txtBthYear'] = ''; $_SESSION['values']['txtEmail'] = ''; $_SESSION['values']['txtPhone'] = ''; $_SESSION['values']['chkReadTerms'] = ''; } if (!isset($_SESSION['errors'])) { $_SESSION['errors']['txtUsername'] = 'hidden'; $_SESSION['errors']['txtName'] = 'hidden'; $_SESSION['errors']['selGender'] = 'hidden'; $_SESSION['errors']['selBthMonth'] = 'hidden'; $_SESSION['errors']['txtBthDay'] = 'hidden'; $_SESSION['errors']['txtBthYear'] = 'hidden'; $_SESSION['errors']['txtEmail'] = 'hidden'; $_SESSION['errors']['txtPhone'] = 'hidden'; $_SESSION['errors']['chkReadTerms'] = 'hidden'; } ?> Now create index.php, and add the following code to it: <?php require_once ('index_top.php'); ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www. w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html > <head> <title>Degradable AJAX Form Validation with PHP and MySQL</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <link href="validate.css" rel="stylesheet" type="text/css" /> </head> <body onload="setFocus();"> <script type="text/javascript" src="json2.js"></script> <script type="text/javascript" src="xhr.js"></script> <script type="text/javascript" src="validate.js"></script> <fieldset> <legend class="txtFormLegend"> New User Registratio Form </legend> <br /> <form name="frmRegistration" method="post" action="validate.php"> <input type="hidden" name="validationType" value="php"/> <!-- Username --> <label for="txtUsername">Desired username:</label> <input id="txtUsername" name="txtUsername" type="text" onblur="validate(this.value, this.id)" value="<?php echo $_SESSION['values'] ['txtUsername'] ?>" /> <span id="txtUsernameFailed" class="<?php echo $_SESSION['errors']['txtUsername'] ?>"> This username is in use, or empty username field. </span> <br /> <!-- Name --> <label for="txtName">Your name:</label> <input id="txtName" name="txtName" type="text" onblur="validate(this.value, this.id)" value="<?php echo $_SESSION['values']['txtName'] ?>" /> <span id="txtNameFailed" class="<?php echo $_SESSION['errors']['txtName']?>"> Please enter your name. </span> <br /> <!-- Gender --> <label for="selGender">Gender:</label> <select name="selGender" id="selGender" onblur="validate(this.value, this.id)"> <?php buildOptions($genderOptions, $_SESSION['values']['selGender']); ?> </select> <span id="selGenderFailed" class="<?php echo $_SESSION['errors']['selGender'] ?>"> Please select your gender. </span> <br /> <!-- Birthday --> <label for="selBthMonth">Birthday:</label> <!-- Month --> <select name="selBthMonth" id="selBthMonth" onblur="validate(this.value, this.id)"> <?php buildOptions($monthOptions, $_SESSION['values']['selBthMonth']); ?> </select> &nbsp;-&nbsp; <!-- Day --> <input type="text" name="txtBthDay" id="txtBthDay" maxlength="2" size="2" onblur="validate(this.value, this.id)" value="<?php echo $_SESSION['values']['txtBthDay'] ?>" /> &nbsp;-&nbsp; <!-- Year --> <input type="text" name="txtBthYear" id="txtBthYear" maxlength="4" size="2" onblur="validate(document.getElementById ('selBthMonth').options[document.getElementById ('selBthMonth').selectedIndex].value + '#' + document.getElementById('txtBthDay').value + '#' + this.value, this.id)" value="<?php echo $_SESSION['values']['txtBthYear'] ?>" /> <!-- Month, Day, Year validation --> <span id="selBthMonthFailed" class="<?php echo $_SESSION['errors']['selBthMonth'] ?>"> Please select your birth month. </span> <span id="txtBthDayFailed" class="<?php echo $_SESSION['errors']['txtBthDay'] ?>"> Please enter your birth day. </span> <span id="txtBthYearFailed" class="<?php echo $_SESSION['errors']['txtBthYear'] ?>"> Please enter a valid date. </span> <br /> <!-- Email --> <label for="txtEmail">E-mail:</label> <input id="txtEmail" name="txtEmail" type="text" onblur="validate(this.value, this.id)" value="<?php echo $_SESSION['values']['txtEmail'] ?>" /> <span id="txtEmailFailed" class="<?php echo $_SESSION['errors']['txtEmail'] ?>"> Invalid e-mail address. </span> <br /> <!-- Phone number --> <label for="txtPhone">Phone number:</label> <input id="txtPhone" name="txtPhone" type="text" onblur="validate(this.value, this.id)" value="<?php echo $_SESSION['values']['txtPhone'] ?>" /> <span id="txtPhoneFailed" class="<?php echo $_SESSION['errors']['txtPhone'] ?>"> Please insert a valid US phone number (xxx-xxx-xxxx). </span> <br /> <!-- Read terms checkbox --> <input type="checkbox" id="chkReadTerms" name="chkReadTerms" class="left" onblur="validate(this.checked, this.id)" <?php if ($_SESSION['values']['chkReadTerms'] == 'on') echo 'checked="checked"' ?> /> I've read the Terms of Use <span id="chkReadTermsFailed" class="<?php echo$_SESSION['errors'] ['chkReadTerms'] ?>"> Please make sure you read the Terms of Use. </span> <!-- End of form --> <hr /> <span class="txtSmall">Note: All fields arerequired. </span> <br /><br /> <input type="submit" name="submitbutton" value="Register" class="left button" /> </form> </fieldset> </body> </html> Create a new file named allok.php, and add the following code to it: <?php // clear any data saved in the session session_start(); session_destroy(); ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html > <head> <title>AJAX Form Validation</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <link href="validate.css" rel="stylesheet" type="text/css" /> </head> <body> Registration Successful!<br /> <a href="index.php" title="Go back">&lt;&lt; Go back</a> </body> </html> Copy json2.js (which you downloaded in a previous exercise from http://json.org/json2.js) to your ajax/validate folder. Create a file named validate.js. This file performs the client-side functionality, including the AJAX requests: // holds the remote server address var serverAddress = "validate.php"; // when set to true, display detailed error messages var showErrors = true; // the function handles the validation for any form field function validate(inputValue, fieldID) { // the data to be sent to the server through POST var data = "validationType=ajax&inputValue=" + inputValue + "&fieldID=" + fieldID; // build the settings object for the XmlHttp object var settings = { url: serverAddress, type: "POST", async: true, complete: function (xhr, response, status) { if (xhr.responseText.indexOf("ERRNO") >= 0 || xhr.responseText.indexOf("error:") >= 0 || xhr.responseText.length == 0) { alert(xhr.responseText.length == 0 ? "Server error." : response); } result = response.result; fieldID = response.fieldid; // find the HTML element that displays the error message = document.getElementById(fieldID + "Failed"); // show the error or hide the error message.className = (result == "0") ? "error" : "hidden"; }, data: data, showErrors: showErrors }; // make a server request to validate the input data var xmlHttp = new XmlHttp(settings); } // sets focus on the first field of the form function setFocus() { document.getElementById("txtUsername").focus(); } Now it's time to add the server-side logic. Start by creating config.php, with the following code in it: <?php // defines database connection data define('DB_HOST', 'localhost'); define('DB_USER', 'ajaxuser'); define('DB_PASSWORD', 'practical'); define('DB_DATABASE', 'ajax'); ?> Now create the error handler code in a file named error_handler.php: <?php // set the user error handler method to be error_handler set_error_handler('error_handler', E_ALL); // error handler function function error_handler($errNo, $errStr, $errFile, $errLine) { // clear any output that has already been generated if(ob_get_length()) ob_clean(); // output the error message $error_message = 'ERRNO: ' . $errNo . chr(10) . 'TEXT: ' . $errStr . chr(10) . 'LOCATION: ' . $errFile . ', line ' . $errLine; echo $error_message; // prevent processing any more PHP scripts exit; } ?> The PHP script that handles the client's AJAX calls, and also handles the validation on form submit, is validate.php: <?php // start PHP session session_start(); // load error handling script and validation class require_once ('error_handler.php'); require_once ('validate.class.php'); // Create new validator object $validator = new Validate(); // read validation type (PHP or AJAX?) $validationType = ''; if (isset($_POST['validationType'])) { $validationType = $_POST['validationType']; } // AJAX validation or PHP validation? if ($validationType == 'php') { // PHP validation is performed by the ValidatePHP method, //which returns the page the visitor should be redirected to //(which is allok.php if all the data is valid, or back to //index.php if not) header('Location:' . $validator->ValidatePHP()); } else { // AJAX validation is performed by the ValidateAJAX method. //The results are used to form a JSON document that is sent //back to the client $response = array('result' => $validator->ValidateAJAX ($_POST['inputValue'],$_POST['fieldID']), 'fieldid' => $_POST['fieldID'] ); // generate the response if(ob_get_length()) ob_clean(); header('Content-Type: application/json'); echo json_encode($response); } ?>
Read more
  • 0
  • 0
  • 1615

article-image-working-binding-data-and-ui-elements-silverlight-4
Packt
10 May 2010
6 min read
Save for later

Working with Binding data and UI elements in Silverlight 4

Packt
10 May 2010
6 min read
Binding data to another UI element Sometimes, the value of the property of an element is directly dependent on the value of the property of another element. In this case, you can create a binding in XAML called an element binding or element-to-element binding . This binding links both values. If needed, the data can flow bidirectionally. In the banking application, we can add a loan calculator that allows the user to select an amount and the number of years in which they intend to pay the loan back to the bank, including (of course) a lot of interest. Getting ready To follow this recipe, you can either continue with your solution from the previous recipe or use the provided solution that can be found in the Chapter02/SilverlightBanking_ Element_Binding_Starter folder in the code bundle that is available on the Packt website. The finished application for this recipe can be found in the Chapter02/SilverlightBanking_Element_Binding_Completed folder. How to do it... To build the loan calculator, we'll use Slider controls. Each Slider is bound to a TextBlock using an element-to-element binding to display the actual value. Let's take a look at the steps we need to follow to create this binding: We will build the loan calculator as a separate screen in the application. Add a new child window called LoanCalculation.xaml. To do so, right-click on the Silverlight project in the Solution Explorer, select Add | New Item..., and choose Silverlight Child Window under Visual C#. Within MainPage.xaml, add a Click event on the LoanCalculationButton as shown in the following code: <Button x_Name="LoanCalculationButton" Click="LoanCalculationButton_Click" /> In the code-behind's event handler for this Click event, we can trigger the display of this new screen with the following code: private void LoanCalculationButton_Click(object sender, RoutedEventArgs e) { LoanCalculation loanCalculation = new LoanCalculation(); loanCalculation.Show(); } The UI of the LoanCalculation.xaml is quite simple—it contains two Slider controls. Each Slider control has set values for its Minimum and Maximum values (not all UI code is included here; the complete listing can be found in the finished sample code) as shown in the following code: <Slider x_Name="AmountSlider" Minimum="10000" Maximum="1000000" SmallChange="10000" LargeChange="10000" Width="300" > </Slider> <Slider x_Name="YearSlider" Minimum="5" Maximum="30" SmallChange="1" LargeChange="1" Width="300" UseLayoutRounding="True"> </Slider> As dragging a Slider does not give us proper knowledge of where we are exactly between the two values, we add two TextBlock controls. We want the TextBlock controls to show the current value of the Slider control, even while dragging. This can be done by specifying an element-to-element binding as shown in the following code: <TextBlock x_Name="AmountTextBlock" Text="{Binding ElementName=AmountSlider, Path=Value}"> </TextBlock> <TextBlock x_Name="MonthTextBlock" Text="{Binding ElementName=YearSlider, Path=Value}"> </TextBlock> Add a Button that will perform the actual calculation called CalculateButton and a TextBlock called PaybackTextBlock to show the results. This can be done using the following code: <Button x_Name="CalculateButton" Content="Calculate" Click="CalculateButton_Click"> </Button> <TextBlock x_Name="PaybackTextBlock"></TextBlock> The code for the actual calculation that is executed when the Calculate button is clicked uses the actual value for either the Slider or the TextBlock. This is shown in the following code: private double percentage = 0.0345; private void CalculateButton_Click(object sender, RoutedEventArgs e) { double requestedAmount = AmountSlider.Value; int requestedYears = (int)YearSlider.Value; for (int i = 0; i < requestedYears; i++) { requestedAmount += requestedAmount * percentage; } double monthlyPayback = requestedAmount / (requestedYears * 12); PaybackTextBlock.Text = "€" + Math.Round(monthlyPayback, 2); } Having carried out the previous steps, we now have successfully linked the value of the Slider controls and the text of the TextBlock controls. The following screenshot shows the LoanCalculation.xaml screen as it is included in the finished sample code containing some extra markup: How it works... An element binding links two properties of two controls directly from XAML. It allows creating a Binding where the source object is another control. For this to work, we need to create a Binding and specify the source control using the ElementName property. This is shown in the following code: <TextBlock Text="{Binding ElementName=YearSlider, Path=Value}" > </TextBlock> Element bindings were added in Silverlight 3. Silverlight 2 did not support this type of binding. There's more... An element binding can also work in both directions, that is, from source to target and vice versa. This can be achieved by specifying the Mode property on the Binding and setting it to TwoWay. The following is the code for this. In this code, we replaced the TextBlock by a TextBox. When entering a value in the latter, the Slider will adjust its position: <TextBox x_Name="AmountTextBlock" Text="{Binding ElementName=AmountSlider, Path=Value, Mode=TwoWay}" > </TextBox> Element bindings without bindings Achieving the same effect in Silverlight 2—which does not support this feature—is also possible, but only through the use of an event handler as shown in the following code. Element bindings eliminate this need: private void AmountSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { AmountSlider.Value = Math.Round(e.NewValue); AmountTextBlock.Text = AmountSlider.Value.ToString(); } See also Element-to-element bindings can be easily extended to use converters. For more information on TwoWay bindings, take a look at the Using the different modes of data binding to allow persisting data recipe in this article. Binding collections to UI elements Often, you'll want to display lists of data in your application such as a list of shopping items, a list of users, a list of bank accounts, and so on. Such a list typically contains a bunch of items of a certain type that have the same properties and need to be displayed in the same fashion. We can use data binding to easily bind a collection to a Silverlight control (such as a ListBox or DataGrid) and use the same data binding possibilities to defi ne how every item in the collection should be bound. This recipe will show you how to achieve this. Getting ready For this recipe, you can fi nd the starter solution in the Chapter02/SilverlightBanking_ Binding_Collections_Starter folder and the completed solution in the Chapter02/SilverlightBanking_Binding_Collections_Completed folder in the code bundle that is available on the Packt website.
Read more
  • 0
  • 0
  • 1579

article-image-data-binding-expression-blend-4-silverlight-4
Packt
13 May 2010
7 min read
Save for later

Data binding from Expression Blend 4 in Silverlight 4

Packt
13 May 2010
7 min read
Using the different modes of data binding to allow persisting data Until now, the data has flowed from the source to the target (the UI controls). However, it can also flow in the opposite direction, that is, from the target towards the source. This way, not only can data binding help us in displaying data, but also in persisting data. The direction of the flow of data in a data binding scenario is controlled by the Mode property of the Binding. In this recipe, we'll look at an example that uses all the Mode options and in one go, we'll push the data that we enter ourselves to the source. Getting ready This recipe builds on the code that was created in the previous recipes, so if you're following along, you can keep using that codebase. You can also follow this recipe from the provided start solution. It can be found in the Chapter02/SilverlightBanking_Binding_ Modes_Starter folder in the code bundle that is available on the Packt website. The Chapter02/SilverlightBanking_Binding_Modes_Completed folder contains the finished application of this recipe. How to do it... In this recipe, we'll build the "edit details" window of the Owner class. On this window, part of the data is editable, while some isn't. The editable data will be bound using a TwoWay binding, whereas the non-editable data is bound using a OneTime binding. The Current balance of the account is also shown—which uses the automatic synchronization—based on the INotifyPropertyChanged interface implementation. This is achieved using OneWay binding. The following is a screenshot of the details screen: Let's go through the required steps to work with the different binding modes: Add a new Silverlight child window called OwnerDetailsEdit.xaml to the Silverlight project. In the code-behind of this window, change the default constructor—so that it accepts an instance of the Owner class—as shown in the following code: private Owner owner; public OwnerDetailsEdit(Owner owner) { InitializeComponent(); this.owner = owner; } In MainPage.xaml, add a Click event on the OwnerDetailsEditButton: <Button x_Name="OwnerDetailsEditButton" Click="OwnerDetailsEditButton_Click" > In the event handler, add the following code, which will create a new instance of the OwnerDetailsEdit window, passing in the created Owner instance: private void OwnerDetailsEditButton_Click(object sender, RoutedEventArgs e) { OwnerDetailsEdit ownerDetailsEdit = new OwnerDetailsEdit(owner); ownerDetailsEdit.Show(); } The XAML of the OwnerDetailsEdit is pretty simple. Take a look at the completed solution (Chapter02/SilverlightBanking_Binding_Modes_Completed)for a complete listing. Don't forget to set the passed Owner instance as the DataContext for the OwnerDetailsGrid. This is shown in the following code: OwnerDetailsGrid.DataContext = owner; For the OneWay and TwoWay bindings to work, the object to which we are binding should be an instance of a class that implements the INotifyPropertyChanged interface. In our case, we are binding an Owner instance. This instance implements the interface correctly. The following code illustrates this: public class Owner : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; ... } Some of the data may not be updated on this screen and it will never change. For this type of binding, the Mode can be set to OneTime. This is the case for the OwnerId field. The users should neither be able to change their ID nor should the value of this field change in the background, thereby requiring an update in the UI. The following is the XAML code for this binding: <TextBlock x_Name="OwnerIdValueTextBlock" Text="{Binding OwnerId, Mode=OneTime}" > </TextBlock> The CurrentBalance TextBlock at the bottom does not need to be editable by the user (allowing a user to change his or her account balance might not be benefi cial for the bank), but it does need to change when the source changes. This is the automatic synchronization working for us and it is achieved by setting the Binding to Mode=OneWay. This is shown in the following code: <TextBlock x_Name="CurrentBalanceValueTextBlock" Text="{Binding CurrentBalance, Mode=OneWay}" > </TextBlock> The final option for the Mode property is TwoWay. TwoWay bindings allow us to persist data by pushing data from the UI control to the source object. In this case, all other fields can be updated by the user. When we enter a new value, the bound Owner instance is changed. TwoWay bindings are illustrated using the following code: <TextBox x_Name="FirstNameValueTextBlock" Text="{Binding FirstName, Mode=TwoWay}" > </TextBox> We've applied all the different binding modes at this point. Notice that when you change the values in the pop-up window, the details on the left of the screen are also updated. This is because all controls are in the background bound to the same source object as shown in the following screenshot: How it works... When we looked at the basics of data binding, we saw that a binding always occurs between a source and a target. The first one is normally an in-memory object, but it can also be a UI control. The second one will always be a UI control. Normally, data flows from source to target. However, using the Mode property, we have the option to control this. A OneTime binding should be the default for data that does not change when displayed to the user. When using this mode, the data flows from source to target. The target receives the value initially during loading and the data displayed in the target will never change. Quite logically, even if a OneTime binding is used for a TextBox, changes done to the data by the user will not flow back to the source. IDs are a good example of using OneTime bindings. Also, when building a catalogue application, OneTime bindings can be used, as we won't change the price of the items that are displayed to the user (or should we...?). We should use a OneWay binding for binding scenarios in which we want an up-to-date display of data. Data will flow from source to target here also, but every change in the values of the source properties will propagate to a change of the displayed values. Think of a stock market application where updates are happening every second. We need to push the updates to the UI of the application. The TwoWay bindings can help in persisting data. The data can now flow from source to target, and vice versa. Initially, the values of the source properties will be loaded in the properties of the controls. When we interact with these values (type in a textbox, drag a slider, and so on), these updates are pushed back to the source object. If needed, conversions can be done in both directions. There is one important requirement for the OneWay and TwoWay bindings. If we want to display up-to-date values, then the INotifyPropertyChanged interface should be implemented. The OneTime and OneWay bindings would have the same effect, even if this interface is not implemented on the source. The TwoWay bindings would still send the updated values if the interface was not implemented; however, they wouldn't notify about the changed values. It can be considered as a good practice to implement the interface, unless there is no chance that the updates of the data would be displayed somewhere in the application. The overhead created by the implementation is minimal. There's more... Another option in the binding is the UpdateSourceTrigger. It allows us to specify when a TwoWay binding will push the data to the source. By default, this is determined by the control. For a TextBox, this is done on the LostFocus event; and for most other controls, it's done on the PropertyChanged event. The value can also be set to Explicit. This means that we can manually trigger the update of the source. BindingExpression expression = this.FirstNameValueTextBlock. GetBindingExpression(TextBox.TextProperty); expression.UpdateSource(); See also Changing the values that flow between source and target can be done using converters. Data binding from Expression Blend 4 While creating data bindings is probably a task mainly reserved for the developer(s) in the team, Blend 4—the design tool for Silverlight applications—also has strong support for creating and using bindings. In this recipe, we'll build a small data-driven application that uses data binding. We won't manually create the data binding expressions; we'll use Blend 4 for this task.
Read more
  • 0
  • 0
  • 1534
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at $15.99/month. Cancel anytime
article-image-aspnet-social-networks-blogs-fisharoo
Packt
27 Oct 2009
8 min read
Save for later

ASP.NET Social Networks—Blogs in Fisharoo

Packt
27 Oct 2009
8 min read
Problem This article, as stated in Introduction, is all about adding the Blogging feature to our site. This will handle creating and managing a post. It will also handle sending alerts to your friends' filter page. And finally we will handle creating a friendly URL for your blog posts. Here we are making our first post to our blog: Once our post is created, we will then see it on the Blogs homepage and the My Posts section. From here we can edit the post or delete it. Also, we can click into the post to view what we have seen so far. The following screenshot shows what one will see when he/she clicks on the post: I have the blog post set up to show the poster's avatar. This is a feature that you can easily add to or remove. Most of your users want to be able to see who the author is that they are currently reading! Also, we will add a friendly URL to our blog post's pages. Design The design of this application is actually quite simple. We will only need one table to hold our blog posts. After that we need to hook our blog system into our existing infrastructure. Blogs In order for us to store our blog, we will need one simple table. This table will handle all the standard attributes of a normal blog post to include the title, subject, page name, and the post itself. It has only one relationship out to the Accounts table so that we know who owns the post down the road. That's it! Solution Let's take a look at the solution for these set of features. Implementing the database Let's take a look at the tables required by our solution. Blogs The blogs table is super simple. We discussed most of this under the Blogs section. The one thing that is interesting here is the Post column. Notice that I have this set to a varchar(MAX) field. This may be too big for your community, so feel free to change it down the road. For my community I am not overly worried. I can always add a UI restriction down the road without impacting my database design using a validation control. After that we will look at the IsPublished flag. This flag tells the system whether or not to show the post in the public domain. Next to that we will also be interested in the PageName column. This column is what we will display in the browser's address bar. As it will be displayed in the address bar, we need to make sure that the input is clean so that we don't have parsing issues (responsible for causing data type exceptions) down the road. We will handle that on the input side in our presenter later. Creating the relationships Once all the tables are created, we can then create all the relationships. For this set of tables we have relationships between the following tables: Blogs and Accounts Setting up the data access layer To set up the data access layer follow the steps mentioned next: Open the Fisharoo.dbml file. Open up your Server Explorer window. Expand your Fisharoo connection. Expand your tables. If you don't see your new tables try hitting the Refresh icon or right-clicking on tables and clicking Refresh. Then drag your new tables onto the design surface. Hit Save and you should now have the following domain objects to work with! Keep in mind that we are not letting LINQ track our relationships, so go ahead and delete them from the design surface. Your design surface should have all the same items as you see in the screenshot (though perhaps in a different arrangement!). Building repositories With the addition of new tables will come the addition of new repositories so that we can get at the data stored in those tables. We will be creating the following repository to support our needs. BlogRepository Our repository will generally have a method for select by ID, select all by parent ID, save, and delete. We will start with a method that will allow us to get at a blog by its page name that we can capture from the browser's address bar. public Blog GetBlogByPageName(string PageName, Int32 AccountID){Blog result = new Blog();using(FisharooDataContext dc = _conn.GetContext()){result = dc.Blogs.Where(b => b.PageName == PageName &&b.AccountID == AccountID).FirstOrDefault();}return result;} Notice that for this system to work we can only have one blog with one unique page name. If we forced our entire community to use unique page names across the community, we would eventually have some upset users. We want to make sure to enforce unique page names across users only for this purpose. To do this, we require that an AccountID be passed in with the page name, which gives our users more flexibility with their page name overlaps! I will show you how we get the AccountID later. Other than that we are performing a simple lambda expression to select the appropriate blog out of the collection of blogs in the data context. Next, we will discuss a method to get all the latest blog posts via the GetLatestBlogs() method. This method will also get and attach the appropriate Account for each blog. Before we dive into this method, we will need to extend the Blog class to have an Account property. To extend the Blog class we will need to create a public partial class in the Domain folder. using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace Fisharoo.FisharooCore.Core.Domain{ public partial class Blog { public Account Account { get; set; } }} Now we can look at the GetLatestBlogs() method. public List<Blog> GetLatestBlogs(){ List<Blog> result = new List<Blog>(); using(FisharooDataContext dc = _conn.GetContext()) { IEnumerable<Blog> blogs = (from b in dc.Blogs where b.IsPublished orderby b.UpdateDate descending select b).Take(30); IEnumerable<Account> accounts = dc.Accounts.Where(a => blogs.Select(b => b.AccountID).Distinct().Contains(a.AccountID)); foreach (Blog blog in blogs) { blog.Account = accounts.Where(a => a.AccountID == blog.AccountID).FirstOrDefault(); } result = blogs.ToList(); result.Reverse(); } return result;} The first expression in this method gets the top N blogs ordered by their UpdateDate in descending order. This gets us the newest entries. We then add a where clause looking for only blogs that are published. We then move to getting a list of Accounts that are associated with our previously selected blogs. We do this by selecting a list of AccountIDs from our blog list and then doing a Contains search against our Accounts table. This gives us a list of accounts that belong to all the blogs that we have in hand. With these two collections in hand we can iterate through our list of blogs and attach the appropriate Account to each blog. This gives us a full listing of blogs with accounts. As we discussed earlier, it is very important for us to make sure that we keep the page names unique on a per user basis. To do this we need to have a method that allows our UI to determine if a page name is unique or not. To do this we will have the CheckPageNameIsUnique() method. public bool CheckPageNameIsUnique(Blog blog){ blog = CleanPageName(blog); bool result = true; using(FisharooDataContext dc = _conn.GetContext()) { int count = dc.Blogs.Where(b => b.PageName == blog.PageName && b.AccountID == blog.AccountID).Count(); if(count > 0) result = false; } return result;} This method looks at all the blog entries except itself to determine if there are other blog posts with the same page name that are also by the same Account. This allows us to effectively lock down our users from creating duplicate page names. This will be important down the road when we start to discuss our pretty URLs. Next, we will look at a private method that will help us clean up these page name inputs. Keep in mind that these page names will be displayed in the browser's address bar and therefore need not have any characters in them that the browser would want to encode. While we can decode the URL easily, this conversation is more about keeping the URL pretty so that the user and search engine spiders can easily read where they are at. When we have characters in the URL that are encoded, we will end up with something like %20 where %20 is the equivalent to a space. But to read my%20blog%20post is not that easy. It is much easier to ready my-blog-post. So we will strip out all of our so called special characters and replace all spaces with hyphens. This method will be the CleanPageName() method. private Blog CleanPageName(Blog blog){ blog.PageName = blog.PageName.Replace(" ", "-").Replace("!", "") .Replace("&", "").Replace("?", "").Replace(",", ""); return blog;} You can add to this as many filters as you like. For the time being I am replacing the handful of special characters that we have just seen in the code. Next, we will get into the service layers that we will use to handle our interactions with the system.
Read more
  • 0
  • 0
  • 1358