Reputation: 71
I am working in an ASP.NET Web Application, using MVC template.
I am trying send or pass an ObjectViewModel from a View.cshtml to a Controller, but in the Controller the ObjectViewModel arrived as null.
The idea is the follow: I show the view IndexUsersInActiveDirectory.cshtml with a collection of object of type RegisterViewModel. Into this view there is an @Html.ActionLink for send a element of the collection to the Register controller:
IndexUsersInActiveDirectory.csthml
@model IEnumerable<SecureEscuelaWithIdentity.Models.RegisterViewModel>
@{
ViewBag.Title = "IndexUsersInActiveDirectory";
}
<h2>IndexUsersInActiveDirectory</h2>
<p>
@Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
@Html.DisplayNameFor(model => model.UserName)
</th>
<th>
@Html.DisplayNameFor(model => model.Email)
</th>
<th></th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.UserName)
</td>
<td>
@Html.DisplayFor(modelItem => item.Email)
</td>
<td>
@Html.ActionLink("Select", "Register", new { item })
</td>
</tr>
}
</table>
The controller Register is the next:
//
// GET: /Account/Register
[Authorize(Roles = "Admin")]
public ActionResult Register(RegisterViewModel model)
{
return View(model);
}
The class RegisterViewModel is:
public class RegisterViewModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[Display(Name = "Email")]
public string Email { get; set; }
}
The view Register.cshtml
@model SecureEscuelaWithIdentity.Models.RegisterViewModel
@{
ViewBag.Title = "Register";
}
<h2>@ViewBag.Title.</h2>
@using (Html.BeginForm("RegisterInApp", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
@Html.AntiForgeryToken()
<h4>Create a new account.</h4>
<hr />
@Html.ValidationSummary()
<li>@Html.ActionLink("ActiveDirectory", "IndexUsersInActiveDirectory", "Account")</li>
<div class="form-group">
@Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.TextBoxFor(m => m.UserName, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Register" />
</div>
</div>
}
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
How to send the entire collection element (as model) to the controller?
Upvotes: 1
Views: 727
Reputation: 71
The solution proposed by TrueBlueAussie is suitable for scenarios where there is no problem to show querystring in the address bar of the browser.
If so, the @Html.ActionLink could be like this:
@Html.ActionLink("Select", "Register", new {item}, null)
since the controller will take 'item' as a RegisterViewModel object (although in reality be a querystring). Note: not forgetting the 4th parameter that contributed TrueBlueAussie "null".
The Register controller looks like this:
[Authorize (Roles = "Admin")]
public ActionResult Register (RegisterViewModel model)
{
return View (model);
}
It works!
As mention above, this displays the querystring in the Register view, which is not correct in my implementation. I do not want to show this querystring in the address bar.
To achieve this, I added an intermediate controller responsible of receiving the 'item' (from IndexUsersInActiveDirectory.csthml) as a RegisterViewModel object (actually it is a querystring) and add this Object to TempData dictionary with a key called "model".
Here is the controller RegisterTemp:
[Authorize (Roles = "Admin")]
public ActionResult RegisterTemp (RegisterViewModel model)
{
TempData ["model"] = model;
return RedirectToAction ("Register");
}
In the view 'IndexUsersInActiveDirectory.csthml' the @ActionLink point to controller RegisterTemp rather than the controller Register, being as follows:
@ Html.ActionLink ("Select", "RegisterTemp", new {item}, null)
Importantly RegisterTemp returns a "RedirectToAction" to Register view.
And finally, when the RegisterTemp controller returns RedirectToAction to Register controller, it takes the element with the "model" key from the TempData dictionary in order to return it to the view. Thus, the Register controller is:
//
// GET: / Account / Register
[Authorize (Roles = "Admin")]
public ActionResult Register ()
{
return View (TempData ["model"]);
}
I mentioned that, the solution proposed by @TrueBlueAussie is fully functional for the scenario that is not important to show the querystring.
The solution that I have just detailed now, is for avoid showing that querystring.
If there is a more correct solution to solve my scenario, is welcome.
Upvotes: 1
Reputation: 93601
@Html.ActionLink("Select", "Register", new { item }, null)
Without that 4th parameter it assumes the 3rd parameter is the HTML attributes and not the querystring parameters.
I find this little 4th parameter "quirk" to be one of the most annoying overrides in MVC.
item
needs to be simple data values that can be passed via the querystringAs your item is not a simple value type, you need to pass back its unique values. e.g.:
@Html.ActionLink("Select", "Register", new { name = item.Name, email = item.Email }, null)
Action links do not do a postback, they just create anchor links in the page, so any values have to be passed in the query string.
Change the receiving action to match:
[Authorize(Roles = "Admin")]
public ActionResult Register(string name, string email)
{
RegisterViewModel model = new RegisterViewModel()
{
Name = name,
Email = email
};
return View(model);
}
But I leave those details up to you :)
Upvotes: 2
Reputation: 193
You must iterate the elements with a "for" loop, as in for (int i; i <= elements.Length -1; i++) elements[i].etc;
So the collection don't get lost on post.
Read this for more detail: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/
Upvotes: 0