This post was originally published on Coding Glamour.

Alles op den ganse aarde is tegenwoordig Ajaxified, en een behoorlijk aantal websites heeft haar Ajax interacties Javascript-only gemaakt. Nogal vervelend als een aanzienlijk deel van je gebruikers onder werktijd je website probeert te gebruiken. Met ASP.NET MVC is het behoorlijk gemakkelijk gemaakt om eenvoudige interacties ook beschikbaar te maken voor non-Javascript clients.

De 'Bewaren' knop
Men neme als voorbeeld, de 'Bewaren als favoriet' functionaliteit zoals deze op elke detailpagina te zien is. Een knop met drie states:
http://www.100procentjan.nl/tweakers/nonjs_bewaren.png
http://www.100procentjan.nl/tweakers/nonjs_bewaarprogress.png
http://www.100procentjan.nl/tweakers/nonjs_bewaard.png

Aan de achterkant doet dit niets anders dan een GET naar de controller 'ClientActie' (GETs zijn goedkoper dan POSTs) via:

$.get('/clientactie/bewaarobject/?id=47883154', function(d) { /* change state */ }, 'json');
En nu zonder Javascript?!
Allereerst is het zaak om altijd een werkende link te hebben achter een knop. Wanneer je 'Telefoon' linkje normaal direct het telefoonnummer toont, zorg er dan voor dat de <a href/> naar je contact-pagina wijst. Zo breek je je interactie niet. Voor het 'Bewaren' linkje hebben we de volgende link:

<a href="?clientactie=bewaarobject" onclick="...">
    <strong><span class="icn-favor">Bewaren als favoriet</span></strong>
</a>

Als de gebruiker nu klikt, wordt hij doorgestuurd naar '/huidigepagina/?clientactie=bewaarobject'. We moeten dit nu enkel afvangen in de controller die deze pagina serveert.

Action filter
In ASP.NET MVC kan je 'action filters' gebruiken om via attributes op je actions generiek gedrag te implementeren. Een filter voor de 'clientactie' behavior ziet er ongeveer zo uit:

public class ClientActieHandler : ActionFilterAttribute {
    public override void OnActionExecuting(FilterExecutingContext context) {
        // haal huidige actie uit de context
        var clientactie = context.HttpContext.Request["clientactie"];
        
        if(!string.IsNullOrEmpty(clientactie))
        {
            ClientActieController clientActieController = new ClientActieController();
            
            switch(clientactie)
            {
                case "bewaarobject":
                    // haal huidig ID uit de context
                    // ja dit is niet echt mooi; maar soit
                    var id = int.Parse(context.RouteData.Values["id"].ToString());
                    clientActieController.BewaarObject(id);
                    break;
            }
        }
    }
}

Nu kan je je Action decoraten met het attribute:

public class DetailController : Controller {
    // dit is de normale detail controller die de detailpagina's rendert
    
    [ClientActieHandler]
    public ActionResult Overzicht() {
        /* inner working */
    }
}

Et voila! Bij elke aanroep wordt bekeken of er nog clientacties zijn die verwerkt moeten worden.

View
In de view hoeven we geen wijzigingen te maken omdat er hier geen verschillen zijn tussen de Javascript en de non-Javascriptgebruiker (pseudo-code):

    <% if(Profile.IsStoredObject(Model.Id)) { %>
        <!-- render 'bewaard' knop -->
    <% } else { %>
        <!-- render 'bewaren' knop -->
    <% } %>