Raziel
Raziel

Reputation: 57

MVC .NET 4.5 How to execute a method of the model on button click (without loading a new page)

I've go a quite simple problem, I'd like to generate a zip file with a Temp ID and update a link of the page when you click on a button.

Thus, i've got this button (actually, a link) in my view :

<a href='#' id='downloadGraphA3'>...</a>

and this invisible link that should be updated when you click on the button :

<a id="dlTemp" style="display: none"></a>

So I'd like to call a method "ExportGraphTest" (which generate the zip file and returns its path) with javascript, something like that:

downloadGraphA3.onclick = function () {
    var path = @Model.ExportGraphTest("GraphA3");
    dlTemp.href = path;
    dlTemp.click();
}

Is it possible? I tried to make the ExportGraphTest method return javascript code to eval, but it doesn't work and the method is called on page load anyway.

I cannot call this method on page load because it takes time to generate the zip files while this export function won't be used often (and the page itself is already long to load without that).

I know the simplest way would be to open a new page (this is how my code works right now) but my client doesn't want it... and i don't want to reload the page because it takes much time.

Maybe i should use Ajax (I don't know much about it)? Thanks in advance!

-------------UPDATE-------------

Thanks to g.pickardou, I found a solution using XHR :

/*--------------------------graph Export--------------------------*/
function ExportGraph(graphId) {
    var xhr = getXMLHttpRequest();
    xhr.onreadystatechange = function () {
        if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 0)) {
            eval(xhr.responseText);
        }
    };
    xhr.open('POST', window.location.protocol + '//' + window.location.host + '/' + '@System.Threading.Thread.CurrentThread.CurrentUICulture.Name/Home/ExportGraph', true);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    xhr.send('graphId=' + graphId);
}
var dlGA3 = document.getElementById('downloadGraphA3');
if (dlGA3 != null) {
    dlGA3.onclick = function () {
        ExportGraph('GraphA3');
    }
}

Then my HomeController contains this method:

        public MvcHtmlString ExportGraph()
    {
        string graphId = this.Request.Params["graphId"];
        (...)
        return new BSH2O.Models.HomeModel(zoneLabel, plantLabel, zoneId, plantId, modelId, Session).ExportGraph(graphId);
    }

And the ExportGraph method of the Model generates the zip file and returns this kind of string:

dlTemp.href = '/BSH_Data/CsvZipTempDir/2524b0eb-e0a1-454f-992a-1fde1d903e67/2524b0eb-e0a1-454f-992a-1fde1d903e67.zip';dlTemp.click();

Upvotes: 1

Views: 667

Answers (2)

Darin Dimitrov
Darin Dimitrov

Reputation: 1038800

You don't need a hidden anchor for that. It appears that you are generating the first anchor link on the server using some custom @Model.ExportGraphTest("GraphA3") function. So all you need is:

<a href="@Model.ExportGraphTest("GraphA3")">...</a>

and now you could get rid of all the javascript and the hidden anchor.


UPDATE:

I must have misunderstood your question. From your comments it appears that for some very strange reason to me you are trying to avoid calling the Model.ExportGraphTest function until the button is clicked. Since this is a server side function, you have 2 possibilities to achieve that:

  1. Rewrite the logic that you have inside this function in javascript so that you could invoke it when the button is clicked
  2. Make an AJAX request to the server to calculate the url when the button is clicked.

Let me show an example of how to achieve the second. Start by writing a controller action which will take the necessary properties of the model in order to calculate the link:

public ActionResult GetLink(string graphType, string foo, string bar)
{
    string link = ... calculate the link
    return Json(new { href = link }, JsonRequestBehaviot.AllowGet);
}

and then on the client you will have the anchor:

@Html.ActionLink(
    "download graph", 
    "getlink", 
    "somecontroller", 
    new {
        graphType = "GraphA3",
        foo = Model.Foo,
        bar = Model.Bar,
    }, 
    new { @class = "download" }
)

and when this anchor is clicked you will invoke the action using AJAX to calculate the value:

$('.download').click(function() {
    $.ajax({
        url: this.href,
        type: 'GET',
        cache: false,
        success: function(result) {
            window.location.href = result.href;
        },
        error: function() {
            alert('Oops, something went wrong. Look in the network tab of FireBug to see what did the server return as error during the AJAX request.');
        }
    });
    return false;    
});

This being said, IMHO this approach is flawed. I would recommend you calculating the links when the page loads.

Upvotes: 1

g.pickardou
g.pickardou

Reputation: 35853

No this is not possible that way you trying...

It is important to distinguish between server side and client side. The @Model property exists in server side. However the javascript function you've wrote will run at client side (in the browser.

Possible solution:

You would like to do a XMLHttpRequest() (XHR) from the client side javascript. It is possible to call any url with GET or POST methods via the javascript XMLHttpRequest(). Make sure that at server side there is a controller/action "listening" for that particular url/method.

Upvotes: 0

Related Questions