Thursday, March 22, 2012

Full calendar - saving event using ajax / pagemethod/ webmethod in asp.net

In this post we will be saving the event in a full calendar plugin using jQuery ajax and asp.net pagemethod or web service. To go through the post I request you to first check the following post. We are going the proceed forward with the same post and its source code.

http://growingtech.blogspot.in/2012/02/full-calendar-with-json-data-source.html

In addition to the previous script and CSS file references, we have added one more script file for this purpose-
<script src="https://github.com/douglascrockford/JSON-js/raw/master/json2.js" type="text/javascript"></script>
This file is used hare to to JSON stringify the event object.

Now how we are going to solve this issue is that on click of a day, we are going popup an entry form for the event and we will use ajax to save the event to the server and after successful saving we will be adding the event to the calender. Now calender plugin has some event to solve this purpose-
  1. dayClick - this event happen during the mouse click of a day.
  2. renderEvent - using this event we can add an event to the calender. We can set stick to true to permanently add the event to the calender.
For popup we are going to use jQuery dialog. For adding the event we can add the following HTML-
<div id="eventToAdd" style="display: none; font-size: 12px;">
    Event name:
    <input id="eventName" type="text" /><br />
    Event start date:
    <input id="eventStartDate" type="text" />(MM-dd-yyyy)<br />
    Event end date:
    <input id="eventEndDate" type="text" />(MM-dd-yyyy)<br />
</div>
Now we need to show popup dialog for adding event on the day click of the calendar day. To do so we will take the help of the dayClick event of the calender and it goes here-
<script type="text/javascript">
    $('div[id*=fullcal]').fullCalendar({
        dayClick: function (dateSelected, allDay, jsEvent, view) {
            $("#eventToAdd").dialog(
            {
                title: "Add event",
                modal: true,
                buttons: {
                    "Add": function () {
                       //event adding logic goes here                 
                    }
                }
            });
        }
    });
</script>
Now on click of add button in the dialog we need to do two things. First save the event data using jQuery ajax and page method / web service. And secondly on the success of the ajax call add the event on the calender. Both the work is done here-
"Add": function () {
    var event = new Object(), eventToSave = new Object(); ;
    eventToSave.EventID = event.id = Math.floor(200 * Math.random());
    event.start = new Date($("#eventToAdd #eventStartDate").val());
    eventToSave.StartDate = $("#eventToAdd #eventStartDate").val();
    if ($("#eventToAdd #eventEndDate").val() == "") {
        event.end = event.start;
        eventToSave.EndDate = eventToSave.StartDate;
    }
    else {
        event.end = new Date($("#eventToAdd #eventEndDate").val());
        eventToSave.EndDate = $("#eventToAdd #eventEndDate").val();
    }
    eventToSave.EventName = event.title = $("#eventToAdd #eventName").val();

    $("#eventToAdd input").val("");
    $.ajax({
        type: "POST",
        contentType: "application/json",
        data: "{eventdata:" + JSON.stringify(eventToSave) + "}",
        url: "FullcalenderwithPagemethod.aspx/AddEvents",
        dataType: "json",
        success: function (data) {
            $('div[id*=fullcal]').fullCalendar('renderEvent', event, true);
            $("#eventToAdd").dialog("close");
        },
        error: function (XMLHttpRequest, textStatus, errorThrown) {
            debugger;
        }
    });
}
What we are doing here is in line 2 we are creating two JavaScript objects event and eventToSave and line 3-14 we are assigning the values from the popup. And finally we are saving the data to server using ajax call. Why we have taken two objects here is event data of the calender has different property name and the server event have different name. Once the saving of event is successful we are adding the event object to the calendar using line number 24. The page method using for this is as follows-
[System.Web.Services.WebMethod]
public static bool AddEvents(Event eventdata)
{
    List events;
    if (HttpContext.Current.Session["events"] != null)
        events = (List)HttpContext.Current.Session["events"];
    else
        events = new List();
    events.Add(eventdata);
    HttpContext.Current.Session["events"] = events;
    return true;
}
For this example we have used in memory event object. We can easily replace it with proper database driven. The complete modified code for this is available here for download.

Monday, March 19, 2012

Dynamic galleriffic using pagemethod / webmethod in asp.net

In this post we are going to explore using Galleriffic jQuery plugin with asp.net web method/page method /web service. Also we will see how to change the image source dynamically. The detail of the plugin available here. First lets quickly go through the plugin usage-

First of all we need to add the following script files for working with Galleriffic-
<link rel="stylesheet" href="http://www.twospy.com/galleriffic/css/basic.css" type="text/css" />
    <link rel="stylesheet" href="http://www.twospy.com/galleriffic/css/galleriffic-2.css"
        type="text/css" />
    <script type="text/javascript" src="http://www.twospy.com/galleriffic/js/jquery-1.3.2.js"></script>
    <script type="text/javascript" src="http://www.twospy.com/galleriffic/js/jquery.galleriffic.js"></script>
    <script type="text/javascript" src="http://www.twospy.com/galleriffic/js/jquery.opacityrollover.js"></script>
The opacityrollover js file is optional. And also if we want we can define our own CSS files.

The HTML needed for the plugin is as follows-
<div id="container">
    <div id="gallery" class="content">
        <div id="controls" class="controls">
        </div>
        <div class="slideshow-container">
            <div id="loading" class="loader">
            </div>
            <div id="slideshow" class="slideshow">
            </div>
        </div>
        <div id="caption" class="caption-container">
        </div>
    </div>
    <div id="thumbs" class="navigation">
        <ul class="thumbs noscript" id="containerID">
            <li>
                <a class="thumb" name="drop" href="http://www.gallery2c.com/admin/Upload/FullImage/moda01.jpg"
                    title="Title #1">
                    <img src="http://www.gallery2c.com/admin/Upload/ThumbNail/moda01.jpg" alt="Title #1" />
                </a>
                <div class="caption">
                    Any html can be placed here ...
                </div>
            </li>
            .
            .
            .
            .
            .
        </ul>
    </div>
    <div style="clear: both;">
    </div>
</div>
Lets explain the HTML a little more. Line 3-4 is used for adding buttons of the slideshow(Play/Stop slideshow, Next image, previous image). Line 5-10 is used for showing the big image where line 6-7 is used to show the loading effect while the image is getting loaded and line 8-9 is used to show the actual image. And line 11-12 is used to show any additional information for the image. And the line 14-30 is used to add the image information. In that UL represent the image container. And each LI correspond to a image. It contains many information. Image tag in the LI represent the thumbnail image and the href of the container anchor represent the image that will be shown as a big image. Finally the div with class caption can be used as a container of additional information. The additional information can be of type HTML.

We are also going to use an additional select box as an option to change the data source for the plugin. The code goes here-
<select id="changeGallery">
    <option value="gett" selected="selected">Gallery 1</option>
    <option value="moda">Gallery 2</option>
</select>
Now to activate the plugin with rollover we call use the following code block-
var onMouseOutOpacity = 0.67;
$('#thumbs ul.thumbs li').opacityrollover({
    //add all option here
});

// Initialize Advanced Galleriffic Gallery
var gallery = $('#thumbs').galleriffic({
    //add all option here
});
So for enabling the Galleriffic we are going to use the following code-
function attachGallery() {
    var onMouseOutOpacity = 0.67;
    $('#thumbs ul.thumbs li').opacityrollover({
        mouseOutOpacity: onMouseOutOpacity,
        mouseOverOpacity: 1.0,
        fadeSpeed: 'fast',
        exemptionSelector: '.selected'
    });

    galleryVar = $('#thumbs').galleriffic({
        delay: 2500,
        numThumbs: 15,
        preloadAhead: 10,
        enableTopPager: true,
        enableBottomPager: true,
        maxPagesToShow: 7,
        imageContainerSel: '#slideshow',
        controlsContainerSel: '#controls',
        captionContainerSel: '#caption',
        loadingContainerSel: '#loading',
        renderSSControls: true,
        renderNavControls: true,
        playLinkText: 'Play Slideshow',
        pauseLinkText: 'Pause Slideshow',
        prevLinkText: '&lsaquo; Previous Photo',
        nextLinkText: 'Next Photo &rsaquo;',
        nextPageLinkText: 'Next &rsaquo;',
        prevPageLinkText: '&lsaquo; Prev',
        enableHistory: false,
        autoStart: false,
        syncTransitions: true,
        defaultTransitionDuration: 900,
        onSlideChange: function (prevIndex, nextIndex) {
            this.find('ul.thumbs').children()
     .eq(prevIndex).fadeTo('fast', onMouseOutOpacity).end()
     .eq(nextIndex).fadeTo('fast', 1.0);
        },
        onPageTransitionOut: function (callback) {
            this.fadeTo('fast', 0.0, callback);
        },
        onPageTransitionIn: function () {
            this.fadeTo('fast', 1.0);
        }
    });
}
Now to implement the dynamic Galleriffic, lets remove all the LI's form the UL container. And let add a hidden block of LI that we will be using as a template. The code block is as follows-
<div id="itemContainer" style="display: none">
    <li><a class="thumb" name="drop" href="http://www.gallery2c.com/admin/Upload/FullImage/moda01.jpg"
        title="Title #1">
        <img src="http://www.gallery2c.com/admin/Upload/ThumbNail/moda01.jpg" alt="Title #1" />
    </a>
        <div class="caption">
            Any html can be placed here ...
        </div>
    </li>
</div>
Now to make it database driven we need to do the following-
  1. Clone the hidden LI
  2. Change HREF and TITLE of the anchor tag using database values
  3. Change SRC and ALT of the image tag using database values
  4. Change the caption div inner HTML with database value
  5. Append the cloned LI to the container UL
  6. Repeat the process for all images
Now let us have some asp.net page method or web method that will return images that need to be added dynamically. It goes here-
[WebMethod]
public static object GetImageDetail(string id)
{
    var imageData = new { ImageID = 0, URL = string.Format("http://www.gallery2c.com/admin/Upload/FullImage/{0}01.jpg",id), Thumb = string.Format("http://www.gallery2c.com/admin/Upload/ThumbNail/{0}01.jpg",id), Title = "Title 0", Caption = "Caption 0" };
    var imageDataList = (new[] { imageData }).ToList();
    for (int i = 2; i < 10; i++)
        imageDataList.Add(new { ImageID = 0, URL = string.Format("http://www.gallery2c.com/admin/Upload/FullImage/{0}0{1}.jpg", id, i), Thumb = string.Format("http://www.gallery2c.com/admin/Upload/ThumbNail/{0}0{1}.jpg", id, i), Title = string.Format("Title {0}", i), Caption = string.Format("Caption {0}", i) });
    for (int i = 10; i < 20; i++)
        imageDataList.Add(new { ImageID = 0, URL = string.Format("http://www.gallery2c.com/admin/Upload/FullImage/{0}{1}.jpg", id, i), Thumb = string.Format("http://www.gallery2c.com/admin/Upload/ThumbNail/{0}{1}.jpg", id, i), Title = string.Format("Title {0}", i), Caption = string.Format("Caption {0}", i) });
    return imageDataList;
} 
Here I have taken the help of Gallery2c.com for images. And the jQuery code used to dynamically load the images goes here-
var templete, containerVar;
jQuery(document).ready(function ($) {
    templete = $("#itemContainer li");
    containerVar = $("#containerID");
    getData();
});
function getData() {
    $.ajax({
        type: "POST",
        contentType: "application/json",
        data: "{id:'gett'}",
        url: "Clear ALL images in the Galleriffic jquery plugin and Insert new images using jquery or javas.aspx/GetImageDetail",
        dataType: "json",
        success: function (data) {
            $(data.d).each(function (i, item) {
                containerVar.append(templete.clone());
                containerVar.find("li:last a.thumb").attr({ "href": item.URL, "Title": item.Title });
                containerVar.find("li:last a.thumb img").attr({ "src": item.Thumb, "Title": item.Title });
                containerVar.find("li:last .caption").html(item.Caption);
            });
            attachGallery();
        },
        error: function (XMLHttpRequest, textStatus, errorThrown) {
            debugger;
        }
    });
    $('div.navigation').css({ 'width': '300px', 'float': 'left' });
    $('div.content').css('display', 'block');
}
And to dynamically change the datasource in the plugin we can call the getData() method on the change of the previously added dropdown. The code is as below-
$("#changeGallery").change(getData);
And also we need to remove the content of the container UL before changing the content of the UL. So, at the start of the getData() method we can append the code containerVar.empty();. But there is a small problem, if we play the slide show and then change the dropdown selection. Then the slide show shows wrong image in the big image. It shows the previous datasource images. So, before changing the data source we can stop the slide show and then change the source. The final code looks like-
        var templete, containerVar;
        jQuery(document).ready(function ($) {
            templete = $("#itemContainer li");
            containerVar = $("#containerID");
            getData();
            $("#changeGallery").change(getData);
        });

        function getData() {
            try {
                galleryVar.pause();
            }
            catch (ex) {
            }
            containerVar.empty();
            $.ajax({
                type: "POST",
                contentType: "application/json",
                data: "{id:'" + $("#changeGallery").val() + "'}",
                url: "Clear ALL images in the Galleriffic jquery plugin and Insert new images using jquery or javas.aspx/GetImageDetail",
                dataType: "json",
                success: function (data) {
                    $(data.d).each(function (i, item) {
                        containerVar.append(templete.clone());
                        containerVar.find("li:last a.thumb").attr({ "href": item.URL, "Title": item.Title });
                        containerVar.find("li:last a.thumb img").attr({ "src": item.Thumb, "Title": item.Title });
                        containerVar.find("li:last .caption").html(item.Caption);
                    });
                    attachGallery();
                },
                error: function (XMLHttpRequest, textStatus, errorThrown) {
                    debugger;
                }
            });
            $('div.navigation').css({ 'width': '300px', 'float': 'left' });
            $('div.content').css('display', 'block');
        }
You can download the code form here.

Sunday, March 11, 2012

Copy or duplicate column data in a table using context menu

This is a simple post used to copy data in a cell of a table to other cells of the same column. For this post we will be using a very simple HTML layout as follows-
<table>
<tr>
<th>data 1</th><th>data 2</th>
</tr>
<tr>
<td><input type="text" /> </td>
<td><input type="text" /> </td>
</tr>
<tr>
<td><input type="text" /> </td>
<td><input type="text" /> </td>
</tr>
<tr>
<td><input type="text" /> </td>
<td><input type="text" /> </td>
</tr>
<tr>
<td><input type="text" /> </td>
<td><input type="text" /> </td>
</tr>
<tr>
<td><input type="text" /> </td>
<td><input type="text" /> </td>
</tr>
</table>
For context menu we are going to use this plugin.

For content menu we are using the following HTML for the menu content, which include two options- copy and cancel. HTML for the context menu is as follows-
    <ul id="myMenu" class="contextMenu">
<li class="copy"><a href="#copy">Copy value</a></li>
<li class="quit separator"><a href="#cancel">Cancel</a></li>
</ul>
For implementing the context menu we need to take the following files references or we can download from the desired location-
    <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.5.js" type="text/javascript"></script>
<script src="http://labs.abeautifulsite.net/archived/jquery-contextMenu/demo/jquery.contextMenu.js"
type="text/javascript"></script>
<link href="http://labs.abeautifulsite.net/archived/jquery-contextMenu/demo/jquery.contextMenu.css"
rel="stylesheet" type="text/css" />
Now we can use following JavaScript code for copying/duplicating the data in a column-
    <script type="text/javascript">
$(document).ready(function () {
$("table input").contextMenu({
menu: 'myMenu'
}, function (action, el, pos) {
if (action == "copy") {
index = el.closest("tr").children().index(el.parent()[0]);
$("table tr").each(function (i, item) {
$(item).find("td:eq(" + index + ") input").val(el.val());
})
}
});
});
</script>

Friday, March 2, 2012

Binding, posting to a Dictionary in MVC 3 using jQuery ajax

This is a continuation of the previous two posts-
  1. Posting an array or generic list of string to asp.net MVC 3 using jQuery ajax
  2. Posting and binding generic list or array of complex object in MVC 3 using jQuery ajax
In this post we are going to discuss how to post data to a Dictionary using jQuery ajax. Lets have a simple model from the previous post and pass value to the action method as dictionary of the model we get the following controller method-
[HttpPost]
public ActionResult Binding_posting_to_a_dictionary_using_ajax(Dictionary<string, UserModel> userList)
{
    return PartialView("Success");
}
To work with dictionary the default model binder will accept the data in a particular format. If we consider key of the first element of the dictionary as firstkey then we have to give the name of the input parameter as [0].value.ModelPropertyName. So, for the first item we can give the names like-
<input name="[0].key" value="firstkey" type="hidden">
<input name="[0].value.FirstName" value="" type="text">
<input name="[0].value.LastName" value="" type="text">
<input name="[0].value.Age" value="" type="text">
If we see the above code block, we can see there is a hidden field for maintaining the key of the dictionary element and the value of the hidden filed is nothing but the key of the dictionary element.

Below is the code for posting to a dictionary from jQuery ajax-
<div class="data">
    <h4>
        First User</h4>
    <input type="hidden" name="[0].key" value="first" />
    First Name: @Html.TextBox("[0].value.FirstName")
    Last Name: @Html.TextBox("[0].value.LastName")
    Age: @Html.TextBox("[0].value.Age")
</div>
<div class="data">
    <h4>
        Second User</h4>
    <input type="hidden" name="[1].key" value="second" />
    First Name: @Html.TextBox("[1].value.FirstName")
    Last Name: @Html.TextBox("[1].value.LastName")
    Age: @Html.TextBox("[1].value.Age")
</div>
<div class="data">
    <h4>
        Third User</h4>
    <input type="hidden" name="[2].key" value="third" />
    First Name: @Html.TextBox("[2].value.FirstName")
    Last Name: @Html.TextBox("[2].value.LastName")
    Age: @Html.TextBox("[2].value.Age")
</div>
<input type="button" id="submitData" value="Submit data" />
<script type="text/javascript">
    $(document).ready(function () {
        $("#submitData").click(function () {
            var datatopost = new Object();
            $(".data").each(function (i, item) {
                datatopost[$(item).find("input[name*=FirstName]").attr("name")] = $(item).find("input[name*=FirstName]").val();
                datatopost[$(item).find("input[name*=LastName]").attr("name")] = $(item).find("input[name*=LastName]").val();
                datatopost[$(item).find("input[name*=Age]").attr("name")] = $(item).find("input[name*=Age]").val();
                datatopost[$(item).find("input[name*=key]").attr("name")] = $(item).find("input[name*=key]").val();
            });
            $.ajax({
                url: '@Url.Action("Binding_posting_to_a_dictionary_using_ajax")',
                type: 'POST',
                traditional: true,
                data: datatopost,
                dataType: "json",
                success: function (response) {
                    alert(response);
                },
                error: function (xhr) {
                    alert(xhr);

                }
            });
        });
    });
</script>
[HttpPost]
       [HttpPost]
        public JsonResult Binding_posting_to_a_dictionary_using_ajax(Dictionary<string, UserModel> userList)
        {
            return Json("Success");
        }
Explanation of the jQuery code is given in the previous post. We can check the input data of the ajax call and the posted value of dictionary data in the image below-

Thursday, March 1, 2012

Posting and binding generic list or array of complex object in MVC 3 using jQuery ajax

This post is an extension of the previous post. Please go through the previous post before proceeding with this. In this post we are going to discuss about posting a generic list or array of complex object from jQuery ajax.

Before proceeding with the solution, lets see how the posting of list works in case of a normal form post. To start with lets have the following model, view and controller actions-
    public class UserModel
    {
        public int UserId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int Age { get; set; }
    }
@model List<razor.Models.UserModel>
           
@using (Html.BeginForm())
{
    for (int i = 0; i < Model.Count; i++)
    {
        <div class="data">
        <h4>User ID: @Model[i].UserId</h4>
        First Name: @Html.TextBoxFor(m => m[i].FirstName)
        Last Name: @Html.TextBoxFor(m => m[i].LastName)
        Age: @Html.TextBoxFor(m => m[i].Age)
        <br /><br />
        </div> 
    }
    <input type="submit" value="Submit data" />
}

I think the above model and view are self explanatory.
        public ActionResult Binding_posting_to_generic_collection_of_custom_type_using_ajax()
        {
            List<UserModel> users = new List<UserModel>();
            for (int i = 0; i < 5; i++)
            {
                users.Add(new UserModel() {UserId=i, FirstName = "FirstName " + i.ToString(), LastName = "LastName " + i.ToString(), Age = i +34});
            }
            return PartialView(users);
        }
        [HttpPost]
        public ActionResult Binding_posting_to_generic_collection_of_custom_type_using_ajax(List<UserModel> userList)
        {
            return PartialView("Success");
        }
In case of controller action, the first action is used to create some in-memory user object and render the view. The second action method is used to accept the posted data from the view.

Now if we study the generated HTML by the view engine, we can see the pattern of HTML generation for the name attribute of the text boxes. Each property of a user object in the list got the name as [index].PropertyName. As we are binding to a collection, the default model binder will search for the values for the properties of the User class that are prefixed by an index. That's why the view engine is generating the name attribute of that fashion.

So, to pass the data using jQuery ajax we need to pass the querystring parameter of that fashion for model binder. So, first thing that can come to mind we will use traditional:true in the ajax call like in the previous post and ajax() method will internally use param() to build the querystring format for us. But as I have studied, jquery 1.7 version does not support for getting querystring format for array of complex object. That is if we execute the following code-
unescape($.param(
 [{FirstName:"FN 1", LastName:"LN 1", Age:35},
 {FirstName:"FN 2", LastName:"LN 2", Age:36},
 {FirstName:"FN 3", LastName:"LN 3", Age:37}]
 ))
we get the following result-
undefined=undefined&undefined=undefined&undefined=undefined
So, what we can do is write a little jQuery code and also use traditional:true of the ajax call to achieve this. The final jQuery code for doing this as below-
@model List<razor.Models.UserModel>
@for (int i = 0; i < Model.Count; i++)
{
    <div class="data">
        <h4>
            User ID: @Model[i].UserId</h4>
        First Name: @Html.TextBoxFor(m => m[i].FirstName)
        Last Name: @Html.TextBoxFor(m => m[i].LastName)
        Age: @Html.TextBoxFor(m => m[i].Age)
    </div> 
}
<input type="button" id="submitData" value="Submit data" />
<script type="text/javascript">
    $(document).ready(function () {
        $("#submitData").click(function () {
            var datatopost=new Object();
            $(".data").each(function (i, item) {
                datatopost["[" + i + "].FirstName"] = $(item).find("input[name*=FirstName]").val();
                datatopost["[" + i + "].LastName"] = $(item).find("input[name*=LastName]").val();
                datatopost["[" + i + "].Age"] = $(item).find("input[name*=Age]").val();
            });
            $.ajax({
                url: '@Url.Action("Binding_posting_to_generic_collection_of_custom_type_using_ajax")',
                type: 'POST',
                traditional: true,
                data: datatopost,
                dataType: "json",
                success: function (response) {
                    alert(response);
                },
                error: function (xhr) {
                    alert(xhr);
                }
            });
        });
    });
</script>
What we are doing here is looping through all the data and manually building a JavaScript object datatopost with properties like-
"[index].FirstName"
"[index].LastName"
"[index].Age"
So the actual assignment will be something like-
datatopost["[0].FirstName"]="First Name 0"
datatopost["[0].LastName"]="Last Name 0"
datatopost["[0].Age"]="35"

datatopost["[1].FirstName"]="First Name 1"
datatopost["[1].LastName"]="Last Name 1"
datatopost["[1].Age"]="36"

and so on...
Now we have our JavaScript object datatopost is ready, which is a plain JavaScript object with some properties. And now we can use traditional:true to automate the query string format.

Now we will be able to successfully post data from jQuery ajax. We can also see the data posted during the ajax request in the image below-


Note:
We can also change the logic of creation of datatopost like below-
$(".data").each(function (i, item) {
   datatopost[$(item).find("input[name*=FirstName]").attr("name")] = $(item).find("input[name*=FirstName]").val();
   datatopost[$(item).find("input[name*=LastName]").attr("name")] = $(item).find("input[name*=LastName]").val();
   datatopost[$(item).find("input[name*=Age]").attr("name")] = $(item).find("input[name*=Age]").val();
});