web-dev-qa-db-fra.com

Comment gérez-vous plusieurs boutons de soumission dans ASP.NET MVC Framework?

Existe-t-il un moyen simple de gérer plusieurs boutons de soumission à partir du même formulaire? Exemple:

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

Toute idée de comment faire cela dans ASP.NET Framework Beta? Tous les exemples que j'ai googlé pour avoir des boutons simples en eux.

699
Spoike

Voici une solution presque entièrement basée sur les attributs et résolue au problème du bouton de soumission multiple, largement basée sur la publication et les commentaires de 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;
    }
}

rasoir:

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

et contrôleur:

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

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

Mise à jour: pages Razor cherche à fournir les mêmes fonctionnalités immédiatement. Pour les nouveaux développements, cela peut être préférable.

594
mkozicki

Donnez un nom à vos boutons de soumission, puis inspectez la valeur soumise dans la méthode de votre contrôleur:

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

poster sur

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"));
    }

}

MODIFIER:

Pour étendre cette approche afin de travailler avec des sites localisés, isolez vos messages ailleurs (par exemple, en compilant un fichier de ressources en une classe de ressources fortement typée).

Puis modifiez le code pour qu'il fonctionne comme suit:

<% 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(); %>

et votre contrôleur devrait ressembler à ceci:

// 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());
}
455
Dylan Beattie

Vous pouvez vérifier le nom dans l'action comme cela a été mentionné, mais vous pouvez également décider s'il s'agit d'un bon design. C'est une bonne idée de considérer la responsabilité de l'action et de ne pas trop coupler cette conception aux aspects de l'interface utilisateur tels que les noms de boutons. Pensez donc à utiliser 2 formulaires et 2 actions:

<% 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(); %>

En outre, dans le cas de "Annuler", vous ne traitez généralement pas le formulaire et vous accédez à une nouvelle URL. Dans ce cas, vous n'avez pas du tout besoin de soumettre le formulaire et vous avez simplement besoin d'un lien:

<%=Html.ActionLink("Cancel", "List", "MyController") %>
119
Trevor de Koekkoek

Eilon suggère que vous puissiez le faire comme ceci:

Si vous avez plusieurs boutons, vous pouvez les distinguer en leur attribuant un nom:

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

Dans la méthode d'action de votre contrôleur, vous pouvez ajouter des paramètres nommés d'après les noms de balises HTML:

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

Si une valeur est affectée à l'un de ces paramètres, cela signifie que le bouton est celui sur lequel l'utilisateur a cliqué. Le navigateur Web ne publiera une valeur que pour le un bouton sur lequel vous avez cliqué. Toutes les autres valeurs seront nulles.

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

J'aime cette méthode car elle ne repose pas sur la propriété value des boutons de soumission, qui est plus susceptible de changer que les noms attribués et ne nécessite pas l'activation de javascript.

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

95
PabloBlamirez

Je viens d'écrire un article à ce sujet: Plusieurs boutons d'envoi avec ASP.NET MVC :

Fondamentalement, au lieu d’utiliser ActionMethodSelectorAttribute, j’utilise ActionNameSelectorAttribute, ce qui me permet de prétendre que le nom de l’action correspond à tout ce que je veux. Heureusement, ActionNameSelectorAttribute ne me demande pas simplement de spécifier le nom de l'action, je peux plutôt choisir si l'action en cours correspond à la demande.

Il y a donc ma classe (d'ailleurs je n'aime pas trop le nom):

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;
    }
} 

Pour utiliser, définissez simplement un formulaire comme celui-ci:

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

et contrôleur avec deux méthodes

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

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

Comme vous le voyez, l'attribut ne vous oblige pas à spécifier quoi que ce soit. De plus, les noms des boutons sont traduits directement en noms de méthodes. De plus (je n’ai pas essayé cela), elles devraient également fonctionner comme des actions normales, vous pouvez donc les publier directement.

43
Andrey Shchekin

Je suggérerais les parties intéressées jetez un oeil à la solution de Maarten Balliauw . Je pense que c'est très élégant.

Au cas où le lien disparaîtrait, il utilise l'attribut MultiButton appliqué à une action du contrôleur pour indiquer le bouton sur lequel cette action doit se rapporter.

35
Izmoto

c'est court et suite:

Il a été répondu par Jeroen Dop

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

et faire comme ça dans le code behinde

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

Bonne chance.

31
Ali Golgol

Vous devriez pouvoir nommer les boutons et leur donner une valeur; puis mappez ce nom en tant qu'argument de l'action. Vous pouvez également utiliser 2 liens d'action distincts ou 2 formulaires.

21
Marc Gravell

Vous pouvez écrire:

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

Et ensuite, dans la page, vérifiez si le nom == "Envoyer" ou le nom == "Annuler" ...

13
Ironicnet

Quelque chose que je n’aime pas dans ActionSelectName est que IsValidName est appelé pour chaque méthode d’action du contrôleur; Je ne sais pas pourquoi cela fonctionne de cette façon. J'aime une solution où chaque bouton a un nom différent en fonction de ce qu'il fait, mais je n'aime pas le fait que vous devez avoir autant de paramètres dans la méthode d'action que de boutons dans le formulaire. J'ai créé une énumération pour tous les types de boutons:

public enum ButtonType
{
    Submit,
    Cancel,
    Delete
}

Au lieu d'ActionSelectName, j'utilise un 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;
            }
        }
    }
}

Le filtre trouvera le nom du bouton dans les données du formulaire et si le nom du bouton correspond à l'un des types de bouton définis dans l'énumération, il trouvera le paramètre ButtonType parmi les paramètres d'action:

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

et puis dans les vues, je peux utiliser:

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

Voici ce qui fonctionne le mieux pour moi:

<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();
}
11
Sergey

Si votre navigateur prend en charge l'attribut formaction pour les boutons de saisie (IE 10+, vous n'êtes pas sûr des autres navigateurs), les opérations suivantes devraient fonctionner:

@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") />

}
9
user3361233

Si vous n'avez pas de restrictions sur l'utilisation de HTML 5, vous pouvez utiliser la balise <button> avec formaction Attribute:

<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>

Référence: http://www.w3schools.com/html5/att_button_formaction.asp

9
Acaz Souza

J'ai rencontré ce problème aussi, mais j'ai trouvé une solution assez logique en ajoutant l'attribut namename__. Je ne me souvenais pas avoir ce problème dans d'autres langues.

http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2

  • ...
  • Si un formulaire contient plus d'un bouton d'envoi, seul le bouton d'envoi activé réussit.
  • ...

Cela signifie que les attributs de code valuepeuvent être modifiés, localisés, internationalisés sans que le code soit nécessaire pour la vérification supplémentaire du code des fichiers de ressources ou des constantes fortement typés.

<% 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(); %>`

Du côté du destinataire, il vous suffira de vérifier si l’un de vos types de soumission connus n’est pas nullname__

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

    }

}
9
Tom Hofman

Il existe trois façons de résoudre le problème ci-dessus.

  1. Manière HTML
  2. Jquery way
  3. Manière “ActionNameSelectorAttribute”

Ci-dessous, une vidéo qui résume les trois approches de manière démonstrative.

https://www.facebook.com/shivprasad.koirala/videos/vb.100002224977742/80933551248394

façon HTML: -

Au format HTML, nous devons créer deux formulaires et placer le bouton "Soumettre" dans chacun des formulaires. Et chaque action de formulaire indiquera des actions différentes/respectives. Vous pouvez voir le code ci-dessous dans lequel le premier formulaire est en train d’être envoyé sur "Action1" et le deuxième formulaire dans "Action2", en fonction du bouton "Soumettre" sur lequel on a cliqué.

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

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

Manière Ajax: -

Si vous êtes un amateur d’Ajax, cette seconde option vous passionnera davantage. De manière Ajax, nous pouvons créer deux fonctions différentes “Fun1” et “Fun1”, voir le code ci-dessous. Ces fonctions feront des appels Ajax en utilisant JQUERY ou tout autre framework. Chacune de ces fonctions est liée aux événements "OnClick" du bouton "Soumettre". Chacune de ces fonctions appelle les noms d'action respectifs.

<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>

Utilisation de "ActionNameSelectorAttribute": -

Ceci est une excellente et une option propre. “ActionNameSelectorAttribute” est une classe d'attributs simple dans laquelle nous pouvons écrire une logique de prise de décision qui décidera quelle action peut être exécutée.

La première chose est donc en HTML, nous devons attribuer un nom propre aux boutons de soumission pour les identifier sur le serveur.

Vous pouvez voir que nous avons mis "Enregistrer" et "Supprimer" aux noms des boutons. Vous pouvez également remarquer que l'action que nous venons de mettre porte le nom du contrôleur "Client" et non un nom d'action particulier. Nous nous attendons à ce que le nom de l'action soit défini par "ActionNameSelectorAttribute".

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

Ainsi, lorsque le bouton de soumission est cliqué, il appuie d'abord sur l'attribut "ActionNameSelector", puis, selon le type de soumission déclenché, il appelle l'action appropriée.

enter image description here

La première étape consiste donc à créer une classe qui hérite de la classe "ActionNameSelectorAttribute". Dans cette classe, nous avons créé une propriété simple “Nom”.

Nous devons également remplacer la fonction “IsValidName” qui renvoie true ou flase. Cette fonction est l'endroit où nous écrivons la logique si une action doit être exécutée ou non. Donc, si cette fonction renvoie true, l'action est exécutée ou non.

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;

        }
    }

Le coeur principal de la fonction ci-dessus est dans le code ci-dessous. La collection "ValueProvider" contient toutes les données publiées à partir du formulaire. Ainsi, il commence par rechercher la valeur "Name" et, si elle est trouvée dans la requête HTTP, elle renvoie true ou sinon, elle renvoie false.

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

Cette classe d'attributs peut ensuite être décorée sur l'action respective et la valeur "Nom" correspondante peut être fournie. Ainsi, si la soumission est en train de frapper cette action et si le nom du bouton de soumission HTML correspond, il exécute ensuite l'action plus loin, sinon ce n'est pas le cas.

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

Ce script permet de spécifier un attribut data-form-action qui fonctionnera comme attribut HTML5 formaction dans tous les navigateurs (de manière non intrusive):

$(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);             
});

Le formulaire contenant le bouton sera publié sur l'URL spécifiée dans l'attribut data-form-action:

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

Cela nécessite jQuery 1.7. Pour les versions précédentes, vous devriez utiliser live() au lieu de on().

7
Jay Douglass

David Findley écrit sur 3 options différentes que vous avez pour cela, sur son blog ASP.Net.

Lisez l'article plusieurs boutons sous la même forme pour voir ses solutions, ainsi que les avantages et inconvénients de chacune. IMHO il fournit une solution très élégante qui utilise les attributs avec lesquels vous décorez votre action.

7
Saajid Ismail

C'est la technique que j'utiliserais et je ne la vois pas encore ici. Le lien (publié par Saajid Ismail) qui inspire cette solution est http://weblogs.asp.net/dfindley/archive/2009/05/31/asp-net-mvc-multiple-buttons-in-the -same-form.aspx ). Il adapte la réponse de Dylan Beattie pour faire la localisation sans aucun problème.

Dans la vue:

<% 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(); %>

Dans le contrôleur:

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

Voici une méthode d'extension que j'ai écrite pour gérer plusieurs boutons image et/ou texte.

Voici le code HTML pour un bouton d'image:

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

ou pour un bouton d'envoi de texte:

<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"  />

Voici la méthode d'extension que vous appelez à partir du contrôleur avec form.GetSubmitButtonName(). Pour les boutons d’image, il recherche un paramètre de formulaire avec .x (qui indique qu’un bouton d’image a été cliqué) et extrait le nom. Pour les boutons input normaux, il recherche un nom commençant par Submit_ et extrait la commande par la suite. Comme j'abstiens la logique de la détermination de la "commande", vous pouvez basculer entre les boutons image + texte du client sans changer le code côté serveur.

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;
        }
    }
}

Remarque: Pour les boutons de texte, vous devez préfixer le nom avec Submit_. Je préfère cette façon parce que cela signifie que vous pouvez changer la valeur du texte (affichage) sans avoir à changer le code. Contrairement aux éléments SELECT, un bouton INPUT a uniquement un attribut 'valeur' ​​et aucun attribut 'texte' distinct. Mes boutons disent différentes choses dans différents contextes - mais correspondent à la même "commande". Je préfère de loin extraire le nom de cette façon plutôt que d'avoir à coder pour == "Add to cart".

6
Simon_Weaver

Je n'ai pas assez de représentants pour commenter au bon endroit, mais j'ai passé toute la journée à ce sujet, alors je veux partager.

Lors de la tentative d'implémentation de la solution "MultipleButtonAttribute", ValueProvider.GetValue(keyValue) ne reviendrait pas correctement null.

Il s'est avéré que je faisais référence à la version 3.0 de System.Web.MVC alors qu'il aurait dû s'agir de 4.0 (les autres assemblys sont 4.0). Je ne sais pas pourquoi mon projet n'a pas été mis à niveau correctement et je n'ai pas eu d'autres problèmes évidents.

Donc, si votre ActionNameSelectorAttribute ne fonctionne pas ... vérifiez-le.

6
HeatherD

Je suis assez en retard pour la fête, mais voilà ... Mon implémentation emprunte à @mkozicki mais nécessite moins de chaînes codées en dur pour se tromper. Framework 4.5+ requis . Essentiellement, le nom de la méthode du contrôleur devrait être la clé du routage.

Balisage . Le nom du bouton doit être saisi avec "action:[controllerMethodName]"

(notez l'utilisation de l'API C # 6 nameof , en fournissant une référence spécifique au type du nom de la méthode de contrôleur que vous souhaitez appeler.

<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>

Contrôleur :

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();
        }
    }
}

Attribut Magic . Notez l'utilisation de 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;
    }
}
6
Aaron Hudon

c'est le meilleur moyen que j'ai trouvé:

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

Voici le code:

    /// <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);
           }
       }
   }

Profitez d'un bouton de soumission multiple sans code ni odeur.

merci

5
user156888
//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();
        }
5
SHAURAJ SINGH

J'ai essayé de faire une synthèse de toutes les solutions et de créer un attribut [ButtenHandler] facilitant la gestion de plusieurs boutons dans un formulaire.

Je l'ai décrit dans CodeProject boutons de formulaire multiples paramétrés (localisables) dans ASP.NET MVC .

Pour gérer le cas simple de ce bouton:

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

Vous aurez quelque chose comme la méthode d'action suivante:

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

Notez que le nom du bouton correspond au nom de la méthode d'action. L'article explique également comment avoir des boutons avec des valeurs et des boutons avec des index.

5
codetuner

Version modifiée de la méthode HttpParamActionAttribute mais avec un correctif pour ne pas causer d'erreur aux publications postées de session expirées/non valides. Pour savoir si cela pose un problème avec votre site actuel, ouvrez le formulaire dans une fenêtre et, juste avant de cliquer sur Save ou Publish, ouvrez une fenêtre en double et déconnectez-vous. Revenez maintenant à votre première fenêtre et essayez de soumettre votre formulaire en utilisant l'un ou l'autre des boutons. Pour moi, j'ai eu une erreur, donc ce changement résout ce problème pour moi. J'omets un tas de trucs pour des raisons de brièveté, mais vous devriez avoir l'idée. Les parties clés sont l’inclusion de ActionName sur l’attribut et l’assurance que le nom transmis est celui de la vue qui affiche le formulaire.

Classe d'attribut

[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;
    }
}

contrôleur

[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");
}

Voir

@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" />
}
4
Nick Albrecht

Mon approche JQuery utilisant une méthode d'extension:

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);
}

Vous pouvez l'utiliser comme ceci:

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

Et ça rend comme ça:

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

Basé sur la réponse de mkozicki, je propose une solution légèrement différente. J'utilise toujours ActionNameSelectorAttribute Mais je devais gérer deux boutons 'Save' et 'Sync'. Ils font presque la même chose alors je ne voulais pas avoir deux actions.

attribut:

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;
    }
}

vue

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

contrôleur

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

Je tiens également à souligner que si les actions sont différentes, je suivrais probablement post mkozicki.

3
Petr J

Pour chaque bouton d'envoi, ajoutez simplement:

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

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

});
3
mishap
[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>

    }
3
Mahendra

J'ai créé une méthode ActionButton pour le HtmlHelper. Il générera un bouton d’entrée normal avec un peu de javascript dans le événement OnClick qui soumettra le formulaire au Controller/Action spécifié.

Vous utilisez l'aide comme ça

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

cela générera le code HTML suivant

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

Voici le code de la méthode d'extension:

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 # (le code C # est juste décompilé à partir de la DLL VB, il peut donc être embelli, mais le temps est si court :-))

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));
}

Ces méthodes ont divers paramètres, mais pour la facilité d'utilisation, vous pouvez créer une surcharge prenant uniquement les paramètres dont vous avez besoin.

1
Max

Lorsque vous utilisez des formulaires ajax, nous pouvons utiliser ActionLinks avec POST HttpMethod et sérialiser le formulaire dans l'événement AjaxOptions.OnBegin.

Disons que vous avez deux actions, InsertAction et UpdateAction:

<form>
    @Html.Hidden("SomeField", "SomeValue")

    @Ajax.ActionLink(
        "Insert",
        "InsertAction",
        null,
        new AjaxOptions { 
            OnBegin = "OnBegin", 
            UpdateTargetId = "yourDiv", 
            HttpMethod = "POST" })
    @Ajax.ActionLink(
        "Update",
        "UpdateAction",
        null,
        new AjaxOptions { 
            OnBegin = "OnBegin", 
            UpdateTargetId = "yourDiv", 
            HttpMethod = "POST" })
</form>

Javascript

function OnBegin(xhr, settings) {
    settings.data = $("form").serialize();
}
0
jBelanger

Utilisez un assistant personnalisé (créez un fichier "Helpers.cshtml" dans le dossier App_Code, à la racine de votre projet) avec javascript pour réécrire (lors d'un événement "onclick") l'attribut "action" du formulaire en un élément de votre choix, puis envoyez-le. il.

L'assistant pourrait être comme:

@helper SubmitButton(string text, string controller,string action)
{   
    var uh = new System.Web.Mvc.UrlHelper(Context.Request.RequestContext);
    string url = @uh.Action(action, controller, null);   
    <input type=button  onclick="(
                                       function(e)
                                                 {
                                                   $(e).parent().attr('action', '@url'); //rewrite action url
                                                   //create a submit button to be clicked and removed, so that onsubmit is triggered
                                                   var form = document.getElementById($(e).parent().attr('id'));
                                                   var button = form.ownerDocument.createElement('input');
                                                   button.style.display = 'none';
                                                   button.type = 'submit';
                                                   form.appendChild(button).click(); 
                                                   form.removeChild(button);              
                                                  }
                                      )(this)" value="@text"/>
}

Et puis utilisez-le comme:

@Helpers.SubmitButton("Text for 1st button","ControllerForButton1","ActionForButton1")
@Helpers.SubmitButton("Text for 2nd button","ControllerForButton2","ActionForButton2")
...

Dans votre formulaire.

0
galmeida