ASP.NET MVC: Getting values from TextBoxFor, TextBox, DropDownList and TextArea

doosh picture doosh · Jun 20, 2013 · Viewed 7.4k times · Source

In ASP.Net MVC I am opening one view from another view. The first view sends two values to the second view. In the second view the user can send an email.

I am having two problems.

The first problem is that the two values that I send from the first view aren't being shown in my second view.

The second problem is that I can't get the email form to trigger my email function in the controller.

Here's a more detailed explanation.

My first view named ViewOne is using the controller ControllerOne. In ViewOne I have the following code to call the second view, ViewTwo:

@Html.ActionLink("Go to second view", "ViewTwo", "Home", new { firstval = firstval, secondval = secondval }, null)

When the ActionLink is clicked, the following function in the controller HomeController is called:

public ActionResult ViewTwo(string firstval, string secondval)
{
    MyModel model = new MyModel();
    model.firstval = firstval;
    model.secondval = secondval;
    var list = new SelectList(new[]
                                  {
                                      new {ID="1",Name="One"},
                                      new{ID="2",Name="Two"},
                                      new{ID="3",Name="Three"},
                                  },
                    "ID", "Name", 1);
    model.myList = list;
    return View(model);
}

So in the controller HomeController I attempt to populate the model myModel with the values I get from the first view, ViewOne.

The model MyModel looks like this:

public class MyModel
{
    public string firstval { get; set; }
    public string secondval { get; set; }
    public IEnumerable<SelectListItem> myList { get; set; }

    [Required]
    [DisplayName("My name")]
    public string reporter { get; set; }

    [Required]
    [DisplayName("Description")]
    public string description { get; set; }

    [DisplayName("Dropdown")]
    public string myDropDownListValue { get; set; }
}

The view ViewTwo looks like this:

@model myapp.Models.MyModel
@{ ViewBag.Title = "Send e-mail"; }

<hgroup class="title">
    <h1>@ViewBag.Title</h1>
    <h2>@ViewBag.Message</h2>
</hgroup>

@using (Html.BeginForm("sendEmail"))
{ 
    @Html.AntiForgeryToken()
    @Html.ValidationSummary()

    <fieldset>
    <legend>Send e-mail</legend>

    <p>First value:</p>
    <p>@Html.LabelFor(m => m.firstval)</p>
    <p>Second value:</p>
    <p>@Html.LabelFor(m => m.secondval)</p>
    <p>Reporter</p>
    <p>@Html.TextBoxFor(m => m.reporter)</p>
    <p>Dropdownlist</p>
    <p>@Html.DropDownListFor(m => m.myDropDownListValue, Model.myList as SelectList)</p>
    <p>Description:</p>
    <p>@Html.TextAreaFor(m => m.description, new { @cols = 150, @rows = 5})</p>
 <input type="submit" value="Send e-mail"/>

</fieldset>

}

In the controller HomeController, which is the same controller that has the ViewTwo() function that gets triggered right before the above form gets drawn, I have the following function:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult sendEmail(ContactModel model) // (string keyword, string partofspeech, string reporter, string category, string description, string acceptance)
{
    // code to send email
}

So I want this function, sendEmail, to get triggered whenever I submit the form. But that doesn't happen. What happens when I click the submit button (labeled "Send e-mail") is that the view ViewTwo gets reloaded and the ActionResult ViewTwo() in the controller HomeController gets triggered. This is my second (and biggest) problem.

Also, my first problem is that

<p>@Html.LabelFor(m => m.firstval)</p>

Doesn't show the value that gets sent from the first view. It shows the string "firstval". Before the form is drawn I can see in the function ViewTwo() that the value gets correctly sent from the first view.

Any ideas?

EDIT:

Second problem solved. See my reply below.

Answer

gdp picture gdp · Jun 20, 2013

You have a few options, normally with a postback you would submit the form with an <input type="submit" value="sendEmail" />, the values in the form would be represented in a ViewModel like:

public class EmailFormViewModel() 
{
    public string value1 {get; set;}
    public string reporter {get; set;}
    //More properties on the form
}

Your endpoint would look like this:

[HttpPost]
public ActionResult SendEmail(EmailFormViewModel model) 
{ 
    //Send the email
}

If you still want to use a hyperlink to submit the form, which natively performs a GET request, you will can catch the click with javascript, and manually send the form via Ajax.

Something like:

$('#sendEmail').click(function(e) {
    e.preventDefault();         
    $.ajax({
        type: 'POST',
        data: $('#formId').serialize(),
        url: '/controllerName/sendemail'
    }).done(function(response) { 
        //Do something on success response
    });
});

Update:

You should also decorate your post action sendEmail with [ValidateAntiForgeryToken] and add a @Html.AntiForgeryToken() within the form. This will help protect against cross site forgery requests.

You can build your form, endpoint and model like this:

@using (Html.BeginForm("sendEmail"))
{ 
    @Html.AntiForgeryToken()
    @Html.ValidationSummary()
    <p>@Html.LabelFor(m => m.value1)</p>
    <p>@Html.EditorFor(m => m.value1)</p>
    <p>@Html.LabelFor(m => m.reporter)</p>
    <p>@Html.EditorFor(m => m.reporter)</p>
    <p>@Html.LabelFor(m => m.myDropDownListValue)</p>
    <p>@Html.DropDownListFor(m => m.myDropDownListValue, Model.myList as SelectList)</p>
    <p>@Html.LabelFor(m => m.myTextAreaValue)</p>
    <p>@Html.TextAreaFor(m => m.myTextAreaValue, new { @cols = 150, @rows = 5})</p>
    <input type="submit" value="Send Email"/>
}

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SendEmail(myModel model) 
{ 
    //Send the email
}

public class myModel
{
    public IEnumerable<SelectListItem> myList { get; set; }
    [DisplayName('Value 1')]
    public string value1 { get; set; }
    [DisplayName('Reporter')]
    public string reporter { get; set; }
    [DisplayName('Text Area')]
    public string myTextAreaValue { get; set; }
    [DisplayName('Dropdown')]
    public string myDropDownListValue { get; set; }
}

As long as you are already on the same controller, it will postback to /controllername/sendemail with the form data inside the post. You should also look up attributes on your models, you can enforce descriptions and validations for example. Check here for more details, its MVC 2 but still relevant.