I have one simple form with two buttons inside. Each button has to redirect me on different view in my controller. I was looking for some examples over the net, found solutions and implemented them. Unfortunately, it isn't working for me.
Controller:
public class HomeController : Controller
{
private MovieEntities db = new MovieEntities();
[HttpGet]
public ActionResult Index()
{
return View();
}
[HttpPost]
[Button(ButtonName = "clickButton", ButtonValue = "Send")]
public ActionResult Send()
{
return View();
}
[HttpPost]
[Button(ButtonName = "clickButton", ButtonValue = "Reset")]
public ActionResult Reset()
{
return View();
}
}
Index view:
#model IEnumerable<CustomWizzardMVC.Models.MovieInfo>
#{
ViewBag.Title = "Home";
}
<h1>Insert informations</h1>
#using(Html.BeginForm())
{
<input type="button" name="clickButton" value="Send" />
<input type="button" name="clickButton" value="Reset" />
}
Send and Reset view are just simple views with <p> tags inside.
I have Button class too:
public class Button : ActionNameSelectorAttribute
{
public string ButtonName { get; set; }
public string ButtonValue { get; set; }
public override bool IsValidName(ControllerContext controllerContext, string actionName, System.Reflection.MethodInfo methodInfo)
{
return controllerContext.HttpContext.Request[ButtonName] != null && controllerContext.HttpContext.Request[ButtonName] == ButtonValue;
}
}
What am I doing wrong in here? Also, if you know some other way to do the same functionality without using jQuery, please post some code :)
You can configure a form's target by it's action attribute.
So you can do this by changing your form's action attribute. You need to use client side script to do that.
An another option, you can send a value that contains user's option (like Option = "reset" or Option = "send"). And decide what view you need to go in your default view.
Change your input type="button" to type="submit.
<input type="button" /> buttons will not submit a form - they don't do anything by default. They're generally used in conjunction with JavaScript as part of an AJAX application.
<input type="submit"> buttons will submit the form they are in when the user clicks on them, unless you specify otherwise with JavaScript.
Found how it can be done. <input type="submit" value="Send" formaction="#Url.Action("Send","Home")" /> Just found out that formaction is the new HTML5 attribute that specifies the URL of the form for the HttpPost action. :)
Related
Using ASP.NET MVC, in my view, I list some files and show them as a button. By clicking each button the corresponding file should be downloaded.
To show the list of files, I pass a model to view and when a user clicks on each of those buttons, I have to send the filename plus the original model back to the controller.
I found answers for a similar question but in my case, I don't have only one button. I have one button for each filename that I render on view.
This is the code for my view:
#using (Html.BeginForm("DownloadFile", "SharedFolder", FormMethod.Post))
{
<div class="col-sm-6">
<div class="panel panel-info">
<div class="panel-heading">Files</div>
<div class="panel-body" style="max-height:300px; height:300px; overflow-y:scroll">
#foreach (var file in Model.Files)
{
<button type="button" class="btn btn-link btn-sm" onclick="location.href='#Url.Action("DownloadFile", "SharedFolder", new { fileToDownload = file, data = Model })'">
<div class="glyphicon glyphicon-file" style="color:dodgerblue">
<span style="color:black;">#file</span>
</div>
</button>
<br />
}
</div>
</div>
</div>
}
And my controller action:
[HttpPost]
public ActionResult DownloadFile(string fileToDownload, FolderExplorerViewModel data)
{
// download the file and return Index view
return RedirectToAction("Index");
}
When I click on a file to download it, I get below error:
The resource cannot be found.
Requested URL: /SharedFolder/DownloadFile
Update:
This is my ViewModel
public class FolderExplorerViewModel
{
public int ID { get; set; }
public List<Folder> Folders { get; set; }
public List<string> Files { get; set; }
public string SelectedPath { get; set; }
}
You shouldn't store data = Model like this causes the performance and security problem.
You just store one fileToDownload value from View. After that, in Controller, You should get file by fileToDownload param.
[HttpPost]
public ActionResult DownloadFile(string fileToDownload)
{
// download the file by `fileToDownload` param here
return RedirectToAction("Index");
}
The reason why you are getting that error is because, its trying to do form post and there are no form html elements representing fileToDownload and data.
Actually, instead of using form POST you can use anchor tags invoking controller GET action (since its just download, no need to use POST here). As #Phong mentioned, using fileToDownload you probably can retrieve other information which is needed for redirection to Index.
#foreach (var file in Model.Files)
{
#file
}
and then your controller action will be:
public ActionResult DownloadFile(string fileToDownload)
{
// do the stuff
// download would return FileResult on success, not sure what you meant by RedirectToAction("Index")
// download the file and return Index view
return RedirectToAction("Index");
}
If you still wanted to send Model, then you can do that via javascript button click event with ajax.
I want to do something very simple, which is to create an HTML button that calls a controller function when clicked, the same as this HTML actionlink. This should really be remarkably easy. The action link is:
#Html.ActionLink("Submit", "Submit", "Home")
I'm using the Razer viewmodel and .NET 4.5. I've done some research, and it seems that I may have to create my own custom button. I'm fine with that, but is that really necessary? See: Mvc Html.ActionButton. It would seem like an oversight for this to have no native microsoft support, but if not, I can live with that.
Please forgive the naivety of this question - I'm new to ASP.NET, though not to C# or web development. Thanks!
I grabbed this from somewhere. but you can map view actions to controller actions with the following code.
Create a class with the following code.
[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;
}
}
In your View code you can have the following submit buttons
<input type="submit" value="Action A" name="action:ActionA" />
<input type="submit" value="Action B" name="action:ActionB" />
And your controller contains the following code.
[HttpPost]
[MultipleButton(Name="action", Argument="ActionA")]
public ActionResult MyActionA(myModel model)
{
...
}
[HttpPost]
[MultipleButton(Name = "action", Argument = "ActionB")]
public ActionResult MyActionB(myModel model)
{
...
}
so I have a Url Action
Create new teacher & assign to account.
That passes in two routeValues: createAndAssign, and teacherID.
Now when I go to my Teacher/Create page, my URL is like so:
.../Teacher/Create?createAndAssign=True&teacherID=ea817321-5633-4fdc-b388-5dba2c4a728e
Which is good, I want this. Now when I POST to create my teacher, how do I grab createAndAssign and teacherID value?
You can set the Querystring value in a hidden variables in the form and render in your GET action method and accept that in your POST action method.
View rendered by your GET Action
#using (Html.BeginForm())
{
//Other form elements also
#Html.Hidden("teacher",#Request.QueryString["teacherID"] as string)
#Html.Hidden("createAndAssign",#Request.QueryString["createAndAssign"]
as string)
<input type="submit" />
}
and now have a teacher parameter and createAndAssign parameter in your HttpPost action method so that it will be available when you submit the form.
[HttpPost]
public ActionResult Create(string teacher,string createAndAssign)
{
//Save and Redirect
}
If your view is strongly typed (which is my personal preference), it is quite easy,
public ActionResult GET(string teacherID,string createdAndAssing)
{
var yourVMObject=new YourViewModel();
yourVMObject.TeacherID=teacherID;
yourVMObject.CreateAndAssign=createdAndAssing;
return View(createdAndAssing);
}
and in your strongly typed view,
#model YourViewModel
#using (Html.BeginForm())
{
//Other form elements also
#Html.HiddenFor(x=>x.TeacherID)
#Html.HiddenFor(x=>x.CreateAndAssign)
<input type="submit" />
}
And in your POST action
[HttpPost]
public ActionResult Create(YourViewModel model)
{
//look for model.TeacherID
//Save and Redirect
}
you can get the value from the query string or as params of the controller like
var x =Request.QueryString["createAndAssign"];
or
public ActionResult Create(bool createAndAssign, string teacherID){
return View();
}
I have a view with three submit buttons. First button must validate some fields, second button must validate other fields, third button doesn't validate anything.
How can I do that on both client (unobtrusive) and server sides?
Most likely with alot of hand rolled usage, especially if you want it to be unobtrusive. You're going to need to create something like <input data-validationgroup="group1"... and then on the click action that your javascript code will then validate what you want. I would imagine jQuery Validate has some type of ruleset support but you'll have to figure that out.
You're going to have to do a similar sequence on the server side, and create ruleset type validation classes/blocks/methods that you interpret the submit action to the relevant ruleset. I'd look at a library like FluentValidation for this part.
To achieve what you want it, is is very extremely unlikely you will be able to achieve this using the DataAnnotations attributes on your model class.
Personally I have always liked and used the FluentValidation.NET library in all my projects. Not only that it is very powerful in terms of expressing validation rules but this library has an excellent integration with ASP.NET MVC. So I will try to provide a sample solution for this problem using it (only server side validation for the moment, later we can talk about unobtrusive client side validation if you want).
So start a new ASP.NET MVC 3 project using the default template and install the FluentValidation.MVC3 NuGet package (the current stable version is 2.0.0.0).
Then let's define a view model:
public class MyViewModel
{
public string Field1 { get; set; }
public string Field2 { get; set; }
}
Now we can assume that if button1 is clicked Field1 is required and if button2 is clicked Field2 is required and if button3 is clicked none of them are required. A fictional scenario but pretty close to your requirements.
Now let's define two different fluent validators for this model corresponding each to button1 and button2:
public class MyModelValidator1 : AbstractValidator<MyViewModel>
{
public MyModelValidator1()
{
RuleFor(x => x.Field1)
.NotEmpty();
}
}
public class MyModelValidator2 : AbstractValidator<MyViewModel>
{
public MyModelValidator2()
{
RuleFor(x => x.Field2)
.NotEmpty();
}
}
Now because it is only at runtime that we know which button is clicked we need to apply the correct validator based on value in the request. So let's write a custom validator provider factory:
public class MyFactory : IValidatorFactory
{
private readonly Func<HttpContextBase> _contextProvider;
public MyFactory(Func<HttpContextBase> contextProvider)
{
_contextProvider = contextProvider;
}
public IValidator GetValidator(Type type)
{
if (type == typeof(MyViewModel))
{
var context = _contextProvider();
if (!string.IsNullOrEmpty(context.Request["button1"]))
{
return new MyModelValidator1();
}
if (!string.IsNullOrEmpty(context.Request["button2"]))
{
return new MyModelValidator2();
}
}
return null;
}
public IValidator<T> GetValidator<T>()
{
return (IValidator<T>)GetValidator(typeof(T));
}
}
and register it in Application_Start:
ModelValidatorProviders.Providers.Add(
new FluentValidationModelValidatorProvider(
new MyFactory(() => new HttpContextWrapper(HttpContext.Current))
)
);
and that's pretty much all. Now what's left is trivial.
A controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel();
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
return Content("Thanks for submitting", "text/plain");
}
}
and a view:
#model MyViewModel
#using (Html.BeginForm())
{
<div>
#Html.LabelFor(x => x.Field1)
#Html.EditorFor(x => x.Field1)
#Html.ValidationMessageFor(x => x.Field1)
</div>
<div>
#Html.LabelFor(x => x.Field2)
#Html.EditorFor(x => x.Field2)
#Html.ValidationMessageFor(x => x.Field2)
</div>
<input type="submit" value="Submit with button 1" name="button1" />
<input type="submit" value="Submit with button 2" name="button2" />
<input type="submit" value="Submit with button 3" name="button3" />
}
I have a View that allows a user to enter/edit data for a new Widget. I'd like to form up that data into a json object and send it to my controller via AJAX so I can do the validation on the server without a postback.
I've got it all working, except I can't figure out how to pass the data so my controller method can accept a complex Widget type instead of individual parameters for each property.
So, if this is my object:
public class Widget
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
I'd like my controller method to look something like this:
public JsonResult Save(Widget widget)
{
...
}
Currently, my jQuery looks like this:
var formData = $("#Form1").serializeArray();
$.post("/Widget/Save",
formData,
function(result){}, "json");
My form (Form1) has an input field for each property on the Widget (Id, Name, Price). This works great, but it ultimately passes each property of the Widget as a separate parameter to my controller method.
Is there a way I could "intercept" the data, maybe using an ActionFilterAttribute, and deserialize it to a Widget object before my controller method gets called?
Thanks Jeff, that got me on the right path. The DefaultModelBinder is smart enough to do all the magic for me...my problem was in my Widget type. In my haste, my type was defined as:
public class Widget
{
public int Id;
public string Name;
public decimal Price;
}
Notice that the type has public fields instead of public properties. Once I changed those to properties, it worked. Here's the final source code that works correctly:
Widget.aspx:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true" CodeBehind="Widget.aspx.cs" Inherits="MvcAjaxApp2.Views.Home.Widget" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
<script src="../../Scripts/jquery-1.2.6.js" type="text/javascript"></script>
<script type="text/javascript">
function SaveWidget()
{
var formData = $("#Form1").serializeArray();
$.post("/Home/SaveWidget",
formData,
function(data){
alert(data.Result);
}, "json");
}
</script>
<form id="Form1">
<input type="hidden" name="widget.Id" value="1" />
<input type="text" name="widget.Name" value="my widget" />
<input type="text" name="widget.Price" value="5.43" />
<input type="button" value="Save" onclick="SaveWidget()" />
</form>
</asp:Content>
HomeController.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
namespace MvcAjaxApp2.Controllers
{
[HandleError]
public class HomeController : Controller
{
public ActionResult Index()
{
ViewData["Title"] = "Home Page";
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About()
{
ViewData["Title"] = "About Page";
return View();
}
public ActionResult Widget()
{
ViewData["Title"] = "Widget";
return View();
}
public JsonResult SaveWidget(Widget widget)
{
// Save the Widget
return Json(new { Result = String.Format("Saved widget: '{0}' for ${1}", widget.Name, widget.Price) });
}
}
public class Widget
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
}
Note that (in MrDustpan's solution) the parameter name widget in the MVC Action method must match with the prefix used in the name attribute in the ASPX file.
If this is not the case then the Action method will always receive a null object.
<input type="text" name="widget.Text" value="Hello" /> - OK
<input type="text" name="mywidget.Text" value="Hello" /> - FAILS
Phil Haack has a good blog post about model binding that might be helpful. Not 100% what you're talking about here, but I think it might give you a better overall understand about the DefaultModelBinder.
What you want to do is structure your javascript form object in the same way your backend object is structured:
{ Id : "id", Name : "name", Price : 1.0 }
Then use the toJSON plugin to convert it into the above string. You send this string to your backend and use something like the JayRock libraries to convert it to a new Widget object.