0

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.

doosh
  • 875
  • 3
  • 9
  • 13
  • The `ActionLink` renders a hyperlink, which won't post the current form input values. Have a look here on how to do this http://stackoverflow.com/questions/3449807/mvc-make-action-link-perform-a-submit – StuartLC Jun 20 '13 at 13:04
  • I'd be content to just send the values from the TextBoxFor, etc, back to my control as normal variables. But you're saying that can't be done? :( – doosh Jun 20 '13 at 13:15
  • Ok, so you want me to submit the form instead. Then I can just use the variant. But how do I get the controller to understand what function it should use..? – doosh Jun 20 '13 at 13:17
  • @doosh If you submit using 'HtmlBeginForm()' it will POST back to the current endpoint with the form values, you should really catch them in a view model. – gdp Jun 20 '13 at 13:26
  • 1
    If you don't want to do a full postback, you'll need to use Ajax. Either use [Ajax.BeginForm](http://stackoverflow.com/questions/5410055/using-ajax-beginform-with-asp-net-mvc-3-razor) or just use jQuery `$.ajax` as per gdp. – StuartLC Jun 20 '13 at 13:38
  • I'd be happy to do a full postback. I was using an ActionLink in testing because that seemed easiest to me, but if I can figure out how to do this with an instead then I'd be happy. But I can't get that working. See my original post under ADDENDUM where I have explained where I'm failing. – doosh Jun 20 '13 at 13:41
  • As StuartLC suggests I too would use $.ajax along form.serialize – Kaarthik Jun 20 '13 at 13:47

3 Answers3

2

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.

gdp
  • 8,032
  • 10
  • 42
  • 63
  • The approach seems like the best one. However, I can't get it to work. See my original post under "ADDENDUM" (it was too long to write in a comment). – doosh Jun 20 '13 at 13:40
  • @doosh What exactly cant you get to work? The model isnt being populated? Have you tried stepping through it? – gdp Jun 20 '13 at 13:53
  • @doosh Can you also confirm the current page you are on, as you are submitting back to the current page using Html.BeginForm(), you may need to send it to the sendEmail action like this: Html.BeginForm("sendemail"), also make sure the input submit button is inside the form – gdp Jun 20 '13 at 13:57
  • What I can't get working is that I can't get my app to jump to the sendEmail function in the controller. I did the Html.BeginForm("sendEmail") and the has been inside the using (Html.BeginForm("sendEmail")) all along. When I click the submit button the page reloads but it doesn't jump into my sendEmail() function. – doosh Jun 20 '13 at 14:02
  • @doosh Can you update your razor view with what you actually have now. And confirm both actions are on the same controller. – gdp Jun 20 '13 at 14:04
  • Done. I updated the entire post to show what the code looks like right now after having followed your suggestions. – doosh Jun 20 '13 at 14:14
  • I can't get your code to work. I'll update my question once more to describe the problem further. – doosh Jun 23 '13 at 16:39
  • @doosh Seems like you have lots of nested smaller issues, i recommend you going over the basics http://www.asp.net/mvc/overview/getting-started – gdp Jun 24 '13 at 09:43
0

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

dave clements
  • 1,505
  • 2
  • 13
  • 28
0

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. :)

doosh
  • 875
  • 3
  • 9
  • 13