Domanda

Esiste un modo semplice per gestire più pulsanti di invio dallo stesso modulo? Esempio:

<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" value="Send" />
<input type="submit" value="Cancel" />
<% Html.EndForm(); %>

Qualche idea su come eseguire questa operazione in ASP.NET Framework Beta? Tutti gli esempi per cui ho cercato su Google hanno singoli pulsanti.

È stato utile?

Soluzione

Ecco una soluzione per lo più pulita, basata su attributi, al problema del pulsante di invio multiplo basato pesantemente sul post e sui commenti di Maartin Balliauw .

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class MultipleButtonAttribute : ActionNameSelectorAttribute
{
    public string Name { get; set; }
    public string Argument { get; set; }

    public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
    {
        var isValidName = false;
        var keyValue = string.Format("{0}:{1}", Name, Argument);
        var value = controllerContext.Controller.ValueProvider.GetValue(keyValue);

        if (value != null)
        {
            controllerContext.Controller.ControllerContext.RouteData.Values[Name] = Argument;
            isValidName = true;
        }

        return isValidName;
    }
}

rasoio:

<form action="" method="post">
 <input type="submit" value="Save" name="action:Save" />
 <input type="submit" value="Cancel" name="action:Cancel" />
</form>

e controller:

[HttpPost]
[MultipleButton(Name = "action", Argument = "Save")]
public ActionResult Save(MessageModel mm) { ... }

[HttpPost]
[MultipleButton(Name = "action", Argument = "Cancel")]
public ActionResult Cancel(MessageModel mm) { ... }

Aggiornamento: Pagine di rasoio sembra fornire la stessa funzionalità pronta all'uso. Per i nuovi sviluppi, potrebbe essere preferibile.

Altri suggerimenti

Assegna un nome ai pulsanti di invio, quindi controlla il valore inviato nel metodo del controller:

<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" name="submitButton" value="Send" />
<input type="submit" name="submitButton" value="Cancel" />
<% Html.EndForm(); %>

invio a

public class MyController : Controller {
    public ActionResult MyAction(string submitButton) {
        switch(submitButton) {
            case "Send":
                // delegate sending to another controller action
                return(Send());
            case "Cancel":
                // call another action to perform the cancellation
                return(Cancel());
            default:
                // If they've submitted the form without a submitButton, 
                // just return the view again.
                return(View());
        }
    }

    private ActionResult Cancel() {
        // process the cancellation request here.
        return(View("Cancelled"));
    }

    private ActionResult Send() {
        // perform the actual send operation here.
        return(View("SendConfirmed"));
    }

}

EDIT:

Per estendere questo approccio al funzionamento con siti localizzati, isolare i messaggi altrove (ad es. compilare un file di risorse in una classe di risorse fortemente tipizzata)

Quindi modifica il codice in modo che funzioni come:

<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" name="submitButton" value="<%= Html.Encode(Resources.Messages.Send)%>" />
<input type="submit" name="submitButton" value="<%=Html.Encode(Resources.Messages.Cancel)%>" />
<% Html.EndForm(); %>

e il tuo controller dovrebbe apparire così:

// Note that the localized resources aren't constants, so 
// we can't use a switch statement.

if (submitButton == Resources.Messages.Send) { 
    // delegate sending to another controller action
    return(Send());

} else if (submitButton == Resources.Messages.Cancel) {
     // call another action to perform the cancellation
     return(Cancel());
}

Puoi controllare il nome nell'azione come è stato menzionato, ma potresti considerare se questo è un buon design. È una buona idea considerare la responsabilità dell'azione e non associare troppo questo design agli aspetti dell'interfaccia utente come i nomi dei pulsanti. Quindi considera l'utilizzo di 2 moduli e 2 azioni:

<% Html.BeginForm("Send", "MyController", FormMethod.Post); %>
<input type="submit" name="button" value="Send" />
<% Html.EndForm(); %>

<% Html.BeginForm("Cancel", "MyController", FormMethod.Post); %>
<input type="submit" name="button" value="Cancel" />
<% Html.EndForm(); %>

Inoltre, nel caso di " Annulla " di solito non stai elaborando il modulo e stai andando a un nuovo URL. In questo caso non è necessario inviare il modulo e basta un link:

<%=Html.ActionLink("Cancel", "List", "MyController") %>

Eilon suggerisce che puoi farlo in questo modo:

  

Se hai più di un pulsante tu   riesco a distinguerli dando   ogni pulsante un nome:

<input type="submit" name="SaveButton" value="Save data" />
<input type="submit" name="CancelButton" value="Cancel and go back to main page" />
     

Nel tuo metodo di azione del controller tu   può aggiungere parametri che prendono il nome da   Nomi dei tag di input HTML:

public ActionResult DoSomeStuff(string saveButton, string
cancelButton, ... other parameters ...)
{ ... }
     

Se un valore viene registrato in uno di   quei parametri, ciò significa che   il pulsante era quello su cui è stato fatto clic.   Il browser Web pubblicherà solo un valore   per il pulsante uno su cui è stato fatto clic.   Tutti gli altri valori saranno nulli.

if (saveButton != null) { /* do save logic */ }
if (cancelButton != null) { /* do cancel logic */ }

Mi piace questo metodo in quanto non si basa sulla proprietà value dei pulsanti di invio, che è più probabile che cambi rispetto ai nomi assegnati e non richiede che javascript sia abilitato

Vedi: http://forums.asp.net/p/1369617/2865166.aspx#2865166

Ho appena scritto un post a riguardo: Più pulsanti di invio con ASP.NET MVC :

Fondamentalmente, invece di usare ActionMethodSelectorAttribute, sto usando ActionNameSelectorAttribute, che mi consente di fingere che il nome dell'azione sia quello che voglio che sia. Fortunatamente, <=> non mi fa solo specificare il nome dell'azione, ma posso scegliere se l'azione corrente corrisponde alla richiesta.

Quindi c'è la mia classe (tra l'altro non mi piace troppo il nome):

public class HttpParamActionAttribute : ActionNameSelectorAttribute {
    public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo) {
        if (actionName.Equals(methodInfo.Name, StringComparison.InvariantCultureIgnoreCase))
            return true;

        if (!actionName.Equals("Action", StringComparison.InvariantCultureIgnoreCase))
            return false;

        var request = controllerContext.RequestContext.HttpContext.Request;
        return request[methodInfo.Name] != null;
    }
} 

Per usare basta definire un modulo come questo:

<% using (Html.BeginForm("Action", "Post")) { %>
  <!— …form fields… -->
  <input type="submit" name="saveDraft" value="Save Draft" />
  <input type="submit" name="publish" value="Publish" />
<% } %> 

e controller con due metodi

public class PostController : Controller {
    [HttpParamAction]
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult SaveDraft(…) {
        //…
    }

    [HttpParamAction]
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Publish(…) {
        //…
    } 
}

Come vedi, l'attributo non richiede di specificare nulla. Inoltre, il nome dei pulsanti viene tradotto direttamente nei nomi dei metodi. Inoltre (non ho provato!) # 8217; t ho provato a farlo) dovrebbero funzionare anche come normali azioni, quindi puoi postare su uno di essi direttamente.

Suggerirei le parti interessate dai un'occhiata alla soluzione di Maarten Balliauw . Penso che sia molto elegante.

Nel caso in cui il link scompaia, sta utilizzando l'attributo MultiButton applicato a un'azione del controller per indicare a quale pulsante fare clic a cui l'azione dovrebbe fare riferimento.

è breve e suite:

  

Ha ricevuto risposta Jeroen Dop

<input type="submit" name="submitbutton1" value="submit1" />
<input type="submit" name="submitbutton2" value="submit2" />

e fai così nel code behinde

 if( Request.Form["submitbutton1"] != null)
{
    // Code for function 1
}
else if(Request.Form["submitButton2"] != null )
{
       // code for function 2
}

Buona fortuna.

Dovresti essere in grado di nominare i pulsanti e dare loro un valore; quindi associare questo nome come argomento all'azione. In alternativa, utilizzare 2 link azioni separati o 2 moduli.

Potresti scrivere:

<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" name="button" value="Send" />
<input type="submit" name="button" value="Cancel" />
<% Html.EndForm(); %>

E poi nella pagina controlla se il nome == " Invia " o name == " Annulla " ...

Qualcosa che non mi piace di ActionSelectName è che IsValidName viene chiamato per ogni metodo di azione nel controller; Non so perché funzioni in questo modo. Mi piace una soluzione in cui ogni pulsante ha un nome diverso in base a ciò che fa, ma non mi piace il fatto che devi avere tanti parametri nel metodo di azione come i pulsanti nel modulo. Ho creato un enum per tutti i tipi di pulsanti:

public enum ButtonType
{
    Submit,
    Cancel,
    Delete
}

Invece di ActionSelectName, utilizzo ActionFilter:

public class MultipleButtonsEnumAttribute : ActionFilterAttribute
{
    public Type EnumType { get; set; }

    public MultipleButtonsEnumAttribute(Type enumType)
    {
        EnumType = enumType;
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        foreach (var key in filterContext.HttpContext.Request.Form.AllKeys)
        {
            if (Enum.IsDefined(EnumType, key))
            {
                var pDesc = filterContext.ActionDescriptor.GetParameters()
                    .FirstOrDefault(x => x.ParameterType == EnumType);
                filterContext.ActionParameters[pDesc.ParameterName] = Enum.Parse(EnumType, key);
                break;
            }
        }
    }
}

Il filtro troverà il nome del pulsante nei dati del modulo e se il nome del pulsante corrisponde a uno dei tipi di pulsante definiti nell'enum, troverà il parametro ButtonType tra i parametri di azione:

[MultipleButtonsEnumAttribute(typeof(ButtonType))]
public ActionResult Manage(ButtonType buttonPressed, ManageViewModel model)
{
    if (button == ButtonType.Cancel)
    {
        return RedirectToAction("Index", "Home");
    }
    //and so on
    return View(model)
}

e quindi nelle viste, posso usare:

<input type="submit" value="Button Cancel" name="@ButtonType.Cancel" />
<input type="submit" value="Button Submit" name="@ButtonType.Submit" />

Ecco cosa funziona meglio per me:

<input type="submit" value="Delete" name="onDelete" />
<input type="submit" value="Save" name="onSave" />


public ActionResult Practice(MyModel model, string onSave, string onDelete)
{
    if (onDelete != null)
    {
        // Delete the object
        ...
        return EmptyResult();
    }

    // Save the object
    ...
    return EmptyResult();
}

Se non si hanno restrizioni sull'uso di HTML 5, è possibile utilizzare il tag <button> con formaction Attributo:

<form action="demo_form.asp" method="get">
   First name: <input type="text" name="fname" /><br />
   Last name: <input type="text" name="lname" /><br />
   <button type="submit">Submit</button><br />
   <button type="submit" formaction="demo_admin.asp">Submit as admin</button>
</form>

Riferimento: http://www.w3schools.com/html5/att_button_formaction.asp

Se il tuo browser supporta la formulazione degli attributi per i pulsanti di input (IE 10+, non sono sicuro di altri browser), dovrebbe funzionare quanto segue:

@using (Html.BeginForm()){
    //put form inputs here

<input id="sendBtn" value="Send" type="submit" formaction="@Url.Action("Name Of Send Action")" />

<input id="cancelBtn" value="Cancel" type="submit" formaction="@Url.Action("Name of Cancel Action") />

}

Ho riscontrato anche questo "problema", ma ho trovato una soluzione piuttosto logica aggiungendo l'attributo name. Non ricordo di aver avuto questo problema in altre lingue.

http://www.w3.org/ TR / html401 / interagire / Forms.html # h-17.13.2

  • ...
  • Se un modulo contiene più di un pulsante di invio, solo il pulsante di invio attivato ha esito positivo.
  • ...

Significa che il seguente codice value attributi può essere modificato, localizzato, internazionalizzato senza la necessità per il codice aggiuntivo che controlla file o costanti di risorse fortemente tipizzati.

<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<input type="submit" name="send" value="Send" />
<input type="submit" name="cancel" value="Cancel" />
<input type="submit" name="draft" value="Save as draft" />
<% Html.EndForm(); %>`

Sul lato ricevente dovrai solo verificare se uno dei tuoi tipi di invio noti non è null

public ActionResult YourAction(YourModel model) {

    if(Request["send"] != null) {

        // we got a send

    }else if(Request["cancel"]) {

        // we got a cancel, but would you really want to post data for this?

    }else if(Request["draft"]) {

        // we got a draft

    }

}

Esistono tre modi in cui è possibile risolvere il problema sopra riportato

  1. modo HTML
  2. Modo Jquery
  3. &
  4. # 8220; ActionNameSelectorAttribute # 8221 &; modo

Di seguito è riportato un video che sintetizza tutti e tre gli approcci in modo dimostrativo.

https://www.facebook.com/shivprasad.koirala/ video / vb.100002224977742 / 809.335.512.483.940

Modo HTML: -

In modo HTML dobbiamo creare due moduli e posizionare & # 8220; Invia & # 8221; pulsante all'interno di ciascuna delle forme. E ogni azione della forma indicherà azioni diverse / rispettive. Puoi vedere il codice seguente che il primo modulo sta inviando a & # 8220; Azione1 & # 8221; e il secondo modulo verrà pubblicato su & # 8220; Action2 & # 8221; a seconda di & # 8220; Invia & # 8221; si fa clic sul pulsante.

<form action="Action1" method=post>
<input type=”submit” name=”Submit1”/>
</form>

<form action="Action2" method=post>
<input type=”submit” name=”Submit2”>
</form>

Modo Ajax: -

Se sei un amante dell'Ajax, questa seconda opzione ti entusiasmerebbe di più. Nel modo Ajax possiamo creare due diverse funzioni & # 8220; Fun1 & # 8221; e & # 8220; Fun1 & # 8221; , vedi il codice seguente. Queste funzioni effettueranno chiamate Ajax utilizzando JQUERY o qualsiasi altro framework. Ognuna di queste funzioni è associata a & # 8220; Invia & # 8221; pulsante & # 8217; s & # 8220; OnClick & # 8221; eventi. Ognuna di queste funzioni chiama i rispettivi nomi di azioni.

<Script language="javascript">
function Fun1()
{
$.post(“/Action1”,null,CallBack1);
}
function Fun2()
{
$.post(“/Action2”,null,CallBack2);
}
</Script>

<form action="/Action1" method=post>
<input type=submit name=sub1 onclick=”Fun2()”/>
</form>
<form action="/Action2" method=post>
<input type=submit name=sub2 onclick=”Fun1()”/>
</form>

Uso di & # 8220; ActionNameSelectorAttribute & # 8221;: -

Questa è un'ottima e pulita opzione. & # 8220; ActionNameSelectorAttribute & # 8221; è una semplice classe di attributi in cui possiamo scrivere una logica decisionale che deciderà quale azione può essere eseguita.

Quindi la prima cosa è in HTML che dobbiamo inserire il nome proprio & # 8217; s nei pulsanti di invio per identificarli sul server.

Puoi vedere che abbiamo messo & # 8220; Salva & # 8221; e & # 8220; Elimina & # 8221; ai nomi dei pulsanti. Inoltre puoi notare nell'azione che abbiamo appena messo il nome del controller & # 8220; Cliente & # 8221; e non un nome di azione particolare. Prevediamo che il nome dell'azione sarà deciso da & # 8220; ActionNameSelectorAttribute & # 8221 ;.

<form action=”Customer” method=post>
<input type=submit value="Save" name="Save" /> <br />
<input type=submit value="Delete" name="Delete"/>
</form>

Quindi, quando si fa clic sul pulsante di invio, viene prima colpito & # 8220; ActionNameSelector & # 8221; e quindi, a seconda di quale invio viene attivato, invoca l'azione appropriata.

inserisci qui la descrizione dell'immagine

Quindi il primo passo è creare una classe che eredita da & # 8220; ActionNameSelectorAttribute & # 8221; classe. In questa classe abbiamo creato una proprietà semplice & # 8220; Nome & # 8221 ;.

Dobbiamo anche sostituire & # 8220; IsValidName & # 8221; funzione che restituisce true o flase. Questa funzione è dove scriviamo la logica se un'azione deve essere eseguita o meno. Quindi se questa funzione ritorna vera allora l'azione viene eseguita altrimenti non lo è.

public class SubmitButtonSelector : ActionNameSelectorAttribute
    {
        public string Name { get; set; }
        public override bool IsValidName(ControllerContext controllerContext, string actionName, System.Reflection.MethodInfo methodInfo)
        {
            // Try to find out if the name exists in the data sent from form
var value = controllerContext.Controller.ValueProvider.GetValue(Name);
            if (value != null)
            {
                return true;
            }
            return false;

        }
    }

Il cuore principale della funzione sopra è nel codice qui sotto. Il & # 8220; ValueProvider & # 8221; raccolta contiene tutti i dati che sono stati pubblicati dal modulo. Quindi cerca prima & # 8220; Nome & # 8221; valore e se si trova nella richiesta HTTP restituisce vero oppure restituisce falso.

var value = controllerContext.Controller.ValueProvider.GetValue(Name);
if (value != null)
      {
        return true;
      }
      return false;

Questa classe di attributi può quindi essere decorata sulla rispettiva azione e sul rispettivo & # 8220; Nome & # 8221; valore può essere fornito. Quindi se l'invio sta colpendo questa azione e se il nome corrisponde al nome del pulsante di invio HTML, allora esegue ulteriormente l'azione oppure no.

public class CustomerController : Controller
{
        [SubmitButtonSelector(Name="Save")]
        public ActionResult Save()
        {
            return Content("Save Called");
        }
        [SubmitButtonSelector(Name = "Delete")]
        public ActionResult Delete()
        {
            return Content("Delete Called");
        }
}

David Findley scrive su 3 diverse opzioni che hai per farlo, sul suo blog ASP.Net.

Leggi l'articolo pulsanti multipli nella stessa forma per vedere le sue soluzioni e i vantaggi e gli svantaggi di ciascuno. IMHO fornisce una soluzione molto elegante che utilizza gli attributi con cui decori la tua azione.

Questa è la tecnica che userei e non la vedo ancora qui. Il link (pubblicato da Saajid Ismail ) che ispira questa soluzione è http://weblogs.asp.net/dfindley/archive/2009/05/31/asp-net-mvc-multiple-buttons-in-the-same-form.aspx ). Adatta la risposta di Dylan Beattie per eseguire la localizzazione senza problemi.

Nella vista:

<% Html.BeginForm("MyAction", "MyController", FormMethod.Post); %>
<button name="button" value="send"><%: Resources.Messages.Send %></button>
<button name="button" value="cancel"><%: Resources.Messages.Cancel %></button>
<% Html.EndForm(); %>

Nel controller:

public class MyController : Controller 
{
    public ActionResult MyAction(string button)
    {
         switch(button)
         {
             case "send":
                 this.DoSend();
                 break;
             case "cancel":
                 this.DoCancel();
                 break;
         }
    }
}

Questo script consente di specificare un attributo data-form-action che funzionerà come attributo di formulazione HTML5 in tutti i browser (in modo discreto):

$(document).on('click', '[type="submit"][data-form-action]', function(event) {
    var $this = $(this),
    var formAction = $this.attr('data-form-action'),
    $form = $($this.closest('form'));
    $form.attr('action', formAction);             
});

Il modulo contenente il pulsante verrà pubblicato nell'URL specificato nell'attributo data-form-action:

<button type="submit" data-form-action="different/url">Submit</button>   

Ciò richiede jQuery 1.7. Per le versioni precedenti dovresti usare live() invece di on().

Ecco un metodo di estensione che ho scritto per gestire più pulsanti immagine e / o testo.

Ecco il codice HTML per un pulsante immagine:

<input id="btnJoin" name="Join" src="/content/images/buttons/btnJoin.png" 
       type="image">

o per un pulsante di invio testo:

<input type="submit" class="ui-button green" name="Submit_Join" value="Add to cart"  />
<input type="submit" class="ui-button red" name="Submit_Skip" value="Not today"  />

Ecco il metodo di estensione che chiami dal controller con form.GetSubmitButtonName(). Per i pulsanti immagine cerca un parametro del modulo con .x (che indica che è stato fatto clic su un pulsante immagine) ed estrae il nome. Per i normali pulsanti input cerca un nome che inizia con Submit_ ed estrae il comando da allora in poi. Poiché sto astrarre la logica per determinare il "comando", puoi passare tra i pulsanti immagine + testo sul client senza modificare il codice lato server.

public static class FormCollectionExtensions
{
    public static string GetSubmitButtonName(this FormCollection formCollection)
    {
        return GetSubmitButtonName(formCollection, true);
    }

    public static string GetSubmitButtonName(this FormCollection formCollection, bool throwOnError)
    {
        var imageButton = formCollection.Keys.OfType<string>().Where(x => x.EndsWith(".x")).SingleOrDefault();
        var textButton = formCollection.Keys.OfType<string>().Where(x => x.StartsWith("Submit_")).SingleOrDefault();

        if (textButton != null)
        {
            return textButton.Substring("Submit_".Length);
        }

        // we got something like AddToCart.x
        if (imageButton != null)
        {
            return imageButton.Substring(0, imageButton.Length - 2);
        }

        if (throwOnError)
        {
            throw new ApplicationException("No button found");
        }
        else
        {
            return null;
        }
    }
}

Nota: per i pulsanti di testo devi aggiungere il prefisso al nome SELECT. Preferisco così perché significa che puoi cambiare il valore del testo (display) senza cambiare il codice. A differenza di INPUT elementi, un pulsante == "Add to cart" ha solo un attributo "valore" e nessun attributo "testo" separato. I miei pulsanti dicono cose diverse in contesti diversi, ma mappano allo stesso "comando". Preferisco di gran lunga estrarre il nome in questo modo piuttosto che dover codificare per <=>.

Non ho abbastanza rappresentante per commentare nel posto giusto, ma ho trascorso tutto il giorno su questo, quindi voglio condividere.

Durante il tentativo di implementare il " MultipleButtonAttribute " la soluzione ValueProvider.GetValue(keyValue) ritornerebbe erroneamente null.

Si è scoperto che stavo facendo riferimento a System.Web.MVC versione 3.0 quando avrebbe dovuto essere 4.0 (gli altri assembly sono 4.0). Non so perché il mio progetto non sia stato aggiornato correttamente e non ho avuto altri ovvi problemi.

Quindi se il tuo ActionNameSelectorAttribute non funziona ... controlla quello.

Sono abbastanza tardi alla festa, ma qui va ... La mia implementazione prende in prestito da @mkozicki ma richiede meno stringhe hardcoded per sbagliarsi. Framework 4.5+ richiesto . In sostanza, il nome del metodo del controller dovrebbe essere la chiave per il routing.

Markup . Il nome del pulsante deve essere digitato con "action:[controllerMethodName["

(nota l'uso dell'API C # 6 nameof , fornendo un riferimento specifico al tipo al nome del metodo del controller che si desidera invocare.

<form>
    ... form fields ....
    <button name="action:@nameof(MyApp.Controllers.MyController.FundDeathStar)" type="submit" formmethod="post">Fund Death Star</button>
    <button name="action:@nameof(MyApp.Controllers.MyController.HireBoba)" type="submit" formmethod="post">Hire Boba Fett</button>
</form>

Regolatore :

namespace MyApp.Controllers
{
    class MyController
    {    
        [SubmitActionToThisMethod]
        public async Task<ActionResult> FundDeathStar(ImperialModel model)
        {
            await TrainStormTroopers();
            return View();
        }

        [SubmitActionToThisMethod]
        public async Task<ActionResult> HireBoba(ImperialModel model)
        {
            await RepairSlave1();
            return View();
        }
    }
}

Attributo magico . Si noti l'uso di CallerMemberName bontà.

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class SubmitActionToThisMethodAttribute : ActionNameSelectorAttribute
{        
    public SubmitActionToThisMethodAttribute([CallerMemberName]string ControllerMethodName = "")
    {
        controllerMethod = ControllerMethodName;
        actionFormat = string.Concat(actionConstant, ":", controllerMethod);
    }
    const string actionConstant = "action";
    readonly string actionFormat;
    readonly string controllerMethod;

    public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
    {
        var isValidName = false;
        var value = controllerContext.Controller.ValueProvider.GetValue(actionFormat);

        if (value != null)
        {
            controllerContext.Controller.ControllerContext.RouteData.Values[actionConstant] = controllerMethod;
            isValidName = true;
        }
        return isValidName;
    }
}

Ho provato a fare una sintesi di tutte le soluzioni e ho creato un attributo [ButtenHandler] che semplifica la gestione di più pulsanti in un modulo.

L'ho descritto su CodeProject Parametri multipli ( localizzabili) pulsanti modulo in ASP.NET MVC .

Per gestire il semplice caso di questo pulsante:

<button type="submit" name="AddDepartment">Add Department</button>

Avrai qualcosa come il seguente metodo di azione:

[ButtonHandler()]
public ActionResult AddDepartment(Company model)
{
    model.Departments.Add(new Department());
    return View(model);
}

Nota come il nome del pulsante corrisponde al nome del metodo di azione. L'articolo descrive anche come avere pulsanti con valori e pulsanti con indici.

//model
    public class input_element
        {
         public string Btn { get; set; }
        }   

//views--submit btn can be input type also...
    @using (Html.BeginForm())
    {
            <button type="submit" name="btn" value="verify">
             Verify data</button>
            <button type="submit" name="btn" value="save">
             Save data</button>    
            <button type="submit" name="btn" value="redirect">
                 Redirect</button>
    }

//controller

    public ActionResult About()
        {
            ViewBag.Message = "Your app description page.";
            return View();
        }

        [HttpPost]
        public ActionResult About(input_element model)
        {
                if (model.Btn == "verify")
                {
                // the Verify button was clicked
                }
                else if (model.Btn == "save")
                {
                // the Save button was clicked
                } 
                else if (model.Btn == "redirect")
                {
                // the Redirect button was clicked
                } 
                return View();
        }

questo è il modo migliore che ho trovato:

http: // iwayneo. blogspot.co.uk/2013/10/aspnet-mvc-action-selector-with-list.html

Ecco il codice:

    /// <summary>
    /// ActionMethodSelector to enable submit buttons to execute specific action methods.
    /// </summary>
    public class AcceptParameterAttribute : ActionMethodSelectorAttribute
   {
        /// <summary>
        /// Gets or sets the value to use to inject the index into
        /// </summary>
       public string TargetArgument { get; set; }

       /// <summary>
       /// Gets or sets the value to use in submit button to identify which method to select. This must be unique in each controller.
       /// </summary>
       public string Action { get; set; }

       /// <summary>
       /// Gets or sets the regular expression to match the action.
       /// </summary>
       public string ActionRegex { get; set; }

       /// <summary>
       /// Determines whether the action method selection is valid for the specified controller context.
       /// </summary>
       /// <param name="controllerContext">The controller context.</param>
       /// <param name="methodInfo">Information about the action method.</param>
       /// <returns>true if the action method selection is valid for the specified controller context; otherwise, false.</returns>
       public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
       {

           if (controllerContext == null)
           {
               throw new ArgumentNullException("controllerContext");
           }

           Func<NameValueCollection> formGetter;
           Func<NameValueCollection> queryStringGetter;

           ValidationUtility.GetUnvalidatedCollections(HttpContext.Current, out formGetter, out queryStringGetter);

           var form = formGetter();
           var queryString = queryStringGetter();

           var req = form.AllKeys.Any() ? form : queryString;

           if (!string.IsNullOrEmpty(this.ActionRegex))
           {
               foreach (var key in req.AllKeys.Where(k => k.StartsWith(Action, true, System.Threading.Thread.CurrentThread.CurrentCulture)))
               {
                   if (key.Contains(":"))
                   {
                       if (key.Split(':').Count() == this.ActionRegex.Split(':').Count())
                       {
                           bool match = false;
                           for (int i = 0; i < key.Split(':').Count(); i++)
                           {
                               if (Regex.IsMatch(key.Split(':')[0], this.ActionRegex.Split(':')[0]))
                               {
                                   match = true;
                               }
                               else
                               {
                                   match = false;
                                   break;
                               }
                           }

                           if (match)
                           {
                               return !string.IsNullOrEmpty(req[key]);
                           }
                       }
                   }
                   else
                   {
                       if (Regex.IsMatch(key, this.Action + this.ActionRegex))
                       {
                           return !string.IsNullOrEmpty(req[key]);
                       }
                   }

               }
               return false;
           }
           else
           {
               return req.AllKeys.Contains(this.Action);
           }
       }
   }

Goditi un futuro pulsante di invio multiplo senza odore di codice.

grazie

[HttpPost]
public ActionResult ConfirmMobile(string nameValueResend, string nameValueSubmit, RegisterModel model)
    {
        var button = nameValueResend ?? nameValueSubmit;
        if (button == "Resend")
        {

        }
        else
        {

        }
    }


    Razor file Content:
    @using (Html.BeginForm()
    {
        <div class="page registration-result-page">

            <div class="page-title">
                <h1> Confirm Mobile Number</h1>
            </div>

            <div class="result">
                @Html.EditorFor(model => model.VefificationCode)
                @Html.LabelFor(model => model.VefificationCode, new { })
                @Html.ValidationMessageFor(model => model.VefificationCode)
            </div>
            <div class="buttons">
                <button type="submit" class="btn" name="nameValueResend" value="Resend">
                    Resend
                </button>
                <button type="submit" class="btn" name="nameValueSubmit" value="Verify">
                    Submit
                </button>

            </div>
            </div>

    }

Versione modificata del metodo HttpParamActionAttribute ma con una correzione di bug per non causare un errore sui postback della sessione scaduti / non validi. Per vedere se questo è un problema con il tuo sito attuale, apri il modulo in una finestra e appena prima di andare a fare clic su Salva o Pubblica , apri una finestra duplicata e disconnettersi. Ora torna alla prima finestra e prova a inviare il modulo utilizzando uno dei due pulsanti. Per me ho ricevuto un errore, quindi questa modifica risolve il problema per me. Ometto un sacco di cose per brevità, ma dovresti avere l'idea. Le parti chiave sono l'inclusione di ActionName nell'attributo e la verifica che il nome passato sia il nome della vista che mostra il modulo

Classe di attributi

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class HttpParamActionAttribute : ActionNameSelectorAttribute
{
    private readonly string actionName;

    public HttpParamActionAttribute(string actionName)
    {
        this.actionName = actionName;
    }

    public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
    {
        if (actionName.Equals(methodInfo.Name, StringComparison.InvariantCultureIgnoreCase))
            return true;

        if (!actionName.Equals(this.actionName, StringComparison.InvariantCultureIgnoreCase))
            return false;

        var request = controllerContext.RequestContext.HttpContext.Request;
        return request[methodInfo.Name] != null;
    }
}

Regolatore

[Authorize(Roles="CanAddContent")]
public ActionResult CreateContent(Guid contentOwnerId)
{
    var viewModel = new ContentViewModel
    {
        ContentOwnerId = contentOwnerId
        //populate rest of view model
    }
    return View("CreateContent", viewModel);
}

[Authorize(Roles="CanAddContent"), HttpPost, HttpParamAction("CreateContent"), ValidateAntiForgeryToken]
public ActionResult SaveDraft(ContentFormModel model)
{
    //Save as draft
    return RedirectToAction("CreateContent");
}

[Authorize(Roles="CanAddContent"), HttpPost, HttpParamAction("CreateContent"), ValidateAntiForgeryToken]
public ActionResult Publish(ContentFormModel model)
{
    //publish content
    return RedirectToAction("CreateContent");
}

Visualizza

@using (Ajax.BeginForm("CreateContent", "MyController", new { contentOwnerId = Model.ContentOwnerId }))
{
    @Html.AntiForgeryToken()
    @Html.HiddenFor(x => x.ContentOwnerId)

    <!-- Rest of your form controls -->
    <input name="SaveDraft" type="submit" value="SaveDraft" />
    <input name="Publish" type="submit" value="Publish" />
}

Il mio approccio JQuery usando un metodo di estensione:

public static MvcHtmlString SubmitButtonFor<TController>(this HtmlHelper helper, Expression<Action<TController>> action, string value) where TController : Controller
{
    RouteValueDictionary routingValues = Microsoft.Web.Mvc.Internal.ExpressionHelper.GetRouteValuesFromExpression(action);

    var onclick = string.Format("$('form').first().attr('action', '/{0}')", string.Join("/", routingValues.Values.ToArray().Where(x => x != null).Select(x => x.ToString()).ToArray()));
    var html = "<input type=\"submit\" value=\"" + value + "\" onclick=\"" + onclick + "\" />";

    return MvcHtmlString.Create(html);
}

Puoi usarlo in questo modo:

@(Html.SubmitButtonFor<FooController>(c => c.Save(null), "Save"))

E rende in questo modo:

<input type="submit" value="Save" onclick="$('form').first().attr('action', '/Foo/Save')" >

Per ogni pulsante di invio basta aggiungere:

$('#btnSelector').click(function () {

    $('form').attr('action', "/Your/Action/);
    $('form').submit();

});

Sulla base della risposta di mkozicki, mi viene in mente una soluzione leggermente diversa. Uso ancora ActionNameSelectorAttribute ma avevo bisogno di gestire due pulsanti "Salva" e "Sincronizza". Fanno quasi lo stesso, quindi non volevo fare due azioni.

attributo :

public class MultipleButtonActionAttribute : ActionNameSelectorAttribute
{        
    private readonly List<string> AcceptedButtonNames;

    public MultipleButtonActionAttribute(params string[] acceptedButtonNames)
    {
        AcceptedButtonNames = acceptedButtonNames.ToList();
    }

    public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
    {            
        foreach (var acceptedButtonName in AcceptedButtonNames)
        {
            var button = controllerContext.Controller.ValueProvider.GetValue(acceptedButtonName);
            if (button == null)
            {
                continue;
            }                
            controllerContext.Controller.ControllerContext.RouteData.Values.Add("ButtonName", acceptedButtonName);
            return true;
        }
        return false;
    }
}

Visualizza

<input type="submit" value="Save" name="Save" />
<input type="submit" value="Save and Sync" name="Sync" />

Controller

 [MultipleButtonAction("Save", "Sync")]
 public ActionResult Sync(OrgSynchronizationEditModel model)
 {
     var btn = this.RouteData.Values["ButtonName"];

Voglio anche sottolineare che se le azioni fanno cose diverse probabilmente seguirò il post di mkozicki.

Ho creato un metodo ActionButton per HtmlHelper . Genererà il normale pulsante di input con un po 'di javascript nell'evento OnClick che invierà il modulo al controller / azione specificato.

Usi l'helper in quel modo

@Html.ActionButton("MyControllerName", "MyActionName", "button text")

questo genererà il seguente HTML

<input type="button" value="button text" onclick="this.form.action = '/MyWebsiteFolder/MyControllerName/MyActionName'; this.form.submit();">

Ecco il codice del metodo di estensione:

VB.Net

<System.Runtime.CompilerServices.Extension()>
Function ActionButton(pHtml As HtmlHelper, pAction As String, pController As String, pRouteValues As Object, pBtnValue As String, pBtnName As String, pBtnID As String) As MvcHtmlString
    Dim urlHelperForActionLink As UrlHelper
    Dim btnTagBuilder As TagBuilder

    Dim actionLink As String
    Dim onClickEventJavascript As String

    urlHelperForActionLink = New UrlHelper(pHtml.ViewContext.RequestContext)
    If pController <> "" Then
        actionLink = urlHelperForActionLink.Action(pAction, pController, pRouteValues)
    Else
        actionLink = urlHelperForActionLink.Action(pAction, pRouteValues)
    End If
    onClickEventJavascript = "this.form.action = '" & actionLink & "'; this.form.submit();"

    btnTagBuilder = New TagBuilder("input")
    btnTagBuilder.MergeAttribute("type", "button")

    btnTagBuilder.MergeAttribute("onClick", onClickEventJavascript)

    If pBtnValue <> "" Then btnTagBuilder.MergeAttribute("value", pBtnValue)
    If pBtnName <> "" Then btnTagBuilder.MergeAttribute("name", pBtnName)
    If pBtnID <> "" Then btnTagBuilder.MergeAttribute("id", pBtnID)

    Return MvcHtmlString.Create(btnTagBuilder.ToString(TagRenderMode.Normal))
End Function

C # (il codice C # è appena decompilato dalla DLL VB, quindi può ottenere un po 'di abbellimento ... ma il tempo è così breve :-))

public static MvcHtmlString ActionButton(this HtmlHelper pHtml, string pAction, string pController, object pRouteValues, string pBtnValue, string pBtnName, string pBtnID)
{
    UrlHelper urlHelperForActionLink = new UrlHelper(pHtml.ViewContext.RequestContext);
    bool flag = Operators.CompareString(pController, "", true) != 0;
    string actionLink;
    if (flag)
    {
        actionLink = urlHelperForActionLink.Action(pAction, pController, System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue(pRouteValues));
    }
    else
    {
        actionLink = urlHelperForActionLink.Action(pAction, System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue(pRouteValues));
    }
    string onClickEventJavascript = "this.form.action = '" + actionLink + "'; this.form.submit();";
    TagBuilder btnTagBuilder = new TagBuilder("input");
    btnTagBuilder.MergeAttribute("type", "button");
    btnTagBuilder.MergeAttribute("onClick", onClickEventJavascript);
    flag = (Operators.CompareString(pBtnValue, "", true) != 0);
    if (flag)
    {
        btnTagBuilder.MergeAttribute("value", pBtnValue);
    }
    flag = (Operators.CompareString(pBtnName, "", true) != 0);
    if (flag)
    {
        btnTagBuilder.MergeAttribute("name", pBtnName);
    }
    flag = (Operators.CompareString(pBtnID, "", true) != 0);
    if (flag)
    {
        btnTagBuilder.MergeAttribute("id", pBtnID);
    }
    return MvcHtmlString.Create(btnTagBuilder.ToString(TagRenderMode.Normal));
}

Questi metodi hanno vari parametri, ma per la facilità d'uso è possibile creare un sovraccarico che accetta solo i parametri necessari.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top