32 votes

Asp.net mvc3 razor avec des boutons d'envoi multiples

J'utilise MVC3 Razor. J'ai deux boutons d'envoi configurés sur ma vue mais le problème que j'ai est que les deux boutons d'envoi provoquent la validation du modèle. Je veux relier les boutons d'envoi individuels à des contrôles d'entrée spécifiques pour la validation.

64voto

Rojzik Points 763

Je sais que ce sujet date de quelques mois, mais les solutions proposées ici semblent inutilement complexes et il n'y a pas encore de réponse reconnue. Si vous nommez vos entrées de la même façon mais que vous leur donnez des valeurs différentes, vous pouvez obtenir cette valeur dans votre contrôleur en incluant simplement une chaîne avec le nom de l'entrée comme variable. C'est ainsi que j'ai résolu ce problème :

Voir :

 <input type="submit" id="EnterprisePush" name="btnSubmit" value="Push" />
 <input type="submit" id="EnterprisePull" name="btnSubmit" value="Pull" />

Contrôleur :

[HttpPost]
public ActionResult EnterpriseAdmin(int id, string btnSubmit, FormCollection collection)
{
  switch (btnSubmit) {
    case "Push":
      /* Do Something here */
      break;
    case "Pull":
      /* Do Something else here */
      break;
  }

30voto

Bryan Migliorisi Points 4057

Le navigateur soumet toujours l'ensemble du formulaire, quel que soit le bouton d'envoi sur lequel vous appuyez.

La meilleure solution serait d'avoir deux boutons d'envoi avec la même valeur pour l'attribut name et des valeurs différentes pour l'attribut value attributs.

Lorsque vous soumettez le formulaire, la valeur du bouton sera également soumise. Dans votre action qui gère la soumission du formulaire, vous vérifiez la valeur du bouton et effectuez la validation correcte en fonction de celle-ci.

Dans votre formulaire, vous auriez quelque chose comme ceci :

<button type="submit" name="Command" value="command1">Do Command #1</button>
<button type="submit" name="Command" value="command2">Do Command #2</button>

Votre modèle de formulaire ressemblerait à ceci :

public class MyFormModel() {
    public string Command {get;set;}
    public string SomeOtherVal {get;set;}
}

Votre contrôleur \action ressemblerait à ceci :

public ActionResult HandleFormSubmit(MyFormModel model) {
    if (model.Command == "command1") {
        // do something
    } else if (model.Command == "command2") {
        // do something else
    }
}

21voto

Rob Kent Points 3133

Tout d'abord, vous pouvez désactiver la validation du client sur votre bouton d'annulation en lui ajoutant simplement la classe CSS "cancel". Voir : Désactiver la validation côté client dans le bouton d'envoi "cancel" de MVC 3

Deuxièmement, en plus de tester le nom du formulaire de l'élément submit comme décrit ci-dessus, vous pouvez utiliser un sélecteur d'action personnalisé. Voici le mien, que j'ai repris à l'origine de l'article de blog indiqué dans le commentaire :

/// <summary>
/// Used to vary an action method based on which button in a form was pressed. This
/// is useful but is an anti-pattern because it couples the controller to names
/// used in the form elements. 
/// </summary>
/// <remarks>
/// See the example at http://weblogs.asp.net/dfindley/archive/2009/05/31/asp-net-mvc-multiple-buttons-in-the-same-form.aspx
/// </remarks>
public class AcceptButtonAttribute : ActionMethodSelectorAttribute
{
    public string ButtonName { get; set; }

    public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
    {
        var req = controllerContext.RequestContext.HttpContext.Request;
        return !string.IsNullOrEmpty(req.Form[this.ButtonName]);
    }
}

Dans votre contrôleur :

    [HttpPost]
    [ActionName("Edit")]
    [AcceptButton(ButtonName = "Cancel")]
    public ActionResult Edit_Cancel(MyModel model)
    {
        return RedirectToAction("Index");
    }

    [HttpPost]
    [AcceptButton(ButtonName = "Save")]
    public ActionResult Edit(MyModel model)
    {
        // do real work here
    }

Notez que vous avez besoin de l'attribut [ActionName("Edit")] pour indiquer à MVC que, bien qu'utilisant un nom de méthode différent, il s'agit de l'action Edit.

Et dans votre vue :

    <input type="submit" name="Save" value="Save" />
    <input type="submit" name="Cancel" value="Cancel" class="cancel" />

3voto

Ma solution était de faire deux choses. Disons que nous avons un bouton Enregistrer et un autre Ajouter quelque chose. Lorsque l'utilisateur clique sur Enregistrer, nous voulons que la validation du client et la validation du serveur soient effectuées. Pour le bouton Ajouter quelque chose, nous ne voulons pas qu'une validation soit effectuée.

  1. Désactiver temporairement la validation du client pour le deuxième bouton (au clic) :

<input type="submit" name="submit-button" value="Save" />

<input type="submit" name="submit-button" value="Add Something" onclick="document.forms[0].noValidate = true ; document.forms[0].submit() ;" />

L'avantage est que lorsque JavaScript est désactivé, la validation du client n'aurait jamais eu lieu de toute façon.

  1. S'occuper du côté serveur

Comme Bryan le dit, lorsque vous cliquez sur un bouton d'envoi dans un formulaire, le formulaire entier et la valeur du bouton d'envoi cliqué sont affichés. Vous pouvez différencier quel bouton a été cliqué par le nom affiché. Dans l'exemple ci-dessus, lorsque l'utilisateur clique sur le bouton "Save" et que nous lisons Request.Form["submit-button"] dans le contrôleur post action, nous obtenons "Save". Si l'utilisateur a cliqué sur Add Something, nous obtenons "Add Something". C'est ainsi que le HTML est censé fonctionner.

Maintenant, pour éviter d'avoir des chaînes magiques partout, j'ai généralement une classe statique publique dans le contrôleur, comme ceci :

public class HomeController
{
    public static class Buttons
    {
        public const string Save = "Save";
        public const string AddSomething = "Add something";
    }
    // Action methods
}

Vous pouvez donc les utiliser pour rendre le formulaire :

&lt;input type="submit" name="submit-button" value="@HomeController.Buttons.Save" /&gt;

Et vous pouvez facilement lire le bouton cliqué dans le contrôleur :

[HttpPost]
public ActionResult Index(Model viewModel)
{
    var buttonClicked = Request.Form["submit-button"];
    switch (buttonClicked) {
        case HomeController.Buttons.Save:
            return Save(viewModel);
        case HomeController.Buttons.AddSomething:
            return AddSOmething(viewModel);
    }
    return View();
}

Dans la méthode Save, on demande d'abord si ModelState.IsValid et on renvoie le modèle de vue si ce n'est pas le cas, mais dans la méthode AddSomething, on efface toutes les erreurs :

public ActionResult AddSomething(Model viewModel)
{
    ModelState.Clear();
    // your code to add something to model
    return View(viewModel);
}

Cela vous a permis de garder tout propre, bien rangé et testable. Et vous pouvez introduire une constante pour l'attribut html name de submit-button. Il pourrait être possible de faire toutes les constantes avec T4MVC aussi. Une solution similaire s'applique lorsque vous avez besoin d'une boîte combo "auto postback", sauf que vous avez besoin d'un champ caché qui est défini via l'événement onchange de l'élément select.

J'espère que cela vous aidera.

2voto

Chad Brewbaker Points 1101

Il suffit d'utiliser ce code comme modèle :

@{
    var nextButtonVal = "Next >>";
    var backButtonVal = "<< Back";
    if (IsPost) {
      if(Request["navigate"].Equals(backButtonVal)){Response.Redirect("~/pageFoo");}
      if(Request["navigate"].Equals(nextButtonVal)){Response.Redirect("~/pagebar");}
    }
}

<input type="submit" value="@backButtonVal" title="Back" name="navigate"/>
<input type="submit" value="@nextButtonVal" title="Next" name="navigate"/>

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X