Read selected value of drop down in controller - asp.net-mvc-3

Requirment: I have a drop down and a table on my cshtml page. The drop down displays a list of vendors and the details corresponding to selected vendor are displayed in table. I am submitting the form using jquery when the value of the drop down changes.
Problem: How to cath selected value of drop down in controller?
Code:
#Html.DropDownList("VendorList", new SelectList(Model.vendorList, "vendorId", "vendorName"))
#using (Html.BeginForm("VendorDetails", "VendorLookUp", FormMethod.Post, new { id = "vendorDetailsForm" }))
{
<div class="margin-10-top" >
<table id= "VendorDetail" class="VendorDetail">
........ details of vendor.........
</table>
</div>
}
<script type="text/javascript" charset="utf-8">
$(document).ready(function () {
$('#VendorList').change(function () {
$('#vendorDetailsForm').submit();
});
});
</script>
the code in my controller is:
[AcceptVerbs("POST")]
public ActionResult SearchResult(FormCollection collection)
{
try
{
string vendorName = collection["searchItem"].ToString();
vendorName = vendorName.Trim();
List<Vendor> vendorList = Queries.compiledVendorQuery(dbContext, vendorName).ToList<Vendor>();
if(vendorList.Count() == 0)
return View("EmptySearch");
Vendor selectedVendor = vendorList[0];
VendorDetails vendorDeatils = Queries.compiledVendorDetailsQuery(dbContext, selectedVendor.vendorId.ToString()).FirstOrDefault();
VendorResult vendorResult = new VendorResult();
vendorResult.vendorList = vendorList;
vendorResult.vendorDetails = vendorDeatils;
return View(vendorResult);
}
catch (Exception e)
{
return View("EmptySearch");
}
}
[AcceptVerbs("POST")]
public ActionResult VendorDetails(FormCollection collection)
{
**here comes the control after postback
require value of the selected item**
Vendor selectedVendor = ??
VendorDetails vendorDeatils = Queries.compiledVendorDetailsQuery(dbContext, selectedVendor.vendorId.ToString()).FirstOrDefault();
VendorResult vendorResult = new VendorResult();
vendorResult.vendorDetails = vendorDeatils;
return View(vendorResult);
}

Since you're not really using the FormCollection, you could just use an int (or whatever the ID is on your model) in your action method:
[HttpPost]
public ActionResult VendorDetails(int vendorId = 0)
{
Vendor selectedVendor = // Load from your data source using vendorId
... // Do the rest of your work
}
In your HTML, move your #Html.DropDownListFor() into your form, rename it to the argument name, then submit the form as normal. Since the display doesn't seem to have any affect on what gets sent to the server, I would pull this out and just leave the #Html.DropDownListFor() in the form:
#using (Html.BeginForm("VendorDetails", "VendorLookUp", FormMethod.Post, new { id = "vendorDetailsForm" }))
{
#Html.DropDownList("vendorId", new SelectList(Model.vendorList, "vendorId", "vendorName"))
}
<div class="margin-10-top" >
<table id= "VendorDetail" class="VendorDetail">
........ details of vendor.........
</table>
</div>
<script type='text/javascript'>
$(document).ready(function () {
$('#vendorId').change(function () {
$('#vendorDetailsForm').submit();
});
});
</script>
Edit
Take a look at this article about MVC's model binding for an idea of how vendorId gets injected from the submitted form. Basically, the binder will match property names with the name attribute (by default) to your model. In this case, our model is just an int.

Related

AJAX pagedlist with partial view

I can't quite figure out how to get a partial view to render a paged list using ajax.
The closest I've got it to working is the example from Using paging in partial view, asp.net mvc
I'm basically trying to create a page with a list of comments per user where the page can be changed in the same way as the answers tab on the stackoverflow users page.
The paging works fine the on the first pager click, but then the the partial view is all that is returned once I click on the pager again.
Controller:
public class ProductController : Controller
{
public IQueryable<Product> products = new List<Product> {
new Product{ProductId = 1, Name = "p1"},
new Product{ProductId = 2, Name = "p2"},
new Product{ProductId = 3, Name = "p3"},
new Product{ProductId = 4, Name = "p4"},
new Product{ProductId = 5, Name = "p5"}
}.AsQueryable();
public object Index()
{
return View();
}
public object Products(int? page)
{
var pageNumber = page ?? 1; // if no page was specified in the querystring, default to the first page (1)
var onePageOfProducts = products.ToPagedList(pageNumber, 3); // will only contain 25 products max because of the pageSize
ViewBag.OnePageOfProducts = onePageOfProducts;
return PartialView("_Products");
}
}
Views:
Index.cshtml:
<link href="/Content/PagedList.css" rel="stylesheet" type="text/css" />
<h2>List of Products</h2>
<div id="products">
#Html.Action("Products", "Product")
</div>
#section scripts{
<script type="text/javascript">
$(function() {
$('#myPager').on('click', 'a', function() {
$.ajax({
url: this.href,
type: 'GET',
cache: false,
success: function(result) {
$('#products').html(result);
}
});
return false;
});
});
</script>
}
_Products.cshtml:
#using PagedList.Mvc;
#using PagedList;
<ul>
#foreach(var product in ViewBag.OnePageOfProducts){
<li>#product.Name</li>
}
</ul>
<!-- output a paging control that lets the user navigation to the previous page, next page, etc -->
<div id="myPager">
#Html.PagedListPager((IPagedList)ViewBag.OnePageOfProducts, page => Url.Action("Products", new { page }))
</div>
Model
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
}
Can anyone show me what I'm doing wrong?
I ended up using the unobtrusive ajax example from the pagedlist source [https://github.com/troygoode/PagedList][1]
partial view:
#using PagedList;
#using PagedList.Mvc;
<ul id="names" start="#ViewBag.Names.FirstItemOnPage">
#foreach(var i in ViewBag.Names){
<li>#i</li>
}
</ul>
#Html.PagedListPager((IPagedList)ViewBag.Names, page => Url.Action("Index", new { page }), PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing( new AjaxOptions(){ HttpMethod = "GET", UpdateTargetId = "unobtrusive"}))
Index:
#{
ViewBag.Title = "Unobtrusive Ajax";
}
#using PagedList;
#using PagedList.Mvc;
#Styles.Render("~/Content/PagedList.css")
<h2>Unobtrusive Ajax</h2>
<p>Example of paging a list:</p>
<div id="unobtrusive">
#Html.Partial("UnobtrusiveAjax_Partial")
</div>
Controller:
public class UnobtrusiveAjaxController : BaseController
{
// Unobtrusive Ajax
public ActionResult Index(int? page)
{
var listPaged = GetPagedNames(page); // GetPagedNames is found in BaseController
if (listPaged == null)
return HttpNotFound();
ViewBag.Names = listPaged;
return Request.IsAjaxRequest()
? (ActionResult)PartialView("UnobtrusiveAjax_Partial")
: View();
}
}
Just in case, since the original question wasn't answered. I guess the problem was that on click handlers weren't reattached to the new pager elements generated by AJAX request. I also don't like unobstrusive AJAX solution in this case, since pager id is hardcoded in the nested view while passing it in some other way may be too cumbersome.
<script type="text/javascript">
// better not to clutter global scope of course, just for brevity sake
var attachHandlers = function() {
$('#myPager a').click(function() {
$('#myPager').load(this.href, function() {
attachHandlers();
});
return false;
});
};
$(document).ready(function () {
attachHandlers();
});
</script>

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.

pass input text from view to controller

i know there are a lot questions like this but i really cant get the explanations on the answers... here is my view...
<script type="text/javascript" language="javascript">
$(function () {
$(".datepicker").datepicker({ onSelect: function (dateText, inst) { },
altField: ".alternate"
});
});
</script>
#model IEnumerable<CormanReservation.Models.Reservation>
#{
ViewBag.Title = "Index";
}
<h5>
Select a date and see reservations...</h5>
<div>
<div class="datepicker">
</div>
<input name="dateInput" type="text" class="alternate" />
</div>
i want to get the value of the input text... there's already a value in my input text because the datepicker passes its value on it... what i cant do is to pass it to my controller... here is my controller:
private CormantReservationEntities db = new CormantReservationEntities();
public ActionResult Index(string dateInput )
{
DateTime date = Convert.ToDateTime(dateInput);
var reservations = db.Reservations.Where(r=>r.Date==date).Include(r => r.Employee).Include(r => r.Room).OrderByDescending(r => r.Date);
return View(reservations.ToList());
}
i am trying to list in my home page the reservations made during the date the user selected in my calender in my home page....
I don't see a Form tag in your View...or are you not showing the whole view? hard to tell.. but to post to your controller you should either send the value to the controller via an ajax call or post a model. In your case, your model is an IEnumerable<CormanReservation.Models.Reservation> and your input is a date selector and doesn't look like it is bound to your ViewModel. At what point are you posting the date back to the server? Do you have a form with submit button or do you have an ajax call that you aren't showing?
Here is an example of an Ajax request that could be called to pass in your date
$(function () {
$(".datepicker").onselect(function{
searchByDate();
})
});
});
function searchbyDate() {
var myDate = document.getElementById("myDatePicker");
$.ajax({
url: "/Home/Search/",
dataType: "json",
cache: false,
type: 'POST',
data: { dateInput: myDate.value },
success: function (result) {
if(result.Success) {
// do something with result.Data which is your list of returned records
}
}
});
}
Your datepicker control needs something to reference it by
<input id="myDatePicker" name="dateInput" type="text" class="alternate" />
Your action could then look something like this
private CormantReservationEntities db = new CormantReservationEntities();
public JsonResult Search(string dateInput) {
DateTime date = Convert.ToDateTime(dateInput);
var reservations = db.Reservations.Where(r=>r.Date==date).Include(r => r.Employee).Include(r => r.Room).OrderByDescending(r => r.Date);
return View(reservations.ToList());
return Json(new {Success = true, Data = reservations.ToList()}, JsonRequestBehaviour.AllowGet());
}
Update
If you want to make this a standard post where you post data and return a view then you need to make changes similar to this.
Create a ViewModel
public class ReservationSearchViewModel {
public List<Reservation> Reservations { get; set; }
public DateTime SelectedDate { get; set; }
}
Modify your controller actions to initially load the page and then be able to post data return the View back with data
public ActionResult Index() {
var model = new ReservationSearchViewModel();
model.reservations = new List<Reservation>();
return View(model);
}
[HttpPost]
public ActionResult Index(ReservationSearchViewModel model) {
if(ModelState.IsValid)
var reservations = db.Reservations.Where(r => r.Date = model.SelectedDate).Include(r => r.Employee).Include(r => r.Room).OrderByDescending(r => r.Date);
}
return View(model)
}
Modify your view so that you have a form to post to the Index HttpPost action
#model CormanReservation.Models.ReservationSearchViewModel
<h5>Select a date and see reservations...</h5>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
#Html.EditorFor(model => model.SelectedDate)
#Html.EditorFor(model => model.Reservations) // this may need to change to a table or grid to accomodate your data
<input type="submit" value="Search" />
}

Set div id dyanmically in each item in the loop to be referred in Ajax.ActionLink method?

Do we have better ways to handle it?
#foreach (var item in Model)
{
<div id="divDetail#{#item.CategoryId}"/>
#Ajax.ActionLink(
item.CategoryName,
"GetDetails",
new { id = item.CategoryId },
new AjaxOptions() { UpdateTargetId = string.Format("divDetail{0}", item.CategoryId) })
}
</div>
}
I would use the HTML.ActionLink helper method to generate the link and then use my custom jQuery ajax call to get the data. The advantage of doing this is i have full control so that i can do some manipulation of the response data before showing in the detail div.
I added a CSS class to the link so that i can be more specific (in selection of element) when binding my functionality.
#foreach (var item in Model)
{
<div id='divDetail#(item.ID)'></div>
#Html.ActionLink(item.CategoryName, "GetDetails", new { #id = item.CategoryId}, new {#id= "link-"+item.CategoryId, #class="lnkItem" })
}
and the script is
<script type="text/javascript">
$(function () {
$(".lnkItem").click(function (e) {
e.preventDefault();
var itemId = $(this).attr("id").split("-")[1]
$.get($(this).attr("href"), { id: itemId }, function (data) {
//i am free to do anything here before showing the data !
$("#divDetail" + itemId).html(data);
})
});
});
</script>

Resources