How to update View using Ajax in asp.net mvc? - ajax

I have a simple form with drop down list with people names(exhibitors). After I choose one of them and click link “Get Exhibitor data” I want to update only a part of my Home site and show data of chosen exhibitor.
Structure of my folders in project:
My home controller looks like follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace WebApplication5.Controllers
{
public class HomeController : Controller
{
// GET: Home
public ActionResult Index()
{
return View();
}
public PartialViewResult GetExhibitorDataById(int? Id)
{
List<Exhibitor> exhibitors = new List<Exhibitor>()
{
new Exhibitor()
{
Id=1,
Name= "Tom",
Surname="Cruise"
},
new Exhibitor()
{
Id=2,
Name= "Jennifer",
Surname="Lopez"
},
};
if (Id == 1)
{
//return PartialView("_Exhibitor", exhibitors[0]);
Session["ExhibitorData"] = exhibitors[0];
return PartialView("_Exhibitor");
}
else if(Id==2)
{
//return PartialView("_Exhibitor", exhibitors[1]);
Session["ExhibitorData"] = exhibitors[1];
return PartialView("_Exhibitor");
}
else
{
//return PartialView("_Exhibitor", new Exhibitor());
Session["ExhibitorData"] = new Exhibitor();
return PartialView("_Exhibitor");
}
}
public class Exhibitor
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
}
}
My Index View code from Home folder looks like this:
#using WebApplication5.Controllers
<h2>Exhibitors</h2>
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
#Html.DropDownList("ExhibitorsList", new List<SelectListItem>
{
new SelectListItem {Text ="Tom Cruise", Value = "1" },
new SelectListItem {Text ="Jennifer Lopez", Value = "2" },
}, "Select Exhibitor" )
#Ajax.ActionLink("Get Exhibitor data", "GetExhibitorDataById", new { Id = 1 }, new AjaxOptions()
{
HttpMethod = "GET",
UpdateTargetId = "divExhibitors", // ID of the HTML element to update
InsertionMode = InsertionMode.Replace // Replace the existing contents
})
<div id="divExhibitors">
</div>
But I would like to set parameter Id of Ajax.ActionLink, to value from DropDownList called “ExhibitorsList” and I don’t know how to do that.
Partial View code “_Exhibitor” looks like this:
#using WebApplication5.Controllers
<table>
#if (Session["ExhibitorData"] != null)
{
<tr>
<td>Id</td>
#*#{HomeController.Exhibitor exhibitor = ((HomeController.Exhibitor)(#Session["ExhibitorData"]))};*#
#*<td>#exhibitor.Id</td>*#
<td>#((HomeController.Exhibitor)(#Session["ExhibitorData"])).Id</td>
</tr>
<tr>
<td>Name</td>
<td>#((HomeController.Exhibitor)(#Session["ExhibitorData"])).Name</td>
</tr>
<tr>
<td>surname</td>
<td>#((HomeController.Exhibitor)(#Session["ExhibitorData"])).Surname</td>
</tr>
}
</table>
When I tried to run my app and then click Aajax.ActionLink something gone wrong because after I clicked Ajax.ActionLink I go to different url.
Home View:
After Clicked Ajax.ActionLink
What I want is to choose person name from dropdownlist, then click Ajax.ActionLink and get data of chosen person(exhibitor) without any redirect or refresh the site as I wrote at the beginning. Im also curious if it possible to do that only with one View- "Index" without using partialview.

As Stephen Muecke said you should change your controller ina View to pass model and show it:
Controller:
public PartialViewResult GetExhibitorDataById(int? Id)
{
List<Exhibitor> exhibitors = new List<Exhibitor>()
{
new Exhibitor()
{
Id=1,
Name= "Tom",
Surname="Cruise"
},
new Exhibitor()
{
Id=2,
Name= "Jennifer",
Surname="Lopez"
},
};
//here how you pass model to View
ViewData.Model = exhibitors.FirstOrDefault(x => x.Id == Id);
return PartialView("_Exhibitor");
}
Your View will be:
#model WebApplication5.Controllers.Exhibitor
#using WebApplication5.Controllers
<table>
#if (Model != null)
{
<tr>
<td>Id</td>
<td>#Model.Id</td>
</tr>
<tr>
<td>Name</td>
<td>#Model.Name</td>
</tr>
<tr>
<td>surname</td>
<td>#Model.Surname</td>
</tr>
}
</table>
What i did is make your View strongly typed with #model WebApplication5.Controllers.Exhibitor and then use your model properties to show data.

Finally I found the solution. "The problem was due to the corrupted unobtrusive-ajax.min.js file." I found post asp.net mvc partialview #Ajax.ActionLink doesn't work and I had the same problem. I uninstalled unobtrusive-ajax.min.js and intalled it once again. It was good solution in my case and now ajax works properly.

Related

Ajax call returning old ASP.NET MVC partial view instead of updated view

I have an Ajax call triggered by a button, that calls a controller to update a partial view. The controller returns an updated partial view, but the partial view received in the success function of the Ajax call is the original view, not the updated view.
I created a sample ASP.NET MVC program to reproduce the problem. The program displays a list of customers in a table as follows.
Snapshot of UI
The text boxes are rendered in the partial view _Status. When the Toggle Status button is clicked, controller is called via Ajax to toggle the customer's status between true and false, and refresh the partial view of the corresponding text box. The problem is that the status never changes. Why is that?
UPDATE
I just added the following line of code in the Status action of the controller, and now, the Ajax success function correctly receives the updated partial view!
this.ModelState.Clear();
Can someone explain why?
Here is the Index.cshtml view that displays the initial view.
#model IEnumerable<ComboModel.Models.CustomerSummary>
<script type="text/javascript">
function updatePartialView(id) {
debugger;
$.ajax({
url: "/CustomerSummary/Status",
data: $('#' + id + ' :input').serialize(),
dataType: "HTML",
type: "POST",
success: function (partialView) {
// Here the received partial view is not the one created in
// the controller, but the original view. Why is that?
debugger;
$('#' + id).replaceWith(partialView);
},
error: function (err) {
debugger;
},
failure: function (err) {
debugger;
}
});
}
</script>
<h2>Customer Summary</h2>
<table>
<tr>
<th>Name</th>
<th>Active?</th>
<th>Toggle</th>
</tr>
#foreach (var summary in Model)
{
<tr>
<td>#summary.FirstName #summary.LastName</td>
#Html.Partial("_Status", summary.Input)
<td><button type="button" name="#("S" + summary.Input.Number)" onclick="updatePartialView(this.name)">Toggle Status</button></td>
</tr>
}
</table>
The _Status.cshtml partial view.
#model ComboModel.Models.CustomerSummary.CustomerSummaryInput
<td id="#("S" + Model.Number)">
#Html.TextBoxFor(model => model.Active)
<input type="hidden" value="#Model.Number" name="Number" />
</td>
The CustomerSummaryController.cs.
using System.Collections.Generic;
using System.Web.Mvc;
using ComboModel.Models;
namespace ComboModel.Controllers
{
public class CustomerSummaryController : Controller
{
private readonly CustomerSummaries _customerSummaries = new CustomerSummaries();
public ViewResult Index()
{
IEnumerable<CustomerSummary> summaries = _customerSummaries.GetAll();
return View(summaries);
}
public PartialViewResult Status(CustomerSummary.CustomerSummaryInput input)
{
this.ModelState.Clear(); // If I add this, things work. Why?
input.Active = input.Active == "true" ? "false" : "true";
return PartialView("_Status", input);
}
}
public class CustomerSummaries
{
public IEnumerable<CustomerSummary> GetAll()
{
return new[]
{
new CustomerSummary
{
Input = new CustomerSummary.CustomerSummaryInput {Active = "true", Number = 0},
FirstName = "John",
LastName = "Smith"
},
new CustomerSummary
{
Input = new CustomerSummary.CustomerSummaryInput {Active = "false", Number = 1},
FirstName = "Susan",
LastName = "Power"
},
new CustomerSummary
{
Input = new CustomerSummary.CustomerSummaryInput {Active = "true", Number = 2},
FirstName = "Jim",
LastName = "Doe"
},
};
}
}
}
And finally, the CustomerSummary.cs model.
namespace ComboModel.Models
{
public class CustomerSummary
{
public string FirstName { get; set; }
public string LastName { get; set; }
public CustomerSummaryInput Input { get; set; }
public class CustomerSummaryInput
{
public int Number { get; set; }
public string Active { get; set; }
}
}
}
Thanks!
This is a duplicate of Asp.net MVC ModelState.Clear.
In the current scenario, because there is no validation of the status field, it is ok to clear the ModelState. However, the MVC correct way would be to pass a status value (true or false) to a GET action in the controller, toggle the value, return the result string, and update the text box on the page.

Updating only the partial view contained in a mvc 3 page?

I have a MVC 3 page that returns a list of user responses with a partial view called "memo" (which displays/add memos) for each response. When I add a memo to a response, it should update the db and the list of memos for that response. It should be partial page update via ajax, which effects only the partial view "memo".
The view Response.chtml that contains "memo":
#using (Html.BeginForm("Response", "User", FormMethod.Post, new { id = "UserResponse" }))
{
.... code removed ....
#foreach (var response in Model)
{
<div class="qna"><input type="text" id=#response.responseId value="#response.ResponseText" />
<div>#Html.Partial("_memo", response.responseId)</div>
}
.....
The partial page "_memo.chtml":
<div>add memo</div>
<ul id="memos">
#foreach (var memo in Model) {
<li>#memo.Text</li>
}
</ul>
<form method="post" id="memoForm"
action="#Url.Action("AddMemo")">
#Html.TextArea("Memo", new { rows = 5, cols = 50 })
<br />
<input type="submit" value="Add" />
</form>
Controller for view User/Response:
[HttpGet]
public ActionResult Response(id)
{
.....
return View(responses);
I just started with the code above, need help filling the blanks.
If I pass the response Id to the partial view, how do I pull the list of memos for that response? Will it involve ajax? (instead of ..Partial("_memo", response.memos))
How do I update the partial view via ajax call. What is ajax call (sample code) on the client side and how would the controller look? When the ajax call is successful, how do I update the list memos div="memos" to reflect the new memo?
Will the form action from Response conflict with form action of the partial view Memo?
Answers to Questions:
You shouldn't pass the responseId to the partial, you should pass the memo collection from your response object and make your partial view strongly typed to that collection.
See full code example below.
You don't need the form in the partial since you're making a simple ajax call to add the new memo. See full code example below.
This is a modified example from a project I am currently working on:
There is a bit of code to follow, so here goes:
This is my model. There are several sections on a career planning form, one of which is a section to select and update competencies. The SelectCompetencies model has a collection of competencies within it. The user will have the ability to add competencies. When they do, it will be added to the database and will update the list of competencies in the partial.
public class CareerPlanningFormViewModel
{
// code removed ...
public SelectCompetenciesModel SelectCompetencies { get; set; }
// code removed ...
}
public class SelectCompetenciesModel
{
public int CareerPlanningFormID { get; set; }
public IList<CompetencyModel> Competencies { get; set; }
public byte MaximumCompetenciesAllowed { get; set; }
}
public class CompetencyModel
{
public int CompetencyID { get; set; }
public int? CompetencyOptionID { get; set; }
public string ActionPlan { get; set; }
public IDictionary<int, string> CompetencyOptions { get; set; }
}
The main view of the career planning form: /Views/CPF/CareerPlanningForm.cshtml
#model MyNamespace.Models.CareerPlanningForm.CareerPlanningFormViewModel
<link rel="stylesheet" href="#Url.Content("~/Content/CreateCPF.css")" />
#using (Html.BeginForm())
{
// other sections loaded here...
// code removed for brevity...
#Html.Partial("SelectCompetencies", Model.SelectCompetencies)
// other sections loaded here...
// code removed for brevity...
}
The SelectCompetencies partial: /Views/CPF/SelectCompetencies.cshtml
The user will fill in the new action plan text and click the add competency button.
That will post via ajax to CPFController/NewCompetencyTemplate
#model MyNamespace.Models.CareerPlanningForm.SelectCompetenciesModel
#Html.HiddenFor(m => m.CareerPlanningFormID)
<h3>Select Competencies</h3>
<p class="guidance">
Select up to #Model.MaximumCompetenciesAllowed competencies to focus on improving.
</p>
<table id="CompetenciesTable">
<thead>
<tr>
<th>Competency</th>
<th>Action Plan:</th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.Competencies.Count(); i++)
{
#Html.EditorFor(m => m.Competencies[i])
}
</tbody>
<tfoot id="CompetenciesTableFooter" class="#(Model.Competencies.Count() < Model.MaximumCompetenciesAllowed ? "" : "hidden")">
<tr>
<td colspan="2">
#Html.TextArea("NewActionPlanText")
#Html.Button(ButtonType.Button, "Add Another Competency", "add", new { id = "AddCompetencyButton" })
</td>
</tr>
</tfoot>
</table>
#section script
{
<script>
jQuery(document).ready(function ($) {
var competenciesTableBody = $('#CompetenciesTable tbody'),
competenciesTableFooter = $('#CompetenciesTableFooter'),
addCompetencyButton = $('#AddCompetencyButton'),
newCompetencyTemplateUrl = '#Url.Content("~/CPF/NewCompetencyTemplate")',
count = competenciesTableBody.find('tr').length,
newActionPlanText = $('#NewActionPlanText'),
careerPlanningFormID = $('#CareerPlanningFormID');
addCompetencyButton.click(function () {
$.ajax({
url: newCompetencyTemplateUrl(),
type: 'POST',
data: {
careerPlanningFormID: careerPlanningFormID,
actionPlan: newActionPlanText,
itemCount: count
},
dataType: 'html',
success: function (data) {
var elements = $(data);
// other code removed here...
competenciesTableBody.append(elements);
// other code removed here...
}
});
});
});
</script>
}
Views/CPF/EditorTemplates/CompetencyModel.cshtml
#model MyNamespace.Models.CareerPlanningForm.CompetencyModel
<tr class="competency">
<td>
#Html.DropDownListFor(m => m.CompetencyOptionID, new SelectList(Model.CompetencyOptions, "Key", "Value"), "Select competency...")
</td>
<td>
#Html.TextAreaFor(m => m.ActionPlan, new { #class = "competencyActionPlan" })
#Html.HiddenFor(m => m.CompetencyID)
</td>
</tr>
The controller containing the action to add the new competency: /Controllers/CPFController.cs
This will call the CareerPlanningFormService to add the new competency and will return a partial view for NewCompetencyTemplate that will render out the new competency
public class CPFController : Controller
{
private readonly ICareerPlanningFormService careerPlanningFormService;
public CPFController(ICareerPlanningFormService careerPlanningFormService)
{
this.careerPlanningFormService = careerPlanningFormService;
}
[HttpPost]
public PartialViewResult NewCompetencyTemplate(int careerPlanningFormID, int itemCount, string newActionPlanText)
{
var count = itemCount + 1;
// Even though we're only rendering a single item template, we use a list
// to trick MVC into generating fields with correctly indexed name attributes
// i.e. Competencies[1].ActionPlan
var model = new SelectCompetenciesModel
{
Competencies = Enumerable.Repeat<CompetencyModel>(null, count).ToList()
};
model.Competencies[count - 1] = this.careerPlanningFormService.BuildNewCompetencyModel(careerPlanningFormID, newActionPlanText);
return this.PartialView(model);
}
}
My service class: CareerPlanningFormService.cs
This handles the business logic and makes the calls to the repository to add the item to the database and returns a new CompetencyModel
public class CareerPlanningFormService : ICareerPlanningFormService
{
private readonly IMyRenamedRepository repository;
private readonly IPrincipal currentUser;
public CareerPlanningFormService(
IMyRenamedRepository repository,
IPrincipal currentUser)
{
this.repository = repository;
this.currentUser = currentUser;
}
public CompetencyModel BuildNewCompetencyModel(int careerPlanningFormID, string newActionPlanText)
{
var competency = new Competency
{
CareerPlanningFormID = careerPlanningFormID,
CompetencyOptionID = null,
ActionPlan = newActionPlanText
};
this.repository.Add(competency);
this.repository.Commit();
return new CompetencyModel
{
CompetencyID = competency.CompetencyID,
CompetencyOptionID = competency.CompetencyOptionID,
ActionPlan = competency.ActionPlan,
CompetencyOptions = this.GetCompetencyOptionsForCareerPlanningFormID(careerPlanningFormID)
};
}
}
Now, the partial for NewCompetencyTemplate: Views/CPF/NewCompetencyTemplate.cshtml
This is very simple, it simply renders the same editor template as above, for the last competency in the collection (which we just added)
#model MyNamespace.Models.CareerPlanningForm.SelectCompetenciesViewModel
#Html.EditorFor(m => m.Competencies[Model.Competencies.Count() - 1])
When the ajax call succeeds, it will receive this partial back from the controller action method it called. It then takes the partial and appends it to the competencies table body
// snippet from ajax call above
competenciesTableBody.append(elements);
I hope this helps. Let me know if you have any additional questions.
While you're correct that you can do it just by returning a partial view containing the updated content, you may also consider using jQuery's load method.
Look here, in particular at the "loading page fragments" section. Basically you can just get the original page again and jQuery will "extract" the content you want as long as it can be targetted by a selector (such as a div id).
Note, this solution is not suitable in all cases as there will be redundant markup in the response from the server because you will be discarding the rest of the page content and just using the updated part.

Having trouble getting my dropdownlist to display in my view

I am basically trying to display a dropdownlist on my data entry view, and the dropdownlist keeps giving me the error "An expression tree may not contain a dynamic operation". I have added "#Model MyModel" to the top of my view, but still can't get past this error. Does anyone have an idea of how to resolve this issue? I have a controller that looks like this
using MvcApplication1.Models;
namespace MvcApplication1.Controllers
{
public class MyController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult EnterInfo()
{
GetUT myGetUT = new GetUT();
ViewBag.uts = GetOptions();
return View(myGetUT);
}
[HttpPost]
public ActionResult EnterInfo(GetUT myGetUT)
{
ViewBag.uts = GetOptions();
return View(myGetUT);
}
private List<UT> GetOptions()
{
List<UT> uts = new List<UT>();
uts.Add(new UT() { ID = 1, Name = "1st" });
uts.Add(new UT() { ID = 2, Name = "2nd" });
uts.Add(new UT() { ID = 3, Name = "3rd" });
uts.Add(new UT() { ID = 4, Name = "4th" });
return uts;
}
}
}
and a view that looks like
#Model MyModel
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<title>EnterInfo</title>
</head>
<body>
<div>
#Html.DropDownListFor(x => x.UTID, new SelectList(ViewBag.uts, "ID", "Name", Model.UTID))
Enter an Amount :-<input type="text" name="Amount" /><br />
<input type="submit" value="Submit Info" />
</div>
</body>
</html>
Thanks for all the help.
Well, ViewBag is a dynamic type, so I assume that is what it is complaining about. Try putting public List<UT> UTs { get; set; } as a property on MyModel and change your helper to use the UTs property from your model. Like this:
Controller:
public ActionResult EnterInfo()
{
GetUT myGetUT = new GetUT();
myGetUT.UTs = GetOptions();
return View(myGetUT);
}
View:
#Html.DropDownListFor(x => x.UTID,
new SelectList(Model.UTs, "ID", "Name", Model.UTID))`
Edit: If it isn't obvious, your view should be typed to a GetUT type (because that's what you are passing in to the View() function in the EnterInfo action) - I assume that's what you mean when you said #Model MyModel. If not, change it to #Model GetUT and put the property on the GetUT class.

How to display additional readonly view data in Edit view MVC

I have a View that handles the Edit action for editing a weekly update on your weight and nutrition. Editing a singular model is all good. I am using EditorFor to create the fields.
My problem is that I want to also display a read only version of last week's results as a guide but I would like to use DisplayFor so that it formats bools to be disabled checkboxes and formats the dates based on my formatting in the model. I added the model to the Viewbag and tried to access it by using #Html.DisplayFor(x => x.BodyWeight, (myproject.Models.WeeklyReport)ViewBag.LastReport) however it just brings up the data in the model that I sent to the view and not the Viewbag data. What is the best method to display this kind of data while keeping the constraints/formatting of the model intact?
Thanks.
View
#model myproject.Models.WeeklyReport
<h2>Weekly Report - Week 1</h2>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<table class="weeklyreport">
<tr>
<th>Week</th>
<td class="result-bold">Goals</td>
<td>Current Week</td>
</tr>
<tr>
<th>Body Weight</th>
<td class="result-bold">#Html.DisplayFor(x => x.BodyWeight, (myproject.Models.WeeklyReport)ViewBag.Goals)</td>
<td>#Html.EditorFor(model => model.BodyWeight)
#Html.ValidationMessageFor(model => model.BodyWeight)</td>
</tr>
<tr>
<th>Diary Reviewed</th>
<td class="result-bold">#Html.DisplayFor(x => x.DiaryReviewed, (myproject.Models.WeeklyReport)ViewBag.Goals)</td>
<td>#Html.EditorFor(model => model.DiaryReviewed)
#Html.ValidationMessageFor(model => model.DiaryReviewed)</td>
</tr>
</table>
Controller
public ActionResult Edit(int id)
{
WeeklyReport goal = new WeeklyReport()
{
BodyWeight = 60,
DiaryReviewed = true
};
WeeklyReport rpt = new WeeklyReport()
{
BodyWeight = 68,
DiaryReviewed = false
};
ViewBag.LastReport = goal;
return View(rpt);
}
You can create a viewmodels or partialViews and pass the viewbag item to the partial.
An example of viewmodel usage
public class WeeklyReportViewModel
{
public WeeklyReport LastReport { get; set; }
public WeeklyReport Report { get; set; }
}
combine the reports
public ActionResult Edit(int id)
{
WeeklyReport goal = new WeeklyReport()
{
BodyWeight = 60,
DiaryReviewed = true
};
WeeklyReport rpt = new WeeklyReport()
{
BodyWeight = 68,
DiaryReviewed = false
};
WeeklyReportViewModel viewmodel = new WeeklyReportViewModel()
{
LastReport = goal,
ActualReport = rpt
}
return View(viewmodel);
}
In the view
#model myproject.ViewModels.WeeklyReportViewModel
#Html.DisplayFor(x => x.LastReport.BodyWeight)

How to handle Dropdownlist in mvc asp .net

this is my Model1 class
namespace chetan.Models
{
public class Model1
{
public string selectedItem { get; set; }
public IEnumerable<SelectListItem> items { get; set; }
}
}
this is my controller class
public class HomeController : Controller
{
private rikuEntities rk = new rikuEntities();
public ActionResult Index()
{
var model = new Model1
{
items = new[]
{
new SelectListItem { Value = "Theory", Text = "Theory" },
new SelectListItem { Value = "Appliance", Text = "Appliance" },
new SelectListItem { Value = "Lab", Text = "Lab" }
}
}; return View(model);
}
public ActionResult viewToController(Model1 m)
{
string getSelectedName = m.selectedItem;
return Content(getSelectedName);
}
}
this is my view...
#using (Html.BeginForm("viewToController", "Home"))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>emp</legend>
<div class="editor-field">
#Html.DropDownListFor(x => x.selectedItem,
new SelectList(Model.items, "Value", "Text"))
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
i want to add a drop downlist and i want to use selected value in viewToController action of homeController. and there is also one error in View page is "an expression tree may not contain dynamic operation" in (x=>x.selectedItem). Please solve my problem .
I don't understnad what you exactly need. You want to dynamicly add items to the drop down from the database?
I'm big fan of jQuery. You can do everything what you want with HTML using jQuery. So if you are looking how to automaticly add items to the drop down, take look at this: How do I add options to a DropDownList using jQuery?

Resources