html ¿Cómo maneja los múltiples botones de envío en ASP.NET MVC Framework?





15 Answers

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

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

publicar 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 trabajar con sitios localizados, aísle sus mensajes en otro lugar (por ejemplo, compilando un archivo de recursos en una clase de recursos fuertemente tipada)

Luego modifica 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 su controlador debe tener este aspecto:

// 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());
}
html asp.net asp.net-mvc http-post form-submit

¿Hay alguna manera fácil de manejar múltiples botones de envío desde el mismo formulario? 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 he buscado en Google tienen botones individuales en ellos.




Eilon sugiere que puedes hacerlo así:

Si tiene más de un botón, puede distinguirlos al asignar 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 su método de acción del controlador puede agregar parámetros nombrados después de los nombres de las etiquetas de entrada HTML:

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

Si algún valor se publica en uno de esos parámetros, eso significa que ese botón fue el que se hizo clic. El navegador web solo publicará un valor para el botón 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, ya que no se basa en la propiedad de valor de los botones de envío, que es más probable que cambie que los nombres asignados y que no requiera la activación de JavaScript.

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




Sugeriría a las partes interesadas echar un vistazo a la solución de Maarten Balliauw . Creo que es muy elegante.

En caso de que el enlace desaparezca, está utilizando el atributo MultiButton aplicado a una acción del controlador para indicar qué botón debe hacer clic en esa acción.




Debes poder nombrar los botones y darles un valor; luego mapee este nombre como un argumento a la acción. Alternativamente, use 2 enlaces de acción separados o 2 formularios.




Algo que no me gusta de ActionSelectName es que se llama a IsValidName para cada método de acción en el controlador; No sé por qué funciona de esta manera. Me gusta una solución donde cada botón tiene un nombre diferente según lo que hace, pero no me gusta el hecho de que tenga que tener tantos parámetros en el método de acción como botones en el formulario. He creado una enumeración para todos los tipos de botones:

public enum ButtonType
{
    Submit,
    Cancel,
    Delete
}

En lugar de ActionSelectName, uso 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;
            }
        }
    }
}

El filtro encontrará el nombre del botón en los datos del formulario y, si el nombre del botón coincide con alguno de los tipos de botones definidos en la enumeración, encontrará el parámetro ButtonType entre los parámetros de acción:

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

y luego en vistas, puedo usar:

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



Si no tiene restricciones en el uso de HTML 5, puede usar la etiqueta <button> con el atributo de formaction :

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

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




David Findley escribe sobre 3 opciones diferentes que tiene para hacer esto, en su weblog ASP.Net.

Lea los múltiples botones del artículo en la misma forma para ver sus soluciones, y las ventajas y desventajas de cada uno. En mi humilde opinión, proporciona una solución muy elegante que utiliza atributos con los que decoras tu acción.




Este script permite especificar un atributo de acción de formulario de datos que funcionará como el atributo de forma HTML5 en todos los navegadores (de manera discreta):

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

El formulario que contiene el botón se publicará en la URL especificada en el atributo data-form-action:

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

Esto requiere jQuery 1.7. Para versiones anteriores, debe usar live() lugar de on() .




Hay tres formas en que puede resolver el problema anterior

  1. Forma HTML
  2. Forma jquery
  3. "ActionNameSelectorAttribute" forma

A continuación se muestra un video que resume los tres enfoques de manera demostrativa.

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

Forma HTML: -

En la forma HTML, necesitamos crear dos formularios y colocar el botón "Enviar" dentro de cada uno de los formularios. Y la acción de cada forma apuntará a acciones diferentes / respectivas. Puede ver el siguiente código: el primer formulario se publica en "Action1" y el segundo se publicará en "Action2" según el botón "Enviar".

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

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

Ajax forma: -

En caso de que seas un amante del Ajax, esta segunda opción te excitará más. A la manera de Ajax podemos crear dos funciones diferentes "Fun1" y "Fun1", consulte el siguiente código. Estas funciones realizarán llamadas a Ajax utilizando JQUERY o cualquier otro framework. Cada una de estas funciones se enlaza con los eventos "OnClick" del botón "Enviar". Cada una de estas funciones llama a los respectivos nombres de acción.

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

Utilizando "ActionNameSelectorAttribute": -

Esta es una gran y una opción limpia. El "ActionNameSelectorAttribute" es una clase de atributo simple donde podemos escribir la lógica de toma de decisiones que decidirá qué acción se puede ejecutar.

Entonces, lo primero que hay en HTML es que debemos poner un nombre adecuado en los botones de envío para identificarlos en el servidor.

Puede ver que hemos puesto "Guardar" y "Eliminar" en los nombres de los botones. También puede observar en la acción que acabamos de poner el nombre del controlador "Cliente" y no un nombre de acción en particular. Esperamos que el nombre de la acción se decida por "ActionNameSelectorAttribute".

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

Entonces, cuando se hace clic en el botón de envío, primero golpea el atributo "ActionNameSelector" y luego, dependiendo de qué envío se activa, se invoca la acción apropiada.

Entonces, el primer paso es crear una clase que herede de la clase "ActionNameSelectorAttribute". En esta clase hemos creado una propiedad simple "Nombre".

También debemos anular la función "IsValidName" que devuelve true o flase. Esta función es donde escribimos la lógica si una acción tiene que ser ejecutada o no. Entonces, si esta función devuelve verdadero, entonces la acción se ejecuta o no lo es.

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;

        }
    }

El corazón principal de la función anterior se encuentra en el siguiente código. La colección "ValueProvider" tiene todos los datos que se han publicado desde el formulario. Por lo tanto, primero busca el valor de "Nombre" y, si se encuentra en la solicitud HTTP, devuelve verdadero o, si no, devuelve falso.

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

Esta clase de atributo puede decorarse en la acción respectiva y se puede proporcionar el valor de "Nombre" respectivo. Por lo tanto, si el envío está realizando esta acción y si el nombre coincide con el nombre del botón de envío HTML, se ejecutará la acción o 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");
        }
}



No tengo suficiente representante para comentar en el lugar correcto, pero me pasé todo el día en esto, así que quiero compartir.

Al intentar implementar la solución "MultipleButtonAttribute", ValueProvider.GetValue(keyValue) volvería incorrectamente a null .

Resultó que estaba haciendo referencia a System.Web.MVC versión 3.0 cuando debería haber sido 4.0 (otros ensamblajes son 4.0). No sé por qué mi proyecto no se actualizó correctamente y no tuve otros problemas obvios.

Entonces, si su ActionNameSelectorAttribute no está funcionando ... verifique eso.




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



Llego tarde a la fiesta, pero aquí va ... Mi implementación toma prestada de @mkozicki pero requiere menos cadenas codificadas para equivocarse. Marco 4.5+ requerido . Esencialmente, el nombre del método del controlador debe ser la clave para el enrutamiento.

De marcado . El nombre del botón debe estar escrito con"action:[controllerMethodName]"

(observe el uso del nameof API de C # 6 , que proporciona una referencia específica del tipo al nombre del método del controlador que desea invocar).

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

Controlador :

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

Atributo de la magia . Note el uso de la CallerMemberNamebondad.

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



Mi enfoque JQuery utilizando un método de extensión:

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

Puedes usarlo así:

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

Y se hace así:

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



Basado en la respuesta de mkozicki, se me ocurre una solución un poco diferente. Todavía utilizo ActionNameSelectorAttributePero necesitaba manejar dos botones 'Guardar' y 'Sincronizar'. Hacen casi lo mismo, así que no quería tener dos acciones.

atributo :

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

ver

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

controlador

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

También quiero señalar que si las acciones hacen cosas diferentes, probablemente seguiría la publicación de mkozicki.




He creado un método ActionButton para el HtmlHelper . Generará un botón de entrada normal con un poco de javascript en el evento OnClick que enviará el formulario al Controlador / Acción especificado.

Usas el ayudante asi

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

esto generará el siguiente HTML

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

Aquí está el código del método de extensión:

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 # (el código C # solo se ha descompilado de la DLL de VB, por lo que se puede embellecer ... pero el tiempo es muy corto :-)

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

Estos métodos tienen varios parámetros, pero para la facilidad de uso, puede crear una sobrecarga que tome solo los parámetros que necesita.




Related