• Create a Recommendation box similar to LinkedIn using asp.net

    If you are a member of LinkedIn (like me) you probably know the recommendation box at the upper right. It shows you some people you might know (according to LinkedIns algorithms). I like this plugin because it’s easy to use. As I needed to add a similar functionality to a new project I’ve investigated a bit and here is how to add such a plugin to your own aspx.net website.

    Requirements

    First let’s write down the requirements:

    • Box should display a photo and some text.
    • The profile page should be opened if the user clicks on the photo or text.
    • User should be able to skip proposals if they don’t know them.
    • Skiipped users should be logged so they won’t be proposed for the same user again.
    • License of involved plugins is important. It need not be freeware or open-source, but it should be usable in multiple projects without additional charge

    Additionally, as this will be implemented into an existing project, the use of jQuery and Bootstrap is fine because it’s already in use in my project. For current article I removed all other resources I use in my project (e.g. jQuery UI) because they are not needed for current example. Even though e.g. the formatting is quite ok you should of course design the plugin according to your already in-use techniques.

     

    The Slider

    Instead of just removing a user I of course want to have some slide effects. Therefore first I checked for some existing slider solutions. As said, sliders based on jQuery are fine. Ah yes, there are hundreds of sliders…. I checked CodeCanyon, Github and jQuery. After testing some of them I decided to use bxSlider from Steven Wanderski. It’s released on Github under MIT License so it could be used whereever you want. Steven has some examples and a well documented list of options. Additionally the github repository was last updated 4 months ago so it’s up-to-date.

     

    Actions

    User has 2 options when a profile is displayed:

    1. Show user profile, or
    2. confirm that this profile should be ignored.

    First one is easy: Just open the profile page of the displayed user. Then current user could decide whether he wants to contact this user or not. Of course it would also be possible to add a ‘Connect now’-Button already to the profile displayed by current plugin. I don’t need this in my project, but with the technique described below it would be easy for you to add it on your own.

    So on to the second option: Ignore this user. This should not only show the next profile but of course it should store this information so that the currently displayed user profile should never be displayed for the current user again. Nothing could be more boring than ‘ignoring’ the same user again and again… Of course we don’t want a postback to the server as this would be time-consuming. Instead we use a webservice. So the bxSlider needs to provide a unique id to the webservices.

    Get unique ID

    bxSlider has an option ‘getCurrentSlideElement’ but unfortunately this does not seem to work well when called in action ‘onSlideBefore’. But thanks to the data-attribute of HTML5 it’s very easy to get the unique ID. bxSliders ‘onSlideBefore’ supplies 3 parameters: $slideElement, oldIndex and newIndex. $slideElement contains the next element to be displayed, so this does not help because we need the current / previous element. oldIndex contains the index of this previous element so that’s what we need. We add the data-attribute to the LI-Element and then grab it via oldIndex. This script, embedded in onSlideBefore, contains the value of the data-id attribute of the person to be ignored:

     

    var id = $('ul.bxslider li:nth-child(' + (oldIndex + 1) + ')').data('id');

     

     

    Take care: oldIndex is 0-based while nth-child starts at 1 so we need to increase oldIndex by 1 to get the correct nth child. So now that we have the id we could just call the webservice and supply the ID.

    Update bxSlider CSS

    While playing around I noticed that the text is not wrapped correct if the width of the slider is very small. As you see in the screenshot below, large words will not be displayed correct but appear on the next slide. Therefore I changed the css of bxslider according to my needs. I just added min-width: 200px; for class definition of .bx-viewport. This fits my needs but of course you should take care for your solution on your own.

    This is the End

    Next thing to decide: What to do at the end of the slider? Typically you don’t load thousands of profiles as this will waste bandwidth. But if you only show e.g. 4 profiles it’s very likely that the user reaches the end. So what to do now? First I’ve setup bxSlider to do no endless loop. So you can’t scroll any further if you reached the end. You would have different options now, e.g. show ‘Thank you, that’s it’ or load some more profiles to display. I’ve chosen the easiest option: Display ‘You’re done. Please reload to refresh’. So the user could click there and just reload the whole page. While reloading also the plugin will be refreshed with new data because we stored the users to be ignored and therefore new users will be retrieved. Of course you could make it more elegant and reload the data using a webservice but that’s not important for my current project so it’s up to you.

     

    The webpage

    The code should be easy to understand. Basically I have a Repeater-Control for the design. This Repeater is filled with some example data in ‘CreateDataTable’. Of course you should retrieve the data from your database later on. The Footer also contains the element which should be shown as last element when all others are processed.

    Please also note the span-class ‘slider-next’ which shows the next profile when the user clicks it. I’ve defined it as a class instead of id so you could also add it into your repeater-control if you want to have the ‘Ignore’-Link on each profile instead of just below the box.

    The script

    On the webpage you also find 2 scripts. The first one initializes the bxSlider with some options, e.g. we don’t want to show the page numbers. The onSlideBefore retrieves the unique userid as explained above and the calls the procedure IgnoreThisUser, supplying the userid. IgnoreThisUser then calls the webservice.

     

    The webservice

    Webservice ‘Webservice1.asmx’ within subdirectory ‘Webservices’ has just one method ‘ProcessID’ which expects ‘id’ as String-Parameter. This is the unique ID of the user who should not be displayed for current user anymore so you should add your code there, e.g. store this info in your database.

     

    That’s all

    So now you should have a recommendation box similar to the one from LinkedIn

     

     

    jquery.bxslider.css

    .bx-wrapper .bx-viewport {
        -moz-box-shadow: 0 0 5px #ccc;
        -webkit-box-shadow: 0 0 5px #ccc;
        box-shadow: 0 0 5px #ccc;
        border5px solid #fff;
        left: -5px;
        background: #fff;
        min-width: 200px;
       

     

    Recommendation.aspx

     

    <%@ Page Language="vb" AutoEventWireup="false" CodeBehind="Recommendation.aspx.vb"
        Inherits="WebApplication1.Recommendation" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <head runat="server">
        <script src="//code.jquery.com/jquery-1.11.0.min.js" type="text/javascript"></script>
        <!-- bxSlider Javascript file -->
        <script src="/js/jquery.bxslider.js"></script>
        <!-- bxSlider CSS file -->
        <link href="/css/jquery.bxslider.css" rel="stylesheet" />
        <link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
        <title>Recommendation</title>
    </head>
    <body>
    <h1>Recommendation box</h1>
        <div class="col-lg-3 col-md-3 col-sm-4 col-xs-6">
            <asp:Repeater ID="MyRepeater" runat="server">
                <HeaderTemplate>
                    <ul class="bxslider">
                </HeaderTemplate>
                <ItemTemplate>
                    <li data-id='<%# Eval("UserID") %>'><a href='<%# Eval("TargetURL") %>' target='_blank'>
                        <span class="col-lg-6 col-md-6 col-xs-6 col-sm-6">
                            <img style='height: 100%; width: 100%;' src='<%# Eval("ImageURL") %>' class='img-rounded'
                                id='ShowTopNavi_CurrentUserImage' />
                        </span><span class='col-lg-6 col-md-6 col-xs-6 col-sm-6'>
                            <p class='text-left'>
                                <strong>
                                    <%# Eval("Username")%></strong></p>
                            <p class='text-left small'>
                                <%# Eval("AdditionalInfo")%></p>
                        </span></a></li>
                </ItemTemplate>
                <FooterTemplate>
                    <li data-id='Restart'><a href='Recommendation.aspx'><span class="col-lg-6 col-md-6 col-xs-6 col-sm-6">
                    </span><span class='col-lg-6 col-md-6 col-xs-6 col-sm-6'>
                        <p class='text-left'>
                            <strong>Restart</strong></p>
                        <p class='text-left small'>
                            Or implement any other action if you like</p>
                    </span></a></li>
                    </ul>
                </FooterTemplate>
            </asp:Repeater>
            <span class="slider-next"></span>
        </div>
        <script type="text/javascript">
            $(document).ready(function () {
                $('.bxslider').bxSlider({
                    infiniteLoop: false,
                    hideControlOnEnd: true,
                    pager: false,
                    nextSelector: '.slider-next',
                    nextText: 'Skip this user →',
                    onSlideBefore: function ($slideElement, oldIndex, newIndex) {
                        var id = $('ul.bxslider li:nth-child(' + (oldIndex + 1) + ')').data('id');
                        IgnoreThisUser(id);
                    }
                });
            });
            function IgnoreThisUser(key) {
                $.ajax({
                    type: "POST",
                    url: '/Webservices/Webservice1.asmx/ProcessID',
                    contentType: "application/json; charset=utf-8",
                    dataType: "json",
                    data: JSON.stringify({
                        'id': key
                    })
                });
            }
        </script>
    </body>
    </html>

    Recommendation.aspx.vb

     

    Public Class Recommendation
        Inherits System.Web.UI.Page
        Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
            If Not Page.IsPostBack Then
                MyRepeater.DataSource = CreateDatatable()
                MyRepeater.DataBind()
            End If
        End Sub
        Private Function CreateDatatable() As DataTable
            Dim dt As New DataTable
            dt.Columns.Add("UserID")
            dt.Columns.Add("Username")
            dt.Columns.Add("ImageURL")
            dt.Columns.Add("AdditionalInfo")
            dt.Columns.Add("TargetURL")
            Dim row As DataRow
            row = dt.NewRow
            row("UserID") = "ZuckerbergM"
            row("Username") = "Mark Zuckerberg"
            row("AdditionalInfo") = "Co-Founder of Facebook"
            row("TargetURL") = "https://en.wikipedia.org/wiki/Mark_Zuckerberg"
            dt.Rows.Add(row)
            row = dt.NewRow
            row("UserID") = "MuskE"
            row("Username") = "Elon Musk"
            row("AdditionalInfo") = "CEO of SpaceX and Tesla Motors"
            row("TargetURL") = "https://en.wikipedia.org/wiki/Elon_Musk"
            dt.Rows.Add(row)
            row = dt.NewRow
            row("UserID") = "GatesB"
            row("Username") = "Bill Gates"
            row("AdditionalInfo") = "Co-Founder of Microsoft"
            row("TargetURL") = "https://en.wikipedia.org/wiki/Bill_Gates"
            dt.Rows.Add(row)
            row = dt.NewRow
            row("UserID") = "DorseyJ"
            row("Username") = "Jack Dorsay"
            row("AdditionalInfo") = "Co-Founder of Twitter"
            row("TargetURL") = "https://en.wikipedia.org/wiki/Jack_Dorsey"
            dt.Rows.Add(row)
            Return dt
        End Function
    End Class

     WebService1.asmx.vb

    Imports System.Web.Services
    Imports System.Web.Services.Protocols
    Imports System.ComponentModel
    ' To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
    <System.Web.Script.Services.ScriptService()> _
    <System.Web.Services.WebService(Namespace:="http://tempuri.org/")> _
    <System.Web.Services.WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
    <ToolboxItem(False)> _
     Public Class WebService1
        Inherits System.Web.Services.WebService
        <WebMethod()> _
        Public Sub ProcessID(id As String)
            'Place your code here!
        End Sub
    End Class

     

Leave a comment

If you want to share your opinion, leave a comment.

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">