MindGame
MindGame

Reputation: 1251

Pass callback function to partial view mvc

I have a partial view that is used more than once on my site. Depending on where you are after you "Save" the record it should do something different.

So I was thinking of passing a callback function to the controller as a string and then pass it back to the partial view.

Is there a better way of doing this?

Upvotes: 1

Views: 4648

Answers (3)

kilkfoe
kilkfoe

Reputation: 453

Old question, but I was looking for a way to do this and came up with something.

I wanted to be able to add something very simple to my main view and child partial view to allow the javascript method after an ajax post in the partial view to be set by the main view.

I call the partial view from the main view with one of the following lines depending on whether you need to pass model data or not:

@{Html.RenderAction("_Create", new { Callback = "myCallback" });} @{Html.RenderAction("_Create", new { Model = Model, Callback = "myCallback" });}

"myCallback" is the name of a javascript function, for example:

function myCallback(result) {
    alert(result.Id);
}

From here you could ask for this value in the controller action, and then from the action add the value to the view bag, but if you are going to use this on multiple partial views it would be cleaner to have a filter attribute do this for you.

Create the following custom action filter:

public class MyFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var callBackValue = filterContext.RouteData.Values["Callback"];

        if (callBackValue != null)
        {
            filterContext.Controller.ViewBag.Callback = callBackValue.ToString();
            filterContext.RouteData.DataTokens.Remove("Callback");
            filterContext.RouteData.Values.Remove("Callback");
        }

        base.OnActionExecuting(filterContext);
    }
}

This pulls the "Callback" value out of the RouteData object and sets it in the ViewBag.Callback value. Make sure to remove the value from RouteData or else your controller action won't be found (since it will be looking for an action with a parameter called "Callback"

Then from the view you can access this value with @ViewBag.Callback. For example, you might have an ajax call:

function createProduct(callback) {
    $.ajax({
        type: 'POST',
        url: '/Product/_Create',
        data: {
            Name: $('#Name').val()
        },
        success: function (result) {
            var callBack = @(ViewBag.Callback ?? "null");

            if (callback)
                callback(result);
            else {
                if (result) {
                    var url = '/Product/Details';
                    url += "/" + result.Id;
                    window.location.href = url;
                }
            }
        },
        error: function (ajaxContext) { alert('Bad error'); },
        timeout: 10000
    });
}

The default behavior of the partial view is to load the details page, but if a separate callback function is supplied by the main view (or however it gets in to ViewBag.Callback) the callback function is overwritten. Here my main view overwrites with a function that simply shows an alert box with the new product's id.

Upvotes: 1

Travis J
Travis J

Reputation: 82267

You can pass a magic string which would invoke a method through reflection if you want.

Is there a better way?

I would suggest that you determine where this post originated from, and then handle the case based on that.

HttpContext.Current.Request.Url

Will contain enough information for you to switch on.

Upvotes: 2

Nick
Nick

Reputation: 6588

In MVC all addresses/resources/urls are relative to the path in which they are being rendered by default. So if you have a form inside the partial, it can submit to two different controller action methods depending under which url it has been rendered. E.g. to declare a form that will be submitted to the "update" action method you would use this:

@using (Html.BeginForm("Save")) {

Now if this partial is rendered under /Users/Home, the html output will becone:

<form action="/Users/Save" method="POST">

This then changes when the partial is rendered under /Products/Home:

<form action="/Products/Save" method="POST">

You can then implement two "Save" action methods on both the Users and Products Controller. Both of which can perform the standard "Save" function but then also perform whatever unique behaviour is required in each case.

Upvotes: 2

Related Questions