Showing posts with label Ajax. Show all posts
Showing posts with label Ajax. Show all posts

Friday, November 22, 2013

Show selected item details from Autocomplete dropdownlist of jQuery using PartialView

This is a continuation of a series around Autocomplete using jQuery in Asp.net MVC4.

1. Implement Autocomplete using jQuery UI inside Asp.Net MVC4 Application

2. Show selected item details from Autocomplete dropdownlist of jQuery UI – (using Knockout)

3. Show selected item details from Autocomplete dropdownlist of jQuery UI – (using PartialView)

In the last post we looked at how to display details about a selected person from the dropdownlist of jQuery UI Autocomplete using Knockout.js. Knockout.js allowed us to databind our ViewModel with our view.  In this post we are going to look at how to do show details of a person but using a PartialView. 

1. Right click on the Home folder>Add>View.

image

2. Type the name of the view as _Person

image

3. Create another method named GetPerson just like the following but this one returns a PartialViewResult instead of JsonResult.  And in our select statement of LINQ query we are return a custom class with all the properties.  We had to do otherwise we won’t be able to strongly type our view.  For demo purposes I have created the class in the same class as the controller but in real world application I create separate ViewModel folder and create classes there.

[HttpGet]
public PartialViewResult GetPerson(int id)
{
var user = (from p in db.People
join e in db.Employees
on p.BusinessEntityID equals e.BusinessEntityID
join be in db.BusinessEntityAddresses on p.BusinessEntityID equals be.BusinessEntityID
join ad in db.Addresses on be.AddressID equals ad.AddressID
join st in db.StateProvinces on ad.StateProvinceID equals st.StateProvinceID
where p.BusinessEntityID == id
select new PersonDetail
{
Id = p.BusinessEntityID,
FullName = p.LastName + ", " + p.FirstName,
LastName = p.LastName,
FirstName = p.FirstName,
JobTitle = e.JobTitle,
HireDate = e.HireDate,
AddressLine1 = ad.AddressLine1,
City = ad.City,
PostalCode = ad.PostalCode,
State = st.Name
}).ToList().First();
if (user == null)
{
HttpNotFound();
}
return PartialView("_Person", user);
}

Add the following class for PersonDetail below the controller class in your HomeController.

public class PersonDetail
{
public int Id { get; set; }
public string FullName { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string JobTitle { get; set; }
public DateTime HireDate { get; set; }
public string AddressLine1 { get; set; }
public string City { get; set; }
public string PostalCode { get; set; }
public string State { get; set; }
}

Let's make sure our PartialView _Person.cshtml accepts our model of type PersonDetail.

@model AspNetDemo06.Controllers.PersonDetail
<p>@Model.Id </p>
<p>@Model.FullName </p>
<p>@Model.LastName </p>
<p>@Model.FirstName </p>
<p>@Model.JobTitle </p>
<p>@Model.HireDate </p>
<p>@Model.AddressLine1 </p>
<p>@Model.City = ad.City </p>
<p>@Model.PostalCode </p>
<p>@Model.State </p>

Finally fix few things to fix in our Index.cshtml page. First create a div with id selectedPerson.

<div id="selectedPerson">

</div>

Then in our javascript code, first instead of FindPerson change it to GetPerson. Then in the success method of ajax call just replace the div with the returned partial view.

 $(document).ready(function () {
$("#SearchString").autocomplete({
source: function (request, response) {
$.ajax({
url: "/Home/Find",
data: "{ 'prefixText': '" + request.term + "' }",
dataType: "json",
type: "POST",
contentType: "application/json; charset=utf-8",
dataFilter: function (data) { return data; },
success: function (data) {
response($.map(data, function (item) {
return {
label: item.value,
value: item.value,
id: item.id,
firstname: item.firstname,
lastname: item.lastname
}
}))
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(textStatus);
}
});
},
minLength: 2,
select: function (even, ui) {
var businessEntityId = ui.item.id;
$.ajax({
url: "/Home/GetPerson",
type: "GET",
data: { "id": businessEntityId}
}).done(function () {
}).success(function (person) {
$("#selectedPerson").html(person);
});
}
});
});

In the above code line 36 the person object is our partial view that we inserting in the div of id selectedPerson.


image

Monday, November 18, 2013

Show selected item details after selecting an item from autocomplete dropdownlist using Knockout.js

This is a continuation of previous post where we looked at how to implement autocomplete functionality into Asp.net MVC 4 application using jQuery UI and Ajax.  In the previous post when you selected a particular user it redirected you to a different page.  What if we want to show information about that user on the same page without any page refresh.  In this post we are going to do exactly the same thing.  Well there are many ways to do this but I want to show you the one using Knockout.js. To use Knockout.js first include it in our BundleConfig.cs  

bundles.Add(new ScriptBundle("~/bundles/knockout").Include("~/Scripts/knockout-*"));

Second lets tell our _layout.cshtml page to load knockout.

<body>
@RenderBody()
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/jqueryui")
@Scripts.Render("~/bundles/knockout")
@RenderSection("scripts", required: false)
</body>

Add the following javascript below the autocomplete method in Index.cshtml page. This is a PersonViewModel which has properties like LastName, FirstName and BusinessEntityID.

 
//ViewModels Knockout
function PersonViewModel(data) {
this.LastName = ko.observable(data.lastname);
this.FirstName = ko.observable(data.firstname);
this.BusinessEntityId = ko.observable(data.id);
}

We created a ViewModel for the Person Object called PersonViewModel. And ko.observable(data.lastname) means that lastname is an observable property. Next add the following div section where we are going to display selected person’s information.  Inside the span tag see the data-bind=”text: LastName” it will be replaced by the LastName returned from our select function inside autocomplete.

<div style="display: none;" data-bind="visible: true">
<span data-bind="text: LastName"></span>
<span data-bind="text: FirstName"></span>
</div>

In this post I want to just focus on people who are employees. So lets make sure we allow users to search just for Employees. Make appropriate changes suggested below

[HttpPost]
public JsonResult Find(string prefixText)
{
var suggestedUsers = from x in db.People
where x.LastName.StartsWith(prefixText) && x.PersonType == "EM"
select new {
id = x.BusinessEntityID,
value = x.LastName + ", " + x.FirstName,
firstname = x.FirstName,
lastname = x.LastName};
var result = Json(suggestedUsers.Take(5).ToList());
return result;
}

One last thing before we run our application. Replace the window.location.href line with  ko.applyBindings(new PersonViewModel(ui.item));  This will databind our selected person to the datatemplate shown above.

@section scripts{
<script type="text/javascript">
//Javascript function to provide AutoComplete and Intellisense feature for finding Users.
$(document).ready(function () {
$("#SearchString").autocomplete({
source: function (request, response) {
$.ajax({
url: "/Home/Find",
data: "{ 'prefixText': '" + request.term + "' }",
dataType: "json",
type: "POST",
contentType: "application/json; charset=utf-8",
dataFilter: function (data) { return data; },
success: function (data) {
response($.map(data, function (item) {
return {
label: item.value,
value: item.value,
id: item.id,
firstname: item.firstname,
lastname: item.lastname
}
}))
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(textStatus);
}
});
},
minLength: 2,
select: function (even, ui) {
ko.applyBindings(new PersonViewModel(ui.item));
}
});
});
//ViewModels Knockout
function PersonViewModel(data) {
this.LastName = ko.observable(data.lastname);
this.FirstName = ko.observable(data.firstname);
this.BusinessEntityId = ko.observable(data.id);
}
</script>
}

image


I just wanted to show that it could be done with Knockout. At this point our search button is non-functional. We will make it functional later. One thing to notice here is that the information about the selected user is grabbed from the initial query we did to find suggested users.  To bring in more information we would have to adjust our LINQ query.  And I know what you are thinking right now, “But Mitul, that would bring in all the unnecessary information about other users in which we are not interested.” So we are going to write another method in our HomeController to bring in information just about that selected person. Before doing that let’s update our model to bring in additional information about our selected person.


Open the ADworks.edmx file inside our Models folder and then click anywhere next to the entity and select “Update Model from Database


image


And then check on these entities Employee, Address, BusinessEntityAddress, StateProvince. 


image

Now FindPerson method will return detailed information about the selected person.
        [HttpGet]
public JsonResult FindPerson(int id)
{
var user = (from p in db.People
join e in db.Employees
on p.BusinessEntityID equals e.BusinessEntityID
join be in db.BusinessEntityAddresses on p.BusinessEntityID equals be.BusinessEntityID
join ad in db.Addresses on be.AddressID equals ad.AddressID
join st in db.StateProvinces on ad.StateProvinceID equals st.StateProvinceID
where p.BusinessEntityID == id
select new
{
Id = p.BusinessEntityID,
FullName = p.LastName + ", " + p.FirstName,
LastName = p.LastName,
FirstName = p.FirstName,
JobTitle = e.JobTitle,
HireDate = e.HireDate,
AddressLine1 = ad.AddressLine1,
City = ad.City,
PostalCode = ad.PostalCode,
State = st.Name
}).ToList().First();
if (user == null)
{
HttpNotFound();
}
return Json(user, JsonRequestBehavior.AllowGet);
}

Inside our select function of AutoComplete we are going to make an ajax call [line 31] to this method passing in the BusinessEntityID [line 35] for the Person to FindPerson method [line 33]. Also I have made changes to the PersonViewModel to reflect all the properites returned from the FindPerson method.

@section scripts{
<script type="text/javascript">
$(document).ready(function () {
$("#SearchString").autocomplete({
source: function (request, response) {
$.ajax({
url: "/Home/Find",
data: "{ 'prefixText': '" + request.term + "' }",
dataType: "json",
type: "POST",
contentType: "application/json; charset=utf-8",
dataFilter: function (data) { return data; },
success: function (data) {
response($.map(data, function (item) {
return {
label: item.value,
value: item.value,
id: item.id,
firstname: item.firstname,
lastname: item.lastname
}
}))
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(textStatus);
}
});
},
minLength: 2,
select: function (even, ui) {
var businessEntityId = ui.item.id;
$.ajax({
url: "/Home/FindPerson",
type: "GET",
data: { "id": businessEntityId}
}).done(function () {
}).success(function (person) {
ko.applyBindings(new PersonViewModel(person));
});
}
});
});
//Person Detailed View Model
function PersonViewModel(p) {
this.Id = ko.observable(p.Id);
this.FullName = ko.observable(p.FullName);
this.LastName = ko.observable(p.LastName);
this.FirstName = ko.observable(p.FirstName);
this.JobTitle = ko.observable(p.JobTitle);
this.HireDate = ko.observable(p.HireDate);
this.AddressLine1 = ko.observable(p.AddressLine1);
this.City = ko.observable(p.City);
this.PostalCode = ko.observable(p.PostalCode);
this.State = ko.observable(p.State);
}
</script>
}

Finally lets update our div to reflect all the udpated user properties.

<div style="display: none;" data-bind="visible: true">
FullName:<span data-bind="text: FullName"></span>
<br/> LastName:<span data-bind="text: LastName"></span>
<br/> FirstName:<span data-bind="text: FirstName"></span>
<br/> HireDate:<span data-bind="text: HireDate"></span>
<br/> Job Title:<span data-bind="text: JobTitle"></span>
<br/> AddressLine1: <span data-bind="text: AddressLine1"></span>
<br/> City: <span data-bind="text: City"></span>
<br/> PostalCode: <span data-bind="text: PostalCode"></span>
<br/> State: <span data-bind="text: State"></span>
</div>

Run the application and search a particular user.


image


In this post, we looked at how to show selected user’s information after search for a user using jQuery UI’s autocomplete method.  Initially to display the selected user we simply data binded the object returned by the select object, we didn’t went to server to find more information.  In the final attempt we did another ajax call to the server to find more information about the user.  In the next post we will look into how to show detailed information about a person using a PartialView. 

Friday, November 15, 2013

Implement Autocomplete using Jquery UI inside Asp.NET MVC4 application

I think the title explains what I am going to accomplish in this blogpost.  I assume you have AdventureWorks2012 database, visual studio 2012 installed. So let’s get started.
1. Open Visual Studio 2012/2013. I am using VS2012 for this demo.
2. Create an Asp.Net MVC 4 Application and Click on basic application for this demo.
3. Add a new controller to your project. I am naming it as HomeController and using Empty MVC Controller as template.
image
4. Right Click on Models Folder and Add new Item. From the Data section add a new ADO.NET Entity Data Model.  I named it as ADWorks.edmx
image
5. Choose the AdventureWorks2012 database when prompted during new connection.   Choose Person.Person table when “Choose Your Database Objects and Settings” dialog appears in the Entity Data Model Wizard as shown in the figure.  After adding the model build the project once [Ctrl+Shift+B].
image
6. Open HomeController.cs file and Add Reference to your Models. Then create a new method to find the person.
private AdventureWorks2012Entities db = new AdventureWorks2012Entities();

        [HttpPost]
        public JsonResult Find(string prefixText)
        {
            var suggestedUsers = from x in db.People
                                 where x.LastName.StartsWith(prefixText)
                                 select new { id = x.BusinessEntityID, 
                                     value = x.LastName + “, ”+ x.FirstName, 
                                     firstname = x.FirstName, 
                                     lastname = x.LastName };
            var result = Json(suggestedUsers.Take(5).ToList());
            return result;
        }

In the above code we find all the users that match the text that is entered by the user and only return 5. One thing to note here is that the Value field is the one that is going to be displayed when the dropdown for all the possible values shows up. I pass in a anonymous object with all the possible properties that I want for additional stuff. Usually id is a unique identifier and value is something you want to display. Another thing is notice the HttpPost attribute somehow I tried with HttpGet and it doesn’t like because you have to set JsonRequestBehavior to allow GET.

7. Next Add Index.cshtml page to your View Folder and then add the Html.TextBox where we will enter Person’s LastName, inside Html.BeginForm() as shown in the code. 
    </p>
        Search the Person by typing in the LastName of the user.
    </p>
    <div>
        @using (Html.BeginForm())
        {  
            <p>
                @Html.TextBox("SearchString")
                <input type="submit" value="Search">
            </p>
        }
  </div>

8. Add @scripts.Render("~/bundles/jqueryui") to the _Layout.cshtml page as shown below.
image
9. Open Index.cshtml page and now we will add the Javascript code that will query our Find method of Step6 using ajax method of jQuery and autocomplete method of jQuery UI.  We will add the following script inside section called scripts that will place all this javascript code in place of @RenderSection(“scripts”,required: false) in our _layout.cshtml page. 
@section scripts{
    <script type="text/javascript">
        //Javascript function to provide AutoComplete and Intellisense feature for finding Users.
        $(document).ready(function () {
            $("#SearchString").autocomplete({
                source: function (request, response) {
                    $.ajax({
                        url: "/Home/Find",
                        data: "{ 'prefixText': '" + request.term + "' }",
                        dataType: "json",
                        type: "POST",
                        contentType: "application/json; charset=utf-8",
                        dataFilter: function (data) { return data; },
                        success: function (data) {
                            response($.map(data, function (item) {
                                 return {
                                    label: item.value,
                                    value: item.value,
                                    id: item.id,
                                    firstname: item.firstname,
                                    lastname: item.lastname
                                }
                            }))
                        },
                        error: function (XMLHttpRequest, textStatus, errorThrown) {
                            alert(textStatus);
                        }
                    });
                },
                minLength: 2,
                select: function (even, ui) {
                    window.location.href = '/Home/details/' + ui.item.id;
                    }
            });
        });
    </script>
}

In this step, we use autocomplete method of jQuery UI.  The nested function inside autocomplete takes two object request and response.  We take the response object and pass the text that was entered to the Find method of our controller using jQuery Ajax method. In the case of a success event we take the response object and grab the data and for each item return value, id, firstname and lastname.  On the user interface side we are only going to see the value property in the form of a dropdown list.  Once you select a particular object the select function or callback is triggered with even, ui parameters.  The ui parameter has all the properties that we returned from the success event of the ajax method.  In our select event, I want to redirect the user to a different page where you can see the details of the person.  I have included this in the sample code available on my github account.  Redirection is done by setting window.location.href to the url you wish.

Its time to spin this project in the browser so lets run and see what happens when we type a person’s lastname. 

image

10. Fix CSS. Notice the dropdown style is messed up.  Your marketing department and users would be frustrated.  Let’s fix that. Oh ! But you might say that there is CSS file already included for the autocomplete I see in the Content folder under themes/base right. Actually we forgot to include that in our _Layout.cshtml file. If you use any of the jQuery UI things then replace the existing Styles.Render line with the following. Because in the Bundles.Config file we already have created jQuery UI CSS Style Bundle. We just need to include it if we use it.
    @Styles.Render("/Content/themes/base/css", "~/Content/css")

Test it out if the CSS has changed or not. F5.

image

The full source code for this sample is available on github. In the next post we will check out some fun stuff with the help of Knockout.

Sunday, April 7, 2013

How to do Ajax in Asp.Net MVC4 Application?

In this post, I am going to show you how to do Ajax in an Asp.net MVC4 Application using C#.  It is very easy to create a very simple Ajax demo up and running.  Here are few things you need in order to get basic Ajax demo running in your application.  The complete source code for this application is available on my gitHub account.

0. Create basic Asp.net MVC4 application using VS2012
1. You need an Ajax.ActionLink in one of your views
2. Public Method that will return a Partial View
3. PartialView with some content

Open Index.cshtml and remove all the content and paste Ajax.ActionLink as below.  Ajax.ActionLink has 12 overloads (msdn link) to do Ajax and all of them are very self explanatory.  In the code below, I am passing in the text to display as link text, the controller action to hit, and Ajax Options that’s it. 
We need a div whose content we wish to replace or the target location where we wish to replace the contents.
Then we need a public method inside our home controller that will return a partial view with some data in it.  You can pass data to the Partial View using a ViewBag.  As in our case I am using ViewBag.Tiffin variable to hold that data and retrieve inside Partial View. Next in our Partial View we retrieve it.
Hit F5 and click on the link to see what happens.  All of the text will be replaced if you created a default Asp.net MVC4 Internet application.  Because by default Asp.net Template for MVC4 Internet Application doesn’t load the Jquery Validation bundle to do Unobtrusive JavaScript.  Include this line @Scripts.Render("~/bundles/jqueryval") in your _layout.cshtml page and you are all good to go.  Run again and you should see the output.

You might be curious to ask why are you using a POST instead of GET inside Ajax.ActionLink? Because depending on which browser you are running it will cache the response and will not trigger an ajax request every time you wish to do Ajax.  To overcome this problem you can disable cache for just that method use GET in Ajax.ActionLink

I have recorded a screencast video to show upto this point. Check it out and if you have any feedback let me know.
Now moving on to some more stuff like how can we put animated gifs showing that something is running.  That is actually even more easy.  For this purpose we are going to use few more attributes of AjaxOptions object.  This object has some events like OnCompleted, OnBegin, OnSuccess, OnFailure and we will assign javascript functions to them.  So when we trigger a ajax request then OnBegin function we will load the gif and OnCompleted we will hide the image. 
What about passing Parameters? For this we will use another overloaded method of Ajax.ActionLink which will pass routeparameters to our public method.  We are going to change our public method and Ajax.ActionLink as shown in the code below.
Ok I get it but what about some HtmlAttributes?  Actually there is very easy you use another overload of Ajax.ActionLink and you can pass in an anonymous object with values to it.  There are different options you can consider for HtmlAttributes. And also what about jQuery mobile data-role attribtues? For jQuery data attributes you will have to use _ instead of - as shown below.  Alright but now I want to implement Async and Await into this mix how can I do that?  Hmmm. For this we will have to modify our public method inside our controller to return Task of type PartialViewResult.  That is pretty easy too.
Oh! But I am told to just use jQuery to do Ajax then you have be a little bit careful. Please check your jQuery version. If you have jQuery 1.7 then Ajax is done differently and jQuery 1.9 does little bit differently. I am using 1.9 for this demo. There is a difference where you detect start and success events. You attach these events to the document object (see upgrade guide).


What-If Error
Why Ajax is still not working out for me? Here are some very common issues which I ran into while learning Ajax in MVC4 application. 

1. Always debug using Internet Explorer.  If you run your application then Chrome will show you red error count only if you inspect the page but will happily show you the page.  Compare this to Internet Explorer it will give you Microsoft JavaScript Runtime Error.  Doing a quick StackOverflow search will give you most of the common problems’ solutions.

2. Check if you have loaded Jquery Validation bundle

3. Check if you are even loading jQuery or not. Actually this should be number 0.

4. Check if you are loading multiple versions of Jquery or validation, if yes then deletes older ones.

5. Open up fiddler to see what is going on. If I am working with web then I have fiddler open by default most of the times. And it has affected my debugging skills.

6. Put breakpoints as this is very basic debugging skill.

7. Try deleting packages for jQuery and delete related entry in packages.config and again install those packages using Nuget.

8.  The complete source code for this application and demo is at gitHub.  Check this link