¿Cómo se manejan varios botones de envío en ASP.NET MVC Framework?

Resuelto Spoike asked hace 15 años • 35 respuestas

¿Existe alguna manera sencilla de manejar varios botones de envío desde el mismo formulario? Por ejemplo:

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

¿Alguna idea de cómo hacer esto en ASP.NET Framework Beta? Todos los ejemplos que busqué en Google tienen botones únicos.

Spoike avatar Jan 14 '09 18:01 Spoike
Aceptado

Aquí hay una solución basada en atributos, en su mayoría limpia, para el problema del botón de envío múltiple, basada en gran medida en la publicación y los comentarios de Maarten 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;
    }
}

maquinilla de afeitar:

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

y controlador:

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

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

Actualización: Razor Pages busca proporcionar la misma funcionalidad lista para usar. Para un nuevo desarrollo, puede ser preferible.

mkozicki avatar Aug 18 '2011 17:08 mkozicki

Asigne un nombre a sus botones de envío y luego inspeccione el valor enviado en su método de controlador:

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

publicando en

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

}

EDITAR:

Para ampliar este enfoque para que funcione con sitios localizados, aísle sus mensajes en otro lugar (por ejemplo, compilando un archivo de recursos en una clase de recurso fuertemente tipada)

Luego modifique el código para que funcione como:

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

y tu controlador debería verse así:

// 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());
}
Dylan Beattie avatar Jan 14 '2009 14:01 Dylan Beattie

Puede verificar el nombre en la acción como se mencionó, pero puede considerar si se trata de un buen diseño o no. Es una buena idea considerar la responsabilidad de la acción y no vincular demasiado este diseño con aspectos de la interfaz de usuario como los nombres de los botones. Así que considere usar 2 formularios y 2 acciones:

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

Además, en el caso de "Cancelar", normalmente simplemente no estás procesando el formulario y vas a una nueva URL. En este caso no es necesario enviar el formulario en absoluto y sólo necesita un enlace:

<%=Html.ActionLink("Cancel", "List", "MyController") %>
Trevor de Koekkoek avatar Jan 14 '2009 12:01 Trevor de Koekkoek

Eilon sugiere que puedes hacerlo así:

Si tiene más de un botón, puede distinguirlos dándole un nombre a cada botón:

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

En el método de acción de su controlador, puede agregar parámetros con nombres de etiquetas de entrada HTML:

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

Si se publica algún valor en uno de esos parámetros, eso significa que se hizo clic en ese botón. El navegador web solo publicará un valor para el botón en el que se hizo clic. Todos los demás valores serán nulos.

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

Me gusta este método porque no depende de la propiedad de valor de los botones de envío, que es más probable que cambie que los nombres asignados y no requiere que JavaScript esté habilitado.

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

PabloBlamirez avatar Jan 20 '2009 21:01 PabloBlamirez

Acabo de escribir una publicación sobre eso: Múltiples botones de envío con ASP.NET MVC :

Básicamente, en lugar de usar ActionMethodSelectorAttribute, estoy usando ActionNameSelectorAttribute, lo que me permite fingir que el nombre de la acción es el que quiero que sea. Afortunadamente, ActionNameSelectorAttributeno solo me obliga a especificar el nombre de la acción, sino que puedo elegir si la acción actual coincide con la solicitud.

Ahí está mi clase (por cierto, no me gusta mucho el nombre):

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

Para usarlo simplemente defina un formulario como este:

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

y controlador con dos métodos

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

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

Como puede ver, el atributo no requiere que especifique nada en absoluto. Además, los nombres de los botones se traducen directamente a los nombres de los métodos. Además (no lo he probado), estas también deberían funcionar como acciones normales, por lo que puedes publicar en cualquiera de ellas directamente.

Andrey Shchekin avatar Mar 15 '2010 19:03 Andrey Shchekin