0

I have the following simple view that is used to send invitations out to register for our web-site. This is in an admin-only view and once the email address is filled out the admin can click and send the invite. The code is

 @using (Html.BeginForm("Upload", "Tools", FormMethod.Post,
        new { @class = "form-horizontal", role = "form" }))
    {
        @Html.AntiForgeryToken()
        <h2>Invite Users</h2>
        <div class="form-group">
            @Html.LabelFor(m => m.EmailAddress, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.TextBoxFor(m => m.EmailAddress, 
                    new { @class = "form-control", id = "email" })
                @Html.ValidationMessageFor(m => m.EmailAddress)
            </div>
        </div>
        <div class="form-group">
            @Html.Label("Access To", new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.DropDownList("Product", new SelectList(
                    new List<Object> {
                        new { Text = "D", Value = "D" },
                        new { Text = "U", Value = "U" },
                        new { Text = "All", Value = "All" }},
                        "Value",
                        "Text"),
                        new { @class = "form-control", id = "product" })
            </div>
        </div>
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <span class="label label-danger">@ViewBag.FailedMessage</span>
                <span class="label label-success">@ViewBag.SuccessMessage</span>
            </div>
        </div>
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="button" value="Invite User" class="btn btn-primary" />
            </div>
        </div>
    }

To post back to the controller on the button click with the selected combo-box selection, I use the following Java Script

<script>
    $(function () {
        $("input[type=button]").click(function () {
            var data_email = $('#email').val();
            var data_product = $('#product option:selected').text();
            $.ajax({
                url: 'Tools/SendInvite',
                type: 'POST',
                data: { email: data_email, product: data_product },
                success: function (result) {
                }
            });
        });
    });
 </script>

The controller method for this is

[HttpPost]
[AllowAnonymous]
public async Task<ActionResult> SendInvite(
    string email, string product)
{
    ApplicationUser user = null;
    if (ModelState.IsValid)
    {
        user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
        if (user.IsAdmin != null && (bool)user.IsAdmin)
        {
            string key = String.Empty;
            string subMsg = String.Empty;
            var accessDB = new AccessHashesDbContext();
            switch (product) { /* do stuff */ }

            // Send email.
            try
            {
                await Helpers.SendEmailAsync(new List<string>() { email }, null, "my message string");
                ViewBag.FailedMessage = String.Empty;
                ViewBag.SuccessMessage = String.Format(
                    "Invitation successfully sent to \"{0}\"",
                    email);
            }
            catch (Exception)
            {
                ViewBag.SuccessMessage = String.Empty;
                ViewBag.FailedMessage = String.Format(
                    "Invitation to \"{0}\" failed",
                    email);
            }
        }
    }
    return View();
}

This code works and sends my email template off to the required recipient. However, I want to notify that the email has been sent or has failed to send via the ViewBag. This code seems to work, but in the view the ViewBag is empty and no messages are displayed, what am I doing wrong here?

Thanks for your time.


Edit. by using @yhax's answer below, I have come to the following attempted solution; in my controller I have

[HttpPost]
[AllowAnonymous]
public async Task<ActionResult> SendInvite(string email, string product)
{
    ApplicationUser user = null;
    string result = "{\"invite_result\":\"{0}\",\"invite_success\":\"{1}\"}";
    if (ModelState.IsValid)
    {
        user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
        if (user.IsAdmin != null && (bool)user.IsAdmin)
        {
            string key = String.Empty;
            string subMsg = String.Empty;
            var accessDB = new AccessHashesDbContext();
            switch (product) { /* do stuff */ }

            // Send email.
            try
            {
                await Helpers.SendEmailAsync(new List<string>() { email }, null, "my message string");
                result = String.Format(result, 
                    String.Format("Invitation successfully sent to \"{0}\"", email), 
                    "true");
                return Content(result);
            }
            catch (Exception)
            {
                result = String.Format(result, 
                    String.Format("Invitation to \"{0}\" failed", email), 
                    "false");
                return Content(result);
            }
        }
    }
    result = String.Format(result, "Invite Failed", "false");
    return Content(result);
}

In my JavaScript within my view I now have:

<script>
    $(function () {
        $("input[type=button]").click(function () {
            var data_email = $('#email').val();
            var data_product = $('#product option:selected').text();
            $.ajax({
                url: 'Tools/SendInvite',
                type: 'POST',
                data: { email: data_email, product: data_product },
                success: function (result) {
                    var obj = JSON.parse(jsontext);
                    if (obj.invite_success == "true") {
                        $('#fail_message').val("");
                        $('#success_message').val(obj.invite_result);
                    }
                    else {
                        $('#fail_message').val(obj.invite_result);
                        $('#success_message').val("");
                    }
                },
                error: function () {
                    alert("Invite Failure");
                }
            });
        });
    });
</script>

This is always hitting the error condition and shows an alert. When I comment this error: function() out, then my post does not fire to the controller! What am I doing wrong in this case?

MoonKnight
  • 23,214
  • 40
  • 145
  • 277
  • 2
    You're returning the `View` but you do nothing with the result in JS – Vsevolod Goloviznin Dec 09 '14 at 13:33
  • 1
    Your checking Viewback when the page initlay loads. Viewbag wont be checked again until the page refreshes. I would return an obj from your SendInvite email, then in your ajax success, check that obj for your conditions. – Botonomous Dec 09 '14 at 13:39
  • Rather than returning `View()` and using `ViewBag`, return `Json(new { Message = "Some messge"});` – Ben Robinson Dec 09 '14 at 13:51
  • Oh, so the `ViewBag` is only check when the initial load happens! @BenRobinson how would I access the Json object from the view? – MoonKnight Dec 09 '14 at 14:00
  • It would be a property on the `result` parameter passed to your success callback in you JavaScript. Probably `result.data.Message` or similar, you should debug to confirm. – Ben Robinson Dec 09 '14 at 14:02

1 Answers1

1

I think you want something like this:

Controller:

[HttpPost]
[AllowAnonymous]
public async Task<ActionResult> SendInvite(
    string email, string product)
{
    ApplicationUser user = null;
    if (ModelState.IsValid)
    {
        user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
        if (user.IsAdmin != null && (bool)user.IsAdmin)
        {
            string key = String.Empty;
            string subMsg = String.Empty;
            var accessDB = new AccessHashesDbContext();
            switch (product) { /* do stuff */ }

            // Send email.
            try
            {
                await Helpers.SendEmailAsync(new List<string>() { email }, null, "my message string");
                return Content(String.Format("Invitation successfully sent to \"{0}\"", email));
            }
            catch (Exception)
            {
                return Content(String.Format(
                    "Invitation to \"{0}\" failed",
                    email));
            }
        }
    }
// you could redirect to another action, or return some other message
    return Content("not valid etc etc");
}

and then in here you could do something with the returned message:

<script>
    $(function () {
        $("input[type=button]").click(function () {
            var data_email = $('#email').val();
            var data_product = $('#product option:selected').text();
            $.ajax({
                url: 'Tools/SendInvite',
                type: 'POST',
                data: { email: data_email, product: data_product },
                success: function (result) {
// do something here... show a box with the message, whatever you like
console.log(result);
                }
            });
        });
    });

  • Thank very much for this. I am a total noob to this stuff. I know how to get data from HTML elements, but how would I set properties (like my `label`) from the JavaScript? Any additional information or link on this would be massively appreciated... – MoonKnight Dec 09 '14 at 14:08
  • All you would need to do is make sure your HTML elements have id's set on them. For example –  Dec 09 '14 at 14:10
  • One final thing, I would like to have two label colours depending on the result. With the code above you only pass back one string object. How would I pass back two string objects, one for each label? – MoonKnight Dec 09 '14 at 14:14
  • Take a look at this, it has everything you need - http://stackoverflow.com/questions/227624/asp-net-mvc-controller-actions-that-return-json-or-partial-html –  Dec 09 '14 at 14:58