how to pass dropdownlist selected value to controller - asp.net-mvc-3

binding of dropdownlist is as follows
#Html.DropDownListFor(model => model.vDeptCode, (SelectList)ViewBag.deptList, "- Select Department -", new { #class = "form-control", #id = "ddl_Dept" })
On the same view i Have add button also after clicking on add button i have to pass deptCode value to my Action for that i have done
function CreateNewVoucher()
{
window.location.href = "#Url.Action("Add", "Voucher", new { #vDeptCode =#Html.Raw(Model.vDeptCode) })";
}
but it always pass null value.
Please guide me how to pass value to my ActionResult in Controller

Look, How i am passing the DropdownList selected value to Controller Post Method.
I have add only necessary Codes here, Please follow this to get your Dropdown value.
Controller
[HttpGet]
public ActionResult Index()
{
AccountModel account = new AccountModel();
account.accountStatusList= ObjContext.Status.ToList();
return View(account);
}
Custom Model Class
public class AccountModel
{
public int selectedStatusId { get; set; }
public List<Status> accountStatusList { get; set; }
}
View
#model Nop.Models.AccountModel
#using (Html.BeginForm("Index", "ControllerName", FormMethod.Post))
{
<div class="row-fluid span12">
<div class="span3">
<p><strong>Account Status :</strong></p>
</div>
<div class="span5">
// Bind the dropdown by List.
#Html.DropDownListFor(model => model.selectedStatusId, new SelectList(Model.accountStatusList, "StatusId", "StatusName"), "--Select--")
</div>
</div>
<div class="row-fluid span12">
<button class="btn btn-inverse" type="submit" title="Search for Account"><span class="fa fa-search"> Search</span></button>
</div>
}
Controller Post Method
[HttpPost]
public ActionResult Index(AccountModel account)
{
int selectedID = account.selectedStatusId; // You can find your selectedID here..
return View(account);
}
See ViewBag vs Model.

Related

How does selected dropdown value get saved from View Component to main model?

ASP.NET Core 5 MVC web app. The question is HOW it works, not why it doesn't. I don't understand the mechanism and so don't want to see it fail from some "happy-fingers" coding accident in the future...
I have a main model that the controller expects on create:
public class ProductCategory : BaseClass
{
public int ProductId { get; set; }
public virtual Product Product { get; set; }
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
}
(BaseClass just has your typical record keeping fields).
I have a model for the view components; I need two, one for each dropdown, so you can easily imagine the other, with names modified to protect the innocent...:
Category:
public class CategoryList
{
public CategoryList()
{
Categories = new List<Category>();
}
public int CategoryId { get; set; }
[DisplayName("Categories")]
public List<Category> Categories { get; set; }
}
The category view component:
public class CategoryDropdownViewComponent : ViewComponent
{
private readonly ApplicationDbContext _db;
public CategoryDropdownViewComponent(ApplicationDbContext context)
{
_db = context;
}
public async Task<IViewComponentResult> InvokeAsync()
{
var items = await GetCategoriesAsync();
var TheView = "Default";
var list = new CategoryList();
if (items.Count == 0)
{
TheView = "CategoryMaintenanceLink";
}
else
{
items.Insert(0, new Category() { Id = 0, Name = "-- Please select an option --" });
list.Categories = items;
}
return View(TheView, list);
}
private Task<List<Category>> GetCategoriesAsync()
{
return _db.Category.ToListAsync();
}
}
And the default view for category (I store this and above in ~\Shared\Components\CategoryDropdown\):
#model CoolestProjectNameEver.Models.CategoryList
<p>
#Html.DropDownListFor(model => model.CategoryId, new SelectList(Model.Categories, "Id", "Name"), new { #class = "form-control" })
</p>
So, in my controller, I kick off create:
public IActionResult Create()
{
return View();
}
And in the Create view, amongst other things, I fire up the view components:
<div class="form-group">
<label asp-for="ProductId" class="control-label"></label>
#await Component.InvokeAsync("ProductDropdown")
</div>
<div class="form-group">
<label asp-for="CategoryId" class="control-label"></label>
#await Component.InvokeAsync("CategoryDropdown")
</div>
All works and the dropdown lists are filled. I can select options for both. Now the unknown part.
On to the POST method for Create:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(ProductCategory productCategory)
{
try
{
if (ModelState.IsValid) <--- breakpoint
{
_context.Add(productCategory);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
return View(productCategory);
}
The breakpoint will show the correct selected values for CategoryId and ProductId.
So the question is, did this work because of a name match in the VC model to main controller model, and it auto filled somehow?
1 if my ViewComponent model had, say SelectedValueId instead of CategoryId, then it would fail because of a mismatch?
2 How did the value from a separate model in an async ViewComponent get plugged into the main model on postback?
In fact,if you change your Create view code to:
<div class="form-group">
#await Component.InvokeAsync("ProductDropdown")
</div>
<div class="form-group">
#await Component.InvokeAsync("CategoryDropdown")
</div>
it will also successfully binding.
The model binding in asp.net core is matched according to the name. If the name matches, the corresponding attribute will be bound to the model.
In your ViewComponent, your code(model => model.CategoryId):
#Html.DropDownListFor(model => model.CategoryId, new SelectList(Model.Categories, "Id", "Name"), new { #class = "form-control" })
will be given name =CategoryId in the generated html code
Then the name CategoryId is also in your ProductCategory model, if their names match, the binding will be successful.

displaying validation message using Ajax.beginform in a partial

I am new in .NET mvc 5 environment.
I have a view:
Index.cshtml
#model Accounts.WebHost.Models.SendUsernameReminderInputModel
#using (Ajax.BeginForm("Index", "SendUsernameReminder", null, new AjaxOptions { InsertionMode = InsertionMode.Replace, UpdateTargetId = "validationList", LoadingElementId = "loader", OnSuccess = "onSuccess", OnFailure = "onFailure" }, new { #id = "validationForm", #class = "form-inline" }))
{
#Html.AntiForgeryToken()
<div class="form-group">
#Html.EditorFor(model => model.Email, new { htmlAttributes = new { #class = "form-control" } })
</div>
<div class="form-group hidden">
#Html.TextBoxFor(model => model.Tenant, new { #class = "form-control", #Value = "default" })
</div>
<button type="submit" class="btn btn-default">Submit</button>
}
<hr />
<div id="loader" class="alert" style="display:none">
<img src="~/Content/img/ajax-loader.gif" />
</div>
#Html.Partial("_UsernameValidation")
And a Partial view:
_UsernameValidation.cshtml
#model Accounts.WebHost.Models.SendUsernameReminderInputModel
<div id="validationList">
<table>
<tr>
<td>#Html.ValidationMessageFor(model => model.Email)</td>
<td>#Html.ValidationMessageFor(model => model.Tenant)</td>
</tr>
</table>
</div>
this is my Controller:
SendUsernameReminderController.cs
using Accounts.Entities.Models;
using Accounts.WebHost.Models;
using BrockAllen.MembershipReboot;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace Accounts.WebHost.Controllers
{
public class SendUsernameReminderController : Controller
{
public readonly UserAccountService<MemberAccount> userAccountService;
public SendUsernameReminderController(UserAccountService<MemberAccount> userAccountService)
{
this.userAccountService = userAccountService;
}
[HttpGet]
public ActionResult Index(string signin)
{
ViewBag.Signin = signin;
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(SendUsernameReminderInputModel model)
{
if (ModelState.IsValid)
{
try
{
this.userAccountService.SendUsernameReminder(model.Tenant, model.Email);
return RedirectToAction("Success", model);
}
catch (ValidationException ex)
{
if (ex.ValidationResult.ToString() == "The email address for this account is not yet verified.")
{
try
{
userAccountService.ResetPassword(model.Tenant, model.Email);
return RedirectToAction("Unverified");
}
catch (ValidationException resetex)
{
ModelState.AddModelError("", resetex.Message);
ViewBag.Message = "resetex.Message";
return View();
}
}
ModelState.AddModelError("", ex.Message);
return View();
}
}
return View();
}
public ActionResult Success(SendUsernameReminderInputModel model)
{
ViewBag.Subject = "Username Emailed";
ViewBag.Message = "Your username was emailed at " + model.Email + ". If you don't receive this email within 24 hours, please check your junk mail folder or visit our Help pages to contact Customer Service for further assistance.";
return View();
}
public ActionResult Unverified()
{
ViewBag.Subject = "Email has not been verified";
ViewBag.Message = "You will receive an email from us to confirm your email or cancel your registration. If you don't receive this email within 24 hours, please check your junk mail folder or visit our Help pages to contact Customer Service for further assistance.";
return View();
}
}
}
And this is my Model:
SendUsernameReminderInputModel.cs
using System.ComponentModel.DataAnnotations;
namespace Accounts.WebHost.Models
{
public class SendUsernameReminderInputModel
{
[Required]
[EmailAddress]
public string Email
{
get;
set;
}
[Required]
public string Tenant
{
get;
set;
}
}
}
my aim is that when a user clicks the form submit button only the validation message will display below the form. unfortunately, it outputs the whole Index.cshtml in the partial and the validation message at the bottom.
If this is a bad approach please give me directions.
Thank you in advance.
U need to pass Model like View(model) ; so that the View gets the model with those errors.
And u could also use #Html.ValidationSummary to display all the errors on a fly

Partial View and ajax

I want to update Partial View via ajax, but it does not work. Look at this model class:
public class LogOnModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
public bool IsLoggedIn { get; set; }
public string ReturnUrl { get; set; }
}
the following view:
#model ITW2012Mobile.ViewModels.LogOnModel
<div id='LogOn' style="background-color: White;">
#using (Ajax.BeginForm("LogOnAjax", "Home", new AjaxOptions { UpdateTargetId = "LogOn", OnSuccess = "logInComplete" }))
{
ITW2012Mobile.ViewModels.LogOnModel m = Model;
#Html.EditorFor(model => model.IsLoggedIn)
#Html.EditorFor(model => model.ReturnUrl)
<div>
#Html.ValidationSummary()
</div>
<div>
#Html.LabelFor(model => model.UserName)
#Html.EditorFor(model => model.UserName)
</div>
<div>
#Html.LabelFor(model => model.Password)
#Html.EditorFor(model => model.Password)
</div>
<div>
<input type="submit" value="Login" />
</div>
}
</div>
and the following controller class:
public ActionResult LogOnAjax(LogOnModel model)
{
if (!User.Identity.IsAuthenticated)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
model.IsLoggedIn = true;
model.ReturnUrl = Url.Action("Index", "Home");
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return PartialView("PartialViewAjaxLogOn", model);
}
else
{
return PartialView("PartialViewLogOut");
}
}
even when username/password are correct and IsLoggedIn = true and ReturnUrl!=empty view shows empty fields for these variables (but debugger shows values inside). Why and how to make it correctly?
Try clearing the values you are modifying in your action from modelstate or if you use them in html helpers the old values will be used:
ModelState.Remove("IsLoggedIn");
model.IsLoggedIn = true;
ModelState.Remove("ReturnUrl");
model.ReturnUrl = Url.Action("Index", "Home");
Also bear in mind that upon successful authentication and cookie emission you should not display a view (partial in your case). You should redirect so that the authentication cookie is sent by the client on the subsequent request. You should redirect to the return url. But since you are doing this using AJAX you should probably send some indication to the client that the authentication was successful so that you can redirect on the client.

How can I safely access key groups in the FormCollection?

I have a table where each tr is grouped by having their input elements name set to a value that is unique for each row.
For example,
<td>
<input data-field="opps" type="text" value="somevalue" name="#item.Code" />
</td>
<td>
<input data-field="asc" type="text" value="somevalue2" name="#item.Code" />
</td>
On POST
[HttpPost]
public ActionResult Update(FormCollection collection)
{
try
{
//doin work on collection...order assumed static
return RedirectToAction("Index");
}
catch
{
return View();
}
}
my System.Web.MVC.FormCollection is grouped in the same order I define the <td>. I don't like to assume order, but without access to my data-field, I'm not sure what else I can do (maybe I could append the data-field value as a prefix to the name and put it all together with a custom collection and Regex..but that seems nutty).
Is there a way to access the data-field? This way I'm not fragile to re-ordering or adding new columns in the View.
Let's say you have a class (model) defined like this:
public class MyModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
In you controller, you might have an action called Create, like so:
[HttpGet]
public ViewResult Create()
{
MyModel sampleModel = new MyModel();
return View(sampleModel);
}
[HttpPost]
public ActionResult Create(MyModel sampleModel)
{
if (ModelState.IsValid)
{
TempData["Error"] = "There were errors. Please correct the problem and submit again";
return View(sampleModel);
}
// At this point everything is fine and you can access data in your sampleModel
if (sampleModel.Age >= 16)
{
return RedirectToAction("LegalAccess");
}
else
{
TempData["Error"] = "You must be 16 or over to access this site";
return RedirectToAction("AgeRestriction");
}
}
When you create a strongly typed view that uses MyModel as model you might define it something like this:
#model MyModel
#{
Layout = "~/Shared/_Layout.cshtml";
}
#using (Html.BeginForm())
{
#Html.LabelFor(m => m.FirstName)
#Html.TextBoxFor(m => m.FirstName)
<br />
#Html.LabelFor(m => m.LastName)
#Html.TextBoxFor(m => m.LastName)
<br />
#Html.LabelFor(m => m.LastName)
#Html.TextBoxFor(m => m.LastName)
<input type="submit" value="Submit" />
}
When you submit this form, the model binder will in the background copy data from this form using Request.Form into an object of type MyModel which it creates in the background. This new object is passed to an action that handles HTTP POST method. With this you get strongly typed object and you don't have to worry about the order of items in FormCollection.
I hope I helped answer your question.
BTW. I wrote this without Visual Studio, so I hope there are not errors. :-)

MVC3 Entity Framework 4.1RC how does #Html.DropDownListFor actually work?

OK, I've been over Google and StackOverflow and ASP.net - am I really the only person who doesn't get this?
Rough problem = I have an Employee entity that references an Office entity. I was previously able to create new employees (beat me with a hammer for forgetting how that code worked) but now I just can't create an employee nor edit an existing one.
Now, here's what I learned;
1) Make sure you add the Offices list to the ViewBag at every step
2) That includes a failed POST/edit; call the function to re-populate the ViewBag with the Offices list
3) I think(!!) that you always want to set the Employee.Office, not the Employee.Office.OfficeID; the latter leads to "is part of the object's key information and cannot be modified" errors
So, what I have is;
A controller that has the following method;
private void AddOfficesToViewBag()
{
Dictionary<string, Office> list = new Dictionary<string, Office>();
foreach (Office office in company.GetAllOffices())
list.Add(office.ToString(), office);
SelectList items = new SelectList(list, "Value", "Key");
ViewBag.OfficeList = items;
}
Create pair looking like;
public ActionResult Create()
{
if (company.Offices.Count() < 1)
return RedirectToAction("Create", "Office", (object) "You need to create one or more offices first");
AddOfficesToViewBag();
return View(new Employee());
}
//
// POST: /Employee/Create
[HttpPost]
public ActionResult Create(Employee emp)
{
if (TryUpdateModel<Employee>(emp))
{
company.Employees.Add(emp);
company.SaveChanges();
return RedirectToAction("Index");
}
else
{
AddOfficesToViewBag();
return View(emp);
}
}
and an Edit pair that looks like;
public ActionResult Edit(int id)
{
Employee emp = company.Employees.Single(e => e.EmployeeID == id);
AddOfficesToViewBag();
return View(emp);
}
//
// POST: /Employee/Edit/5
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
Employee emp = company.Employees.Single(e => e.EmployeeID == id);
if (TryUpdateModel(emp))
{
company.SaveChanges();
return RedirectToAction("Index");
}
else
{
AddOfficesToViewBag();
return View(emp);
}
}
I'll pick the Edit View, which is pretty much the same as the Create View;
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
Employee
#Html.HiddenFor(model => model.EmployeeID)
<div class="editor-label">
#Html.LabelFor(model => model.Office)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Office, (SelectList) ViewBag.OfficeList)
#Html.ValidationMessageFor(model => model.Office)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Age)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Age)
#Html.ValidationMessageFor(model => model.Age)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
I would say that the Edit, in particular, looks almost there. It manages to bind to the Employee object passed in and sets the dropdown to the appropriate entry.
Viewing the original HTML source shows that the output value is the Office.ToString() value.
The odd thing to me is that some magic is happening that binds Employee->Office to the correct entry, which makes the Edit view work, but there is no corresponding conversion of the selected item (a string, aka object->ToString()) to the original list.
This seems so basic (MVC / EF4 / DropDownList) that I feel I'm missing something incredibly fundamental.
All thoughts appreciated.
Regards
Scott
Based on the following you can
http://forums.asp.net/t/1655622.aspx/1?MVC+3+Razor+DropDownListFor+and+Model+property+from+EFCodeFirst
Do the following:
[HttpPost]
public ActionResult Edit(Guid id, FormCollection collection)
{
CollectionViewModel cvc = new CollectionViewModel();
cvc.Collection = _db.Collections.Where(c => c.CollectionId == id).Include("CollectionType").First();
Guid collectionTypeId = Guid.Parse(collection["CollectionTypeId"].ToString());
cvc.Collection.CollectionType =_db.CollectionTypes.Where(ct =>ct.CollectionTypeId == collectionTypeId).First();
if (TryUpdateModel(cvc))
{
_db.SaveChanges();
return RedirectToAction("Index");
}
}
ViewModel
public class CollectionViewModel
{
public Collection Collection {get; set; }
public Guid CollectionTypeId { get; set; }
public SelectList CollectionTypes { get; set; }
}

Resources