In my controller I set the SelectedYear field as follows;
[Authorize(Roles = "Administrator, AdminAccounts")]
public ActionResult BankHoliday(int? id)
{
BankHolidayViewModel BankHolidayViewModel = new BankHolidayViewModel();
int year = DateTime.Now.Year;
if (id.HasValue)
year = id.GetValueOrDefault();
BankHolidayViewModel.SelectedYear = year;
BankHolidayViewModel.GetYearData(year);
return View(BankHolidayViewModel);
}
Whenever I step through the code, the SelectedYear value is always correct on the return statement. In the Year DropDownList below, this is the Value field that sets the selected item in the DropDownList;
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/AdminAccounts.master" Inherits="System.Web.Mvc.ViewPage<SHP.WebUI.Models.BankHolidayViewModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
BankHoliday
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="AdminAccountsContent" runat="server">
<h3>Bank Holiday Administration</h3>
<p>Select the year: <%: Html.DropDownListFor(model => model.SelectedYear, Model.YearList)%></p>
<script language="javascript" type="text/javascript">
$(function () {
$("#SelectedYear").change(function () {
var year = $("#SelectedYear").val();
$("#wholepage").load("/AdminAccounts/BankHoliday/" + year);
});
});
* EDIT: ADDED INFO *
The YearList is defined as follows;
public class BankHolidayViewModel
{
public IList<BankHolidayExtended> BankHolidays { get; set; }
public IList<BankHolidayYear> Years { get; set; }
public int SelectedYear = DateTime.Now.Year;
public IEnumerable<SelectListItem> YearList
{
get
{
return this.Years.Select(item => new SelectListItem
{
Text = item.Year.ToString(),
Value = item.Year.ToString()
});
}
}
* END EDIT *
For some reason when the View is rendered, there is no selected item in the dropdownlist. How do I fix this?
Your "Inherits" needs to inherit your ViewModel.
Inherits="System.Web.Mvc.ViewPage<ViewModels.BankHolidayViewModel>"
When you create the YearList, you also need to set the selected value. Something like
YearList = new SelectList(GetYears(), "year", "year", SelectedYear);
EDIT
In your view you can set the selected value:
<%= Html.DropDownListFor(x => x.SelectedYear, new SelectList(Model.YearList, "Value", "Text", Model.SelectedYear))%>
Or you can do it when you originally set up the Year List as I put above. GetYears() would return the years for the list.
public SelectList YearList { get; private set; }
public class BankHolidayViewModel(int year)
{
YearList = new SelectList(GetYears(), "Value", "Text", year);
Then when you return the view
return View(new BankHolidayViewModel(year));
I didn't really want to change my object to a SelectList, although I am sure that would have worked.
Instead I got the answer by changing the getter;
public IEnumerable<SelectListItem> YearList
{
get
{
return this.Years.Select(item => new SelectListItem
{
Text = item.Year.ToString(),
Value = item.Year.ToString(),
Selected = item.Year == this.SelectedYear
});
}
}
Related
I am trying to pass the Model data from a View (and PartialView within the View) back to the Controller upon HttpPost. (Adapted from Pass SelectedValue of DropDownList in Html.BeginForm() in ASP.NEt MVC 3)
Why? I want to show a list of assets each with a DropDownList and number of options. Upon submission of form to read the selected items from DropDownList.
My 2 (simplified) models:
public class Booking
{
public int BookingID { get; set; }
public int StoreID { get; set; }
...
public IEnumerable<AssetShort> Assets { get; set; }
}
and
public class AssetShort
{
public int AssetID { get; set; }
....
public int SelectedAction { get; set; }
public IEnumerable<SelectListItem> ActionList { get; set; }
}
In my Booking Controller > Create I build the List:
public ActionResult Booking(int id)
{
// get myBag which contains a List<Asset>
// booking corresponds to 'id'
var myAssets = new List<AssetShort>();
foreach (var a in myBag.Assets)
{
var b = new AssetShort();
b.AssetID = a.ID;
b.SelectedAction = 0;
b.ActionList = new[]
{
new SelectListItem { Selected = true, Value = "0", Text = "Select..."},
new SelectListItem { Selected = false, Value = "1", Text = "Add"},
new SelectListItem { Selected = false, Value = "2", Text = "Remove"},
new SelectListItem { Selected = false, Value = "3", Text = "Relocate"},
new SelectListItem { Selected = false, Value = "4", Text = "Upgrade"},
new SelectListItem { Selected = false, Value = "5", Text = "Downgrade"}
};
myAssets.Add(b);
};
var model = new BookingRequirementsViewModel
{
BookingID = booking.ID,
StoreID = booking.StoreID,
Assets = myAssets.ToList(),
};
return View(model);
My View:
#model uatlab.ViewModels.BookingRequirementsViewModel
#{
ViewBag.Title = "Booking step 2";
}
<h4>Your booking ref. #Model.BookingID</h4>
#using (Html.BeginForm("Booking2", "Booking", FormMethod.Post))
{
<fieldset>
#Html.AntiForgeryToken()
#Html.HiddenFor(model => model.StoreID)
#Html.Partial("_Assets", Model.StoreAssets)
<input type="submit" value="Cancel" class="btn btn-default" />
<input type="submit" value="Next" class="btn btn-default" />
</fieldset>
}
The Partial View includes
#foreach (var item in Model)
{
<tr>
<td>#item.Name</td>
<td>#item.Number</td>
<td>#Html.DropDownListFor(modelItem=>item.SelectedAction, item.ActionList)</td>
</tr>
}
So, all this works fine in the browser and I can select dropdowns for each asset listed but when I submit the only value posted back is the StoreID as it is in a "HiddenFor".
The booking2 controller has the model for a parameter:
public ActionResult Booking2(BookingRequirementsViewModel model)
{
//loop through model.Assets and display SelectedActions
}
Let me make it clear what the problems is - in Booking2 controller the Model is null when viewed in Debug mode and I get error "Object reference not set to an instance of an object."
Any ideas please how to pass back the Model to controller from view?
Regards
Craig
You need to create an EditorTemplate for AssetShort. I also suggest moving ActionList to the BookingRequirementsViewModel so your not regenerating a new SelectList for each AssetShort
The models you have posted aren't making sense. Your controller has var model = new BookingRequirementsViewModel { ..., Assets = myAssets.ToList() }; but in the view you refer to #Html.Partial("_Assets", Model.StoreAssets)? Are these 2 different properties. I will assume that StoreAssets is IEnumerable<AssetShort>
/Views/Shared/EditorTemplates/AssetShort.cshtml
#model AssetShort
<tr>
<td>#Html.DispayFor(m => m.Name)</td>
....
<td>
#Html.DropDownListFor(m => m.SelectedAction, (IEnumerable<SelectListItem>)ViewData["actionList"], "--Please select--")
#Html.ValidationMessageFor(m => m.SelectedAction)
</td>
</tr>
In the main view
#model uatlab.ViewModels.BookingRequirementsViewModel
....
#using (Html.BeginForm()) // Not sure why you post to a method with a different name
{
....
#Html.HiddenFor(m => m.StoreID)
#Html.EditorFor(m => m.StoreAssets, new { actionList = Model.ActionList })
....
}
In the controller
public ActionResult Booking(int id)
{
....
var model = new BookingRequirementsViewModel
{
BookingID = booking.ID,
StoreID = booking.StoreID,
Assets = myBag.Assets.Select(a => new AssetShort()
{
AssetID = a.ID,
SelectedAction = a.SelectedAction, // assign this if you want a selected option, otherwise the "--Please select--" option will be selected
....
})
};
ConfigureViewModel(model); // Assign select list
return View(model);
}
And a separate method to generate the SelectList because it needs to be called in the GET method and again in the POST method if you return the view. Note use the overload of DropDownListFor() to generate the option label (null value) as above, and there is no point setting the Selected property (the value of SelectedAction determines what is selected, not this)
private ConfigureViewModel(BookingRequirementsViewModel model)
{
model.ActionList = new[]
{
new SelectListItem { Value = "1", Text = "Add"},
....
new SelectListItem { Value = "5", Text = "Downgrade"}
};
}
and the POST
public ActionResult Booking(BookingRequirementsViewModel model)
{
if (!ModelState.IsValid)
{
ConfigureViewModel(model); // Re-assign select list
return View(model);
}
// save and redirect
}
I recommend also making SelectedAction nullable with the [Required] attribute so you get client and server side validation
public class AssetShort
{
public int AssetID { get; set; }
....
[Required]
public int? SelectedAction { get; set; }
}
I have a MVC3 application that populates a dropdownlist from a Model. When I select an item from the list , i would like to Update the url ('/Edit/4') on a single 'edit' link which will allow me display the edit view, i.e rather than use a template which creates edit links for all records in the model, I would like to use one edit link and then update it as items are selected in the dropdownlist. I have been able to achieve some of this using jquery , I would like to do it in C# code using MVC.
Any thoughts??
I created a Sample code. I have an area. Highlighted part is your concerned code.
Controller
public class DropdownController : Controller
{
[HttpGet]
public ActionResult DropDownListFor()
{
Practise.Areas.MyPractise.Models.Dropdown d = new Models.Dropdown();
return View(d);
}
public ActionResult Edit(string Val)
{
return View();
}
}
Model
public class Dropdown
{
[Required(ErrorMessage = "Please select state")]
public string StateId { get; set; }
public int MyProperty { get; set; }
public List<SelectListItem> States
{
get
{
return new List<SelectListItem>()
{
new SelectListItem
{
Text = "Punjab",
Value = "Pb",
Selected = false
},
new SelectListItem
{
Selected = false,
Value = "HM",
Text = "Himachal"
}
};
}
}
}
DropDownListFor View
#model Practise.Areas.MyPractise.Models.Dropdown
<!DOCTYPE html>
<html>
<head>
<title>DropDownListFor</title>
<script src="/Scripts/jquery-1.7.1.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function () {
$('#StateId').change(function () {
if($('#StateId option:selected').val() == "")
$('.edit').attr('href', "#");
else
$('.edit').attr('href',
"#Url.Action("Edit", "Dropdown",
new RouteValueDictionary(new { area = "MyPractise" }))"
+ "/" + $('#StateId option:selected').text());
});
});
</script>
</head>
<body>
#using (Html.BeginForm("DropDownListFor", "DropDown", FormMethod.Post,
new { id = "DropDownList" }))
{
#Html.DropDownListFor(m => m.StateId, Model.States, "select");
#Html.ValidationMessageFor(m => m.StateId);
<a class="edit" href="#">Edit</a>
<input type="submit" name="Submit" value="Submit" />
}
</body>
</html>
Area registration under MyPractiseAreaRegistration class under highlighted area
public class MyPractiseAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "MyPractise";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"MyPractise_default1",
"MyPractise/{controller}/{action}/{Val}",
new { action = "Index", Val = UrlParameter.Optional }
);
context.MapRoute(
"MyPractise_default",
"MyPractise/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
}
I am using a WebGrid, which i bind to a List of objects containing information about deliveries. I want to be able to filter said WebGrid using a DropDownList containing Customers. When I select a Customer in the DropDownList the change-method sends an Ajax call which is supposed to get the new items for the WebGrid.
The call is successful, but nothing happens. The WebGrid doesn't change at all. I even tried sending an Ajax call identical to the ones sent when sorting the list. But nothing happens.
What am I doing wrong here?
ViewModel:
public class DeliveriesViewModel : PageViewModel<DeliveriesPage>
{
public DeliveriesViewModel(DeliveriesPage currentPage) : base(currentPage)
{
DeliveryItems = new List<DeliveryItem>();
}
public List<DeliveryItem> DeliveryItems { get; set; }
public List<SelectListItem> Customers { get; set; }
}
Controller:
public ActionResult Index(DeliveriesPage currentPage, string customer)
{
var model = new DeliveriesViewModel(currentPage);
model.Customers = _deliveryService.GetCustomers();
model.DeliveryItems = customer == null ? _deliveryService.GetDeliveryItems() : _deliveryService.GetDeliveryItems(customer);
return View(model);
}
View:
#model DeliveriesViewModel
<h1>#Model.CurrentPage.PageName</h1>
#Html.DropDownList("customerDropDown", Model.Customers)
#Html.Partial("_grid", Model)
<script type="text/javascript">
$("#customerDropDown").change(function () {
$.get("?Customer="+$("#customerDropDown").val());
});
</script>
_grid partial View:
#model DeliveriesViewModel
#{
var grid = new WebGrid(Model.DeliveryItems, canPage:true, canSort: true, ajaxUpdateContainerId:"container-grid");
}
<div id="container-grid">
#grid.GetHtml(
columns: grid.Columns(
grid.Column("DeliveryId"),
grid.Column("CustomerName"),
grid.Column("ShipNumber"),
grid.Column("ShipName"),
grid.Column("Product"),
grid.Column("PlannedWeight"),
grid.Column("TotalWeight"),
grid.Column("ShipStatus"),
grid.Column("TransportTo"),
grid.Column("TransportFrom"),
grid.Column("RevDate"),
grid.Column("ShipStemDept"),
grid.Column("ShipRealDept"),
grid.Column("ShipStemArr"),
grid.Column("ShipRealArr"),
grid.Column("TranspMonth"),
grid.Column("TranspYear")
))
</div>
$.get("?Customer="+$("#customerDropDown").val()); sends an AJAX call to the server and that's about it. You haven't subscribed to the success callback in order to update your DOM. So it is not surprising that nothing happens.
So try like this:
<script type="text/javascript">
$('#customerDropDown').change(function () {
var url = '#Url.Action("index")';
$.get(url, { customer: $(this).val() }, function(result) {
$('#container-grid').html(result);
});
});
</script>
Notice how I have used the UrlHelper to calculate the correct url to your controller action, I have then passed the selected value of the dropdown as second parameter to the $.get method and last but not least I have subscribed to the success callback of the ajax request and updated the #container-grid div with the results returned by the controller action.
Also since you are calling this action with AJAX, you should return only a PartialView from it and not an entire View. This partial view should contain your grid. Otherwise you will end up with duplicate layout injected into the div.
Model
public class EmployerTestResultsModel
{
[Display(Name = "Employer List")]
public IEnumerable<SelectListItem> EmployerList { get; set; }
[Required]
public string SelectedEmployerId { get; set; }
public List<EmployerTestResultsModel> EmployerGrid { get; set; }
public Int64 FileId { get; set; }
[Display(Name = "File Name")]
public string FileName { get; set; }
[DataType(DataType.Date)]
public DateTime Date { get; set; }
[Display(Name = "Scheme Id")]
public string SchemeId { get; set; }
public string Status { get; set; }
[Display(Name = "Validation Error Report")]
public string ValidationErrorReport { get; set; }
}
controller
[HttpGet]
public ActionResult EmployerTestResults()
{
EmployerTestResultsModel model = new EmployerTestResultsModel();
ViewBag.HideSection = true;
model.EmployerList = (from d in _context.Employers
select new System.Web.Mvc.SelectListItem
{
Text = d.EmployerName,
Value = d.EmployerId
});
model.EmployerGrid = (from efd in _context.EmployerFileDatas
// join efhd in _context.EmployerFileHeaderDetails on efd.FileDataIdentityKey equals efhd.FileDataIdentityKey
orderby efd.EmployerId , efd.Timestamp
select new EmployerTestResultsModel
{
FileId = efd.FileDataIdentityKey,
FileName = efd.FileName,
Date = efd.Timestamp,
//SchemeId = efhd.SchemeId,
Status = efd.ValidationStatus,
ValidationErrorReport = "View"
}).ToList();
return View("EmployerTestResults", model);
}
View:
#model EFITestHarness.Models.EmployerTestResultsModel
#using System.Web.Helpers;
#{
ViewBag.Title = "EmployerTestResults";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<script src="~/Scripts/jquery-1.7.1.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
#using (Html.BeginForm("EmployerTestResults", "Home", FormMethod.Post, new { #class = "form-horizontal" }))
{
<div class="text-danger" style="font-size:large;">
#Html.ValidationSummary(true)
</div>
<div class="form-group ">
#Html.LabelFor(s => s.EmployerList, null, new { #class = "col-md-2 control-label" })
<div class="col-md-3">
#Html.DropDownListFor(s => s.SelectedEmployerId, Model.EmployerList, "----All----", new { style = "width:250px", id = "ddl", #class = "dropdown1" })
#Html.ValidationMessageFor(s => s.EmployerList, null, new { #class = "text-danger" })
</div>
</div>
<div id="EmployeeViewGrid">
#Html.Partial("~/Views/EmployerView.cshtml", Model.EmployerGrid)
</div>
}
<script type="text/javascript">
$('#ddl').change(function (e) {
var employer = $('#ddl').val();
$.get('#Url.Action("Filter")', { id: employer }, function (result) {
$('#EmployeeViewGrid').html(result);
});
e.preventDefault();
});
</script>
Controller:
[HttpGet]
public ActionResult Filter(string id)
{
EmployerTestResultsModel model = new EmployerTestResultsModel();
List<EmployerTestResultsModel> objEmployerDetails = new List<EmployerTestResultsModel>();
objEmployerDetails = _repository.getEmployerDetails(id);
model.EmployerGrid = objEmployerDetails;
return PartialView("~/Views/EmployerView.cshtml", model.EmployerGrid);
}
partial View:
#model IEnumerable<EFITestHarness.Models.EmployerTestResultsModel>
#using System.Web.Helpers;
#{
ViewBag.Title = "EmployerTestResultsModel";
//Layout = "~/Views/Shared/_Layout.cshtml";
}
<script src="~/Scripts/jquery-1.7.1.js"></script>
<div id="gridposition" style="overflow: scroll; height: 300px; overflow-x: hidden;">
#{
var grid = new WebGrid(Model, canPage: true, rowsPerPage: 5, selectionFieldName: "selectedRow", ajaxUpdateContainerId: "gridposition"); grid.Pager(WebGridPagerModes.NextPrevious);
#grid.GetHtml(tableStyle: "webGrid",
footerStyle: "foot",
headerStyle: "webGridHeader",
alternatingRowStyle: "webGridAlt",
htmlAttributes: new { id = "positionGrid" },
selectedRowStyle: "select",
fillEmptyRows: true,
columns: grid.Columns(
grid.Column("FileName"), //the model fields to display
grid.Column("Date"),
grid.Column("SchemeId"),
grid.Column("Status"),
grid.Column("ValidationErrorReport", format: (item => Html.ActionLink((string)(#item.ValidationErrorReport).ToString(), "EmployerValidationResults", new { FileId = #item.FileId, #style = "color:blue;" })))
))
}
</div>
Below is the View:
<%# Page Title="" Language="C#"
MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<MvcApplication1.Models.Index>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Contact
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% using (Html.BeginForm())
{%>
<%=Html.DropDownListFor(x => x.SelectedFavColor, Model.DropDownItems)%>
<%= Html.ValidationMessageFor(x=> x.SelectedFavColor) %>
<input type="submit" value="submit" />
<%} %>
</asp:Content>
Below mentioned is the Model:
namespace MvcApplication1.Models
{
public class Index
{
[Range(0, 1000, ErrorMessage = "hello")]
public int SelectedFavColor { get; set; }
public IEnumerable<SelectListItem> DropDownItems { get; set; }
}
public class Colors
{
public int ColorID { get; set; }
public string ColorName { get; set; }
}
}
I am passing some Dropdown values in the View.
Below is the Controller action:
public ActionResult Contact()
{
List<MvcApplication1.Models.Colors> l = new List<Models.Colors>();
l.Add(new Models.Colors { ColorName = "-1", ColorID = -1 });
l.Add(new Models.Colors { ColorName = "a", ColorID = 0 });
l.Add(new Models.Colors { ColorName = "b", ColorID = 2 });
l.Add(new Models.Colors { ColorName = "c", ColorID = 3 });
l.Add(new Models.Colors { ColorName = "d", ColorID = 4 });
l.Add(new Models.Colors { ColorName = "e", ColorID = 4 });
l.Add(new Models.Colors { ColorName = "f", ColorID = 4 });
var model = new MvcApplication1.Models.Index
{
DropDownItems = l.Select(i => new SelectListItem
{
Text = i.ColorName,
Value = i.ColorID.ToString()
})
};
ViewData["records"] = model.DropDownItems;
return View(model);
}
[HttpPost]
public ActionResult Contact(Index posted, FormCollection collection)
{
posted.SelectedFavColor = Convert.ToInt16(collection["SelectedFavColor"]);
return View(posted);
}
Why is the DropDown values Null in the POST action?
The DropDownItems is null because it is not being POSTed in your form. You are only creating a drop down menu with this: Html.DropDownListFor(x => x.SelectedFavColor, Model.DropDownItems), which is not enough to post the collection.
Posting a collection, however, is rather tricky; but you can look into this.
One last thing: you already have the collection (since you are passing it to the View), so technically, you don't even need to POST it back to the server.
The simple reason why the values of DropDown are null is because they are not sent (POSTed) with the form (You can easily check it with Firebug Firefox extension). You will need to collect (repopulate) the values of dropdown list again to see those values in view.
Suggestion:
Do not return the view immediately after the POST. Typical pattern in ASP-MVC apps is Post-Redirect-Get. It will help you to avoid unnecesary re-POSTs of the form (e.g. on browser refresh button) - Why should I use PRG.
My generic list of products won't persist on the Post if I use UpdateModel but if I pass the ViewModel in as a parameter to my post action method it works? How can I get it to work through UpdateModel way?
I'm using asp.net fx3.5 mvc 1.0
Model
namespace PostingGenericListAndUpdateModel.ViewModels
{
public class Product
{
public string Name { get; set; }
public bool IsSelected { get; set; }
}
public class ProductViewModel
{
public int OrderId { get; set; }
public List<Product> Products { get; set; }
public ProductViewModel()
{
Products = new List<Product>();
Products.Add(new Product() { Name = "Widget 1", IsSelected = false });
Products.Add(new Product() { Name = "Widget 2", IsSelected = false });
}
}
}
View
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Index</h2>
<% using (Html.BeginForm())
{ %>
<% for (int i = 0; i < 2; i++)
{ %>
<%= Model.Products[i].Name %> <%= Html.CheckBox("Model.Products[" + i + "].IsSelected") %>
<% } %>
<input id="Submit1" type="submit" value="submit" />
<% } %>
</asp:Content>
Controller
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
ProductViewModel model = new ProductViewModel();
return View(model, new string[] { "OrderId", "Products" });
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(FormCollection form) //It will work if i accept the view model
{
ProductViewModel model = new ProductViewModel();
UpdateModel(model);
return View(model);
}
I see no reason for this parameter 'string sender'. Why do you need it?
The usual way to do this is indeed, by accepting the view model as a parameter to your Post method.
This is the way MVC passes the information from the view to your controller.
Your Post method line should be:
public ActionResult Index(ProductViewModel model)