Augusto Jimenez
Augusto Jimenez

Reputation: 71

How to sent ObjectViewModel from View to Controller in ASP.NET MVC C#?

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

Answers (3)

Augusto Jimenez
Augusto Jimenez

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

iCollect.it Ltd
iCollect.it Ltd

Reputation: 93601

There are 2 problems

1) You are missing a null on the 4th parameter of ActionLink

@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.

2) Your item needs to be simple data values that can be passed via the querystring

As 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

kondas
kondas

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

Related Questions