Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

If you really want to be able to GET the values instead of POSTing them, change the form's action to GET and change the target to be sendEmail

Remove the ActionLink and replace it with a simple submit button

I know you said you wanted to keep the ActionLink, but this will achieve the same thing

I managed to solve my first problem. Once I specified which controller the function sendEmail is in, that code finally got triggered. Like so:

@using (Html.BeginForm("sendEmail", "Home"))

Now if I can only figure out why

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

isn't working then I'm home safe.

It actually prints out the string "firstval" instead of taking the value of the string variable firstval that I set in the model. (See my first post for more detailed explanation).

EDIT:

I fixed that last problem. The very werid thing is that the above code with LabelFor doesn't work. But if I do this instead:

<p>@Model.firstval</p>

then I get the value. But it doesn't get sent back to the controller when the form is submitted. But that I solved with:

@Html.HiddenFor(m => m.firstval)

So HiddenFor works for me, LabelFor doesn't.

Case closed. I'm throwing the "solved checkmark" to the guy who gave me all that help here above. He did awesome. But the real solution is in this post. :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top