Pregunta

¿Hay alguna manera fácil de manejar varios botones de envío de la misma forma?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 Marco Beta?Todos los ejemplos que he buscado en google para tener los botones en ellos.

¿Fue útil?

Solución

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

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: páginas Razor parece proporcionar la misma funcionalidad de fábrica. Para un nuevo desarrollo, puede ser preferible.

Otros consejos

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

publicación 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 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 su 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());
}

Puede verificar el nombre en la acción como se ha mencionado, pero puede considerar si este es un buen diseño o no. Es una buena idea considerar la responsabilidad de la acción y no acoplar demasiado este diseño a los aspectos de la interfaz de usuario, como los nombres de los botones. Entonces 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 " ;, generalmente no está procesando el formulario y va a una nueva URL. En este caso, no es necesario que envíe el formulario y solo necesita un enlace:

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

Eilon sugiere que puede hacerlo así:

  

Si tiene más de un botón,   puede distinguir entre ellos dando   cada botón tiene un nombre:

<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 usted   puede agregar parámetros nombrados después de   Nombres de etiquetas de entrada HTML:

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

Si se registra algún valor en uno de   esos parámetros, eso significa que   botón fue el que se hizo clic.   El navegador web solo publicará un valor   para el botón one 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, 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 no requiere que JavaScript esté habilitado

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

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 lo que quiero que sea. Afortunadamente, <=> no solo me hace especificar el nombre de la acción, sino que puedo elegir si la acción actual coincide con la solicitud.

Así que ahí está mi clase (por cierto no me gusta demasiado 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 usar 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. Además, el nombre de los botones se traduce directamente a los nombres de los métodos. Además (no he & # 8217; lo he intentado) también deberían funcionar como acciones normales, por lo que puede publicar en cualquiera de ellas directamente.

Sugeriría a las partes interesadas eche 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 a qué botón hacer clic esa acción debería relacionarse.

es corto y conjunto:

  

Fue respondido por Jeroen Dop

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

y hacer esto en código detrás

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

Buena suerte.

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

Podrías escribir:

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

Y luego en la página verifique si el nombre == " Enviar " o name == " Cancelar " ...

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 en función de 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 botón 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" />

Esto es lo que funciona mejor para mí:

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

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

<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

Si su navegador admite la formación de atributos para los botones de entrada (IE 10+, no estoy seguro acerca de otros navegadores), entonces lo siguiente debería funcionar:

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

}

También me encontré con este 'problema' pero encontré una solución bastante lógica al agregar el atributo name. No recuerdo haber tenido este problema en otros idiomas.

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

  • ...
  • Si un formulario contiene más de un botón de envío, solo el botón de envío activado es exitoso.
  • ...

Significa que los siguientes atributos de código value pueden cambiarse, localizarse, internacionalizarse sin la necesidad de código adicional que verifique los archivos o constantes de recursos fuertemente tipados.

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

En el extremo receptor solo necesitaría verificar si alguno de sus tipos de envío conocidos no es 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

    }

}

Hay tres formas de resolver el problema anterior

  1. forma HTML
  2. Jquery way
  3. & # 8220; ActionNameSelectorAttribute & # 8221; camino

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 & # 8220; Enviar & # 8221; botón dentro de cada uno de los formularios. Y cada forma de acción & # 8217; apuntará a acciones diferentes / respectivas. Puede ver el siguiente código que el primer formulario está publicando en & # 8220; Acción1 & # 8221; y el segundo formulario se publicará en & # 8220; Acción2 & # 8221; dependiendo de cuál & # 8220; Enviar & # 8221; se hace clic en el botón.

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

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

Ajax: -

En caso de que seas un amante del Ajax, esta segunda opción te entusiasmaría más. De la manera Ajax podemos crear dos funciones diferentes & # 8220; Fun1 & # 8221; y & # 8220; Fun1 & # 8221; , vea el siguiente código. Estas funciones harán llamadas Ajax utilizando JQUERY o cualquier otro marco. Cada una de estas funciones está ligada con & # 8220; Enviar & # 8221; botón & # 8217; s & # 8220; OnClick & # 8221; eventos. Cada una de estas funciones llama a los nombres de acción respectivos.

<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 de & # 8220; ActionNameSelectorAttribute & # 8221;: -

Esta es una excelente y limpia opción. El & # 8220; ActionNameSelectorAttribute & # 8221; 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 es HTML, necesitamos poner el nombre propio & # 8217; s a los botones de envío para identificarlos en el servidor.

Puedes ver que hemos puesto & # 8220; Guardar & # 8221; y & # 8220; Eliminar & # 8221; a los nombres de los botones. También puede notar en la acción que acabamos de poner el nombre del controlador & # 8220; Cliente & # 8221; y no un nombre de acción particular. Esperamos que el nombre de la acción se decida por & # 8220; ActionNameSelectorAttribute & # 8221 ;.

<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 enviar, primero toca el & # 8220; ActionNameSelector & # 8221; atributo y luego, dependiendo de qué envío se activa, invoca la acción apropiada.

ingrese la descripción de la imagen aquí

Entonces, el primer paso es crear una clase que herede de & # 8220; ActionNameSelectorAttribute & # 8221; clase. En esta clase hemos creado una propiedad simple & # 8220; Name & # 8221 ;.

También necesitamos anular & # 8220; IsValidName & # 8221; función que devuelve verdadero o flase. Esta función es donde escribimos la lógica de si una acción tiene que ejecutarse o no. Entonces, si esta función devuelve verdadero, 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 está en el siguiente código. El & # 8220; ValueProvider & # 8221; La recopilación contiene todos los datos que se han publicado desde el formulario. Por lo tanto, primero busca & # 8220; Nombre & # 8221; valor y si se encuentra en la solicitud HTTP devuelve verdadero o de lo contrario 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 el & # 8220 respectivo; Nombre & # 8221; Se puede proporcionar un valor. Entonces, si el envío está presionando esta acción y si el nombre coincide con el nombre del botón de envío HTML, entonces ejecuta la acción más o no lo hace.

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 escribe sobre 3 opciones diferentes que tiene para hacer esto, en su weblog ASP.Net.

Lea el artículo varios botones 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 hace uso de los atributos con los que decoras tu acción.

Esta es la técnica que usaría y todavía no la veo aquí. El enlace (publicado por Saajid Ismail ) que inspira esta solución es http://weblogs.asp.net/dfindley/archive/2009/05/31/asp-net-mvc-multiple-buttons-in-the-same-form.aspx ) Adapta la respuesta de Dylan Beattie para hacer la localización sin ningún problema.

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

En el controlador:

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

Este script permite especificar un atributo data-form-action que funcionará como el atributo de formación HTML5 en todos los navegadores (de una 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() en lugar de on().

He aquí un método de extensión escribí para manejar múltiples imágenes y/o texto de los botones.

Aquí está el código HTML para un botón de la imagen:

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

o el texto de un botón enviar :

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

Aquí es el método de extensión de llamada, el controlador con form.GetSubmitButtonName().Para las imágenes de los botones parece que para un parámetro de forma de con .x (lo que indica una imagen de botón se hizo clic) y extrae el nombre.Para regular input botones busca un nombre que comienza con la Submit_ y extrae el comando desde luego.Porque estoy abstracción de la lógica de la determinación de la 'comando' usted puede cambiar entre imagen + texto de los botones en el cliente sin necesidad de cambiar el lado del servidor de código.

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: Para el texto de los botones tiene como prefijo el nombre con Submit_.Yo prefiero esta porque significa que usted puede cambiar el texto (pantalla) valor sin tener que cambiar el código.A diferencia de SELECT elementos, una INPUT botón sólo tiene un "valor" y no por separado atributo 'text'.Los botones de mi decir cosas diferentes en contextos diferentes - pero se asigne a la misma 'comando'.Yo prefiero extraer el nombre de esta manera, que tener que escribir código para == "Add to cart".

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

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

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

Entonces, si su ActionNameSelectorAttribute no funciona ... verifíquelo.

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

Marcado . El nombre del botón debe estar tecleado con "action:[controllerMethodNamefont>"

(observe el uso del C # 6 nameof API, proporcionando una referencia específica del tipo al nombre del método de 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 mágico . Observe el uso de CallerMemberName bondad.

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

Intenté hacer una síntesis de todas las soluciones y creé un atributo [ButtenHandler] que facilita el manejo de varios botones en un formulario.

Lo describí en CodeProject Múltiple parametrizado ( localizable) botones de formulario en ASP.NET MVC .

Para manejar el caso simple de este botón:

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

Tendrá algo como el siguiente método de acción:

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

Observe cómo el nombre del botón coincide con el nombre del método de acción. El artículo también describe cómo tener botones con valores y botones con índices.

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

esta es la mejor manera que he encontrado:

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

Aquí está el código:

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

Disfrute de un botón de envío múltiple sin código de olor en el futuro.

gracias

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

    }

Versión modificada del método HttpParamActionAttribute pero con una corrección de error para no causar un error en las devoluciones de sesión caducadas / inválidas. Para ver si esto es un problema con su sitio actual, abra el formulario en una ventana y justo antes de hacer clic en Guardar o Publicar , abra una ventana duplicada y cerrar sesión. Ahora regrese a su primera ventana e intente enviar su formulario con cualquiera de los botones. En mi caso, recibí un error, por lo que este cambio me soluciona el problema. Omito un montón de cosas por razones de brevedad, pero deberías tener la idea. Las partes clave son la inclusión de ActionName en el atributo y asegurarse de que el nombre pasado sea el nombre de la Vista que muestra el formulario

Clase de atributo

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

Controlador

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

View

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

Mi enfoque JQuery usando 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 muestra así:

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

Para cada botón de enviar simplemente agregue:

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

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

});

Basado en la respuesta de mkozicki, llego a una solución un poco diferente. Todavía uso ActionNameSelectorAttribute Pero 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;
    }
}

view

<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"];

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

@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 # se acaba de descompilar de la DLL de VB, por lo que puede obtener algo de embellecimiento ... 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 facilitar su uso, puede crear una sobrecarga que tome solo los parámetros que necesita.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top