Richard
Richard

Reputation: 5998

MVC3: Calling Controller From AJAX Button Click

In ASP .Net MVC3, how can I take a user entered input value and handle the click of a button to call a controller, returning a new model entity that updates other form input values with its property values without reloading the entire page?

Steps:

I'd have this done in seconds in Web Forms but being new to MVC3... well... fun :) I'm thinking it's probably something to do with Ajax.ActionLink? Examples I see deal with submitting and saving data.

Richard

Upvotes: 1

Views: 19835

Answers (3)

iCollect.it Ltd
iCollect.it Ltd

Reputation: 93571

If you want an actual Ajax button element, rather than a styling hack or JQuery, it is also possible but a little involved. It is a shame that MS has not yet chosen to add an ActionButton to both the Html and Ajax helpers as the differences are actually very minor when you remove the duplication of private support methods (you would only need the ActionButton and GenerateButton methods shown below).

The end result is you can have real buttons that trigger like ajax action links:

e.g.

@Ajax.ActionButton("Delete", "Delete", "document", 
     new { id = ViewBag.Id }, 
     new AjaxOptions() 
     { 
         Confirm="Do you really want to delete this file?", 
         HttpMethod = "Get", 
         UpdateTargetId = "documentlist" }, 
         new { id = "RefreshDocuments" 
     })

1. Create an AjaxHelper extension

The code below is based on a decompile of the AjaxExtensions class as many of the required helper methods are not exposed on HtmlHelper.

public static partial class AjaxExtensions
{
    public static MvcHtmlString ActionButton(this AjaxHelper ajaxHelper, string buttonText, string actionName, string controllerName, object routeValuesBlah, AjaxOptions ajaxOptions, object htmlAttributesBlah)
    {
        // Convert generic objects to specific collections
        RouteValueDictionary routeValues = new RouteValueDictionary(routeValuesBlah);
        RouteValueDictionary htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributesBlah);

        if (string.IsNullOrEmpty(buttonText))
            throw new ArgumentException("Button text must be provided");
        string targetUrl = UrlHelper.GenerateUrl((string)null, actionName, controllerName, routeValues, ajaxHelper.RouteCollection, ajaxHelper.ViewContext.RequestContext, true);
        return MvcHtmlString.Create(GenerateButton(ajaxHelper, buttonText, targetUrl, AjaxExtensions.GetAjaxOptions(ajaxOptions), htmlAttributes));
    }

    public static string GenerateButton(AjaxHelper ajaxHelper, string linkText, string targetUrl, AjaxOptions ajaxOptions, IDictionary<string, object> htmlAttributes)
    {
        TagBuilder tagBuilder = new TagBuilder("input");
        tagBuilder.MergeAttribute("value", linkText);
        tagBuilder.MergeAttributes<string, object>(htmlAttributes);
        tagBuilder.MergeAttribute("href", targetUrl);
        tagBuilder.MergeAttribute("type", "button");
        if (ajaxHelper.ViewContext.UnobtrusiveJavaScriptEnabled)
            tagBuilder.MergeAttributes<string, object>(ajaxOptions.ToUnobtrusiveHtmlAttributes());
        else
            tagBuilder.MergeAttribute("onclick", AjaxExtensions.GenerateAjaxScript(ajaxOptions, "Sys.Mvc.AsyncHyperlink.handleClick(this, new Sys.UI.DomEvent(event), {0});"));
        return tagBuilder.ToString(TagRenderMode.Normal);
    }

    private static string GenerateAjaxScript(AjaxOptions ajaxOptions, string scriptFormat)
    {
        string str = ajaxOptions.ToJavascriptString();
        return string.Format((IFormatProvider)CultureInfo.InvariantCulture, scriptFormat, new object[1] { str });
    }

    private static AjaxOptions GetAjaxOptions(AjaxOptions ajaxOptions)
    {
        if (ajaxOptions == null)
            return new AjaxOptions();
        else
            return ajaxOptions;
    }

    public static string ToJavascriptString(this AjaxOptions ajaxOptions)
    {
        StringBuilder stringBuilder = new StringBuilder("{");
        stringBuilder.Append(string.Format((IFormatProvider)CultureInfo.InvariantCulture, " insertionMode: {0},", new object[1]
        {
             ajaxOptions.InsertionModeString()
        }));
        stringBuilder.Append(ajaxOptions.PropertyStringIfSpecified("confirm", ajaxOptions.Confirm));
        stringBuilder.Append(ajaxOptions.PropertyStringIfSpecified("httpMethod", ajaxOptions.HttpMethod));
        stringBuilder.Append(ajaxOptions.PropertyStringIfSpecified("loadingElementId", ajaxOptions.LoadingElementId));
        stringBuilder.Append(ajaxOptions.PropertyStringIfSpecified("updateTargetId", ajaxOptions.UpdateTargetId));
        stringBuilder.Append(ajaxOptions.PropertyStringIfSpecified("url", ajaxOptions.Url));
        stringBuilder.Append(ajaxOptions.EventStringIfSpecified("onBegin", ajaxOptions.OnBegin));
        stringBuilder.Append(ajaxOptions.EventStringIfSpecified("onComplete", ajaxOptions.OnComplete));
        stringBuilder.Append(ajaxOptions.EventStringIfSpecified("onFailure", ajaxOptions.OnFailure));
        stringBuilder.Append(ajaxOptions.EventStringIfSpecified("onSuccess", ajaxOptions.OnSuccess));
        --stringBuilder.Length;
        stringBuilder.Append(" }");
        return ((object)stringBuilder).ToString();
    }

    public static string InsertionModeString(this AjaxOptions ajaxOptions)
    {
        switch (ajaxOptions.InsertionMode)
        {
            case InsertionMode.Replace:
                return "Sys.Mvc.InsertionMode.replace";
            case InsertionMode.InsertBefore:
                return "Sys.Mvc.InsertionMode.insertBefore";
            case InsertionMode.InsertAfter:
                return "Sys.Mvc.InsertionMode.insertAfter";
            default:
                return ((int)ajaxOptions.InsertionMode).ToString((IFormatProvider)CultureInfo.InvariantCulture);
        }
    }

    public static string EventStringIfSpecified(this AjaxOptions ajaxOptions, string propertyName, string handler)
    {
        if (string.IsNullOrEmpty(handler))
            return string.Empty;
        return string.Format((IFormatProvider)CultureInfo.InvariantCulture, " {0}: Function.createDelegate(this, {1}),",
            new object[2]
              {
                propertyName,
                handler
              });
    }

    public static string PropertyStringIfSpecified(this AjaxOptions ajaxOptions, string propertyName, string propertyValue)
    {
        if (string.IsNullOrEmpty(propertyValue))
            return string.Empty;
        string str = propertyValue.Replace("'", "\\'");
        return string.Format((IFormatProvider)CultureInfo.InvariantCulture, " {0}: '{1}',",
            new object[2]
              {
                propertyName,
                str
              });
    }
}

2. Modify jquery.unobtrusive-ajax.js

Only a small change is required to the JQuery of jquery.unobtrusive-ajax.js to accept the new button object, as it is very close to begin with. First the selector needs to accept ajax buttons as well as links and then the href needs to come from an attribute so than a non-link can provide it (not strictly browser compliant but works for now).

$(document).on("click", "input[data-ajax=true],a[data-ajax=true]", function (evt) {
        evt.preventDefault();
        asyncRequest(this, {
            url: $(this).attr("href"),
            type: "GET",
            data: []
        });
    });

*Note: this is using the latest version of everything as at the date of answering (MVC 5)

Upvotes: 0

CDubbz31
CDubbz31

Reputation: 141

You could simply use the jquery ajax method to call the action, and have that action return whatever values u need. You can then use the values returned to update the other fields via javascript.

 $('#button').click(function(){
    $.ajax({
        url: 'controller/action.aspx',
        success: function(data) {
           $('.result').html(data);
        }
     });
 });

This is very general solution, since you provided no code it's tough to be specific. U may want to set the datatype, depending on what you are sending back. A popular choice is json.

See here: http://api.jquery.com/jQuery.ajax/

Upvotes: 1

Rajeesh
Rajeesh

Reputation: 4485

This is how you could do a Ajax post using MVC3. In your case, you update the form fields on the OnSuccess callback function based the response from server.

Step 1

Make sure you have referenced the jQuery Ajax library

<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>

Step 2

In the view use the Ajax.BeginForm helper method to generate Ajax aware form. Also provide a OnSuccess javascript callback function

@using(Ajax.BeginForm("Search",new AjaxOptions
{
  HttpMethod = "Post",    
  OnSuccess = "Loaded"    
}))
{
   <input type="text" name="q" />
   <button type="submit" value="Search">Search</button>
}

<script type="text/javascript">
   function Loaded(result) {
      alert('result ' + result.Name);
   }
</script>

Step 3

In the action method, do your business processing and return a JSON result. In this example, I simply returned an Model which append the query to the name.

[HttpPost]
public ActionResult Search(string q)
{
    return Json(new SampleViewModel { Age = 10 , Name = "John " + q });
}

Upvotes: 3

Related Questions