Gestire più bottoni in una form HTML con ASP.NET MVC

di Stefano Mostarda, in ASP.NET,

Una delle funzionalità più comuni di un'applicazione è quella di avere una form con diversi pulsanti che eseguono diverse azioni (ad esempio: Salva, Sincronizza, Esporta, Invia etc etc.).

Quando si usa ASP.NET WebForm gestire una funzionalità del genere è banale perchè ASP.NET genera una sola form che posta su se stessa e che a seconda del pulsante premuto ci offre un evento diverso. L'ovvio vantaggio di questa tecnica è l'estrema semplicità del codice e la facilità nel gestire nuovi pulsanti. Al contrario, l'ovvio svantaggio sta nelle performance. Ad esempio, supponiamo di avere una form con i pulsanti Salva ed Elimina. Al click del primo pulsante dobbiamo postare l'intera form così che nella action Salva aggiorniamo il database. Al click del pulsante Elimina non abbiamo bisogno di postare l'intera form in quanto ci serve soltanto l'id dell'oggetto da cancellare (ID che normalmente è già nell'url). Tuttavia, con ASP.NET WebForm anche il click del pulsante Elimina scatena il post dell'intera form facendo viaggiare in rete dei dati che non servono.

Quando usiamo ASP.NET MVC non abbiamo nessun automatismo per gestire più pulsanti, quindi la palla è completamente nelle nostre mani.

Partiamo dal primo caso in cui vogliamo postare sempre l'intera form qualunque sia il pulsante che premiamo al suo interno.

Una form - più pulsanti

Una form HTML ha un attributo action che corrisponde all'url a cui viene inviato il post. A questo url corrisponde una action in un controller MVC. Questo significa che quando viene postata una form HTML, MVC richiama sempre la stessa action a prescindere dal pulsante che viene cliccato. Abbiamo diversi modi per ovviare a questo problema, vediamoli uno ad uno.

Gestire tutti i pulsanti in un unica action MVC

Visto che una form HTML deve essere postata verso un url, possiamo inviare tra i dati in post il bottone che è stato cliccato. In questo modo, nella action MVC possiamo fare uno switch ed eseguire il codice corretto in base al pulsante cliccato.

Il codice HTML sarebbe qualcosa del genere:

using(Html.BeginForm()){
 <button name="RealAction" value="Save">Salva</button>
 <button name="RealAction" value="Sync">Sincronizza</button>
 }

Mentre il codice server sarebbe così (considerando che nel model ho messo una proprietà RealAction):

public ActionResult MyAction(MyModel model){
   if (model.<strong>RealAction == "Save"</strong>)
     Save();
   else if (model.<strong>RealAction == "Sync"</strong>)
     Sync();
 }

Questa soluzione non mi piace affatto perchè il codice della action è inguardabile. Non solo, il codice è anche poco chiaro a chi deve "studiare" il codice (magari perchè deve mantenerlo). Insomma, come metodo non lo suggerirei a nessuno.

Cambiare la action nel form HTML via Javascript

Un'altra soluzione (che è anche la più semplice IMHO) è quella di gestire il click del pulsante sul client e cambiare la action della form HTML via Javascript. Questo possiamo farlo in una riga di HTML/JavaScript.

<input type="button" value="Salva" onclick="$('#myform').attr('action', 'customers/save').submit();"/>
 <input type="button" value="Sincronizza" onclick="$('#myform').attr('action', 'customers/sync').submit();"/>

Il vantaggio di questa tecnica sta nell'indubbia semplicità e non ho trovato nessuna controindicazione (almeno nei casi in cui l'ho applicata).

Modificare il meccanismo di scelta della action MVC

L'ultima tecnica che vi mostro è sicuramente la più complicata (anche se in realtà sono poche righe di codice) ma allo stesso tempo la più elegante. L'idea è semplice: il post della form HTML viene eseguito sempre allo stesso url, ma sul server sfruttiamo un "action method selector" personalizzato per fare scegliere quale action MVC invocare in base al tasto cliccato. Il primo passo consiste nel creare un attributo (da applicare sulle action) che decide se la action può essere invocata o meno.

public class CustomActionNameSelectorAttribute : ActionMethodSelectorAttribute
{
     public string ParameterName { get; set; }
    public string ParameterValue { get; set; }
     public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
    {
        var request = controllerContext.RequestContext.HttpContext.Request;
        return request.Form[this.ParameterName] == this.ParameterValue;
    }
}

Questo attributo specifica che la action è invocabile solo se nei parametri di post c'è un elemento il cui nome ed il valore corrispondono rispettivamente alle proprietà ParameterName e ParameterValue dell'attributo. Possiamo utilizzare l'attributo nel seguente modo:

[ActionName("MyAction")]
[CustomActionNameSelectorAttribute(Name = "button", Value = "Save")]
public ActionResult Save()
{
...
}
[ActionName("MyAction")]
[CustomActionNameSelectorAttribute(Name = "button", Value = "Sync")]
public ActionResult Sync()
{
...
}

Tramite l'attributo ActionName specifico che le action rispondono tutte all'url "/controller/MyAction". Tramite l'attributo CustomActionNameSelectorAttribute specifico che la action Save viene invocata solo se nei parametri di post trovo una la chiave "button" con valore "Save", mentre la action Sync viene invocata solo se nei parametri di post trovo una la chiave "button" con valore "Sync". Personalmente mi piace molto questa tecnica perchè mi piace l'idea che la scelta della action dipenda esclusivamente dal codice server e non dal Javascript come nell'esempio precedente.

Più form - più pulsanti

Nel caso in cui abbiamo più pulsanti ma non vogliamo postare l'intera form (come nel caso di una cancellazione), abbiamo diverse strade. la prima è quella di creare una form vuota e farne il submit via Javascript al click del pulsante. Una seconda alternativa è quella di creare a runtime una form, attaccarla al body, impostarne la action e poi farne il submit (ovviamente sempre via Javascript). Nonostante il vantaggio in termini di performance (per via del fatto che non viene postata l'intera form con tutti I dati, ma solo una vuota), entrambe le strade richedono la scrittura di codice Javascript e quindi personalmente non le prediligo.

Stay tuned...

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Nella stessa categoria
I più letti del mese