I am attempting to render a partial view that contains a simple html form. I want to render the form from a controller as I handle the postback from an overloaded controller method. I have tried #Html.Action("ContactForm")but I get an Exception because child actions cannot redirect.
My Controller:
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult ContactForm()
{
return PartialView(new ContactForm());
}
[HttpPost]
public ActionResult ContactForm(ContactForm Contact)
{
return RedirectToAction("FormResults", new { ContactForm = Contact });
}
public ActionResult FormResults(ContactForm Contact)
{
return PartialView(Contact);
}
My Form:
#using(Html.BeginForm())
{
<h2>Contact Form</h2>
<div class="input-group">
<h4>#Html.LabelFor(m => m.FirstName, "First Name")</h4>
#Html.TextBoxFor(m => m.FirstName, new { #class = "form-control", #placeholder = "First Name" })
</div>
<div class="input-group">
<h4>#Html.LabelFor(m => m.LastName, "Last Name")</h4>
#Html.TextBoxFor(m => m.LastName, new { #class = "form-control", #placeholder = "Last Name" })
</div>
<div class="input-group">
<h4>#Html.LabelFor(m => m.Email, "Email")</h4>
#Html.TextBoxFor(m => m.Email, new { #class = "form-control", #placeholder = "Email", #type = "text" })
</div>
<input type="submit" class="btn btn-info" value="Submit" />
}
Any Help on how I would accomplish this would be appreciated.
try surrounding the form with a div and a certain id and use:
#using(Ajax.BeginForm("ContactForm","YourController",new AjaxOptions()
{
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "yourCertainId",
HTTPMethod = "POST"
})
and your ActionMethod:
[HttpPost]
public ActionResult ContactForm(ContactForm Contact)
{
return Partial("YourPartialName", Contact });
}
make sure that you include the bundle jqueryval on the bottom of your view.
you wont need the second controller method "FormResults"
Does something like this not work for you?
I don't think you need a redirect.
[HttpPost]
public ActionResult ContactForm(ContactForm Contact)
{
return PartialView("FormResults", Contact);
}
This uses the
PartialView(string viewName, object model)
overload of the PartialView method in the Controller class.
This allows you to use a View that doesn't match the ActionResult's method name.
The same thing works for the plain "View" method as well.
Related
I can´t get into the HttpPost part of the controller.
I´ve created another controller and the problem is the same
I was working fine before
I´ve added tags [Route("CrearSolicitud")]
[HttpPost, ActionName("CrearSolicitud")]
I´ve haven´t added anything to the routing part
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
It goes to http://localhost:55935/Solicitudes
instead of http://localhost:55935/Solicitudes/CrearSolicitud
public class SolicitudesController : Controller
{
ApplicationDbContext db = new ApplicationDbContext();
// GET: Solicitudes
public ActionResult Index()
{
return View();
}
//Get
public ActionResult Solicitud()
{
return View();
}
//Post
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Solicitud(Solicitudes s)
{
s.Id = "e17cba68-0a0b-4d6e-abaf-8026cb91fcd1";
s.fk_tipo_transaccion = 3;
s.fk_estado_solicitud = 1;
db.Solicitudes.Add(s);
db.SaveChanges();
return View();
}
The view
#model HGRecursosHumanos4.Models.Solicitudes
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (#Html.BeginForm("CrearSolicitud", "Solicitudes", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="container">
<div class="form-group col-md-5">
#Html.LabelFor(model => model.VacacionesDias, htmlAttributes: new { #class = "control-label col-md-4" })
<div class="col-md-3">
#Html.EditorFor(model => model.VacacionesDias, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.VacacionesDias, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group col-md-5">
#Html.LabelFor(model => model.FechaInicio, htmlAttributes: new { #class = "control-label col-md-3" })
<div class="col-md-5">
<input type="date" id="fechaInicio" name="fechaInicio" class="Filtros form-control input-sm" />
</div>
</div>
<div class="form-group col-md-2">
<div class="col-md-offset-2 col-md-3">
<input type="submit" value="Solicitar" class="btn btn-success" />
</div>
</div>
</div>
</div>
}
When I use the form and hit the submit button or the application goes to Index or to the same view again
It´s like EF went broke or something
I´ve added that, an error comes out The 'CrearSolicitud' view or its master view is not found or there is no search engine that supports the search locations. We searched in the following locations:
~/Views/Solicitudes/CrearSolicitud.aspx
~/Views/Solicitudes/CrearSolicitud.ascx
~/Views/Shared/CrearSolicitud.aspx
~/Views/Shared/CrearSolicitud.ascx
~/Views/Solicitudes/CrearSolicitud.cshtml
~/Views/Solicitudes/CrearSolicitud.vbhtml
~/Views/Shared/CrearSolicitud.cshtml
~/Views/Shared/CrearSolicitud.vbhtml
#Nemanja I certainly don´t have that view, if I use the same name on the post and no the get, the application goes to the get part again
I have the solution.
The thing is, index is for showing a list of added registries, when it is changed for something else you can´t see that list and that leads you to wrong conclusions.
The routing is working fine, the program is doing what is supposed to do, the RedirectToAction("Index") is ok.
The lesson is, take a while and think about what are you changing and try to follow the VS way of doing things.
I´ve tested now and it´s working
ApplicationDbContext db = new ApplicationDbContext();
// GET: Solicitudes
public ActionResult Index()
{
return View();
}
public ActionResult Index2()
{
return View(db.Solicitudes.ToList());
}
//Get
public ActionResult Solicitud()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Solicitud(Solicitudes s)
{
try
{
if (ModelState.IsValid)
{
s.FechaFinal = DateTime.Now;
s.FechaYHoraSolicitud = DateTime.Now;
s.Id = "e17cba68-0a0b-4d6e-abaf-8026cb91fcd1";
s.fk_tipo_transaccion = 3;
s.fk_estado_solicitud = 1;
db.Solicitudes.Add(s);
db.SaveChanges();
ViewBag.Message = "Solicitud guardada";
ModelState.Clear();
return RedirectToAction("Index2");
}
return View("ModelStateError");
}
//catch
catch (Exception ex)
{
//throw ex;
Console.WriteLine(ex.Message);
return View("Error");
}
}
I am displaying a dropdown list from my controller as follows:
public ActionResult Index()
{
var title = new List<String>()
{
"English", "French", "Spanish"
};
List<SelectListItem> languageList = new List<SelectListItem>();
string defaultTitle = "Language";
foreach (var item in title)
{
languageList.Add(new SelectListItem()
{
Text = item,
Value = item,
Selected = (item == defaultTitle ? true : false)
});
}
ViewBag.LanguageList = languageList;
return View();
}
My View is as follows:
#using (Html.BeginForm("GetStatusTrad", "StatusTradController", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Translation</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.Label("Language")
#Html.DropDownList("lang", new SelectList(ViewBag.LanguageList, "Text", "Value"), "Language")
</div>
</div>
<div></div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
}
It displays the dropdown correctly, but when I want to pass the selected value to an action of the controller, I get a 404 error.
My action is :
public ActionResult GetStatusTrad(string language)
{
*some code*
}
Any idea why I can't pass the selected value of the dropdown to the controller?
Your helper should be:
#Html.DropDownList("language", <-- note this
new SelectList(ViewBag.LanguageList, "Text", "Value"), "Language")
It happend becose basically your helper will be rendered to input like this:
<select name="language">
...
</select>
And on form POST your controller will be able to bind your values based on name property of input.
#Html.DropDownList("lang", new SelectList(... generates a <select> with name="lang. You need to change the parameter in the POST method to match
public ActionResult GetStatusTrad(string lang)
As a side note, LanguageList is already IEnumerable<SelectListItem> so using new SelectList() to create another identical one is pointless. I can be just
#Html.DropDownList("lang", (IEnumerable<SelectListItem>)ViewBag.LanguageList, "Language")
You also have a typo in the BeginForm() method. It needs to be
#using (Html.BeginForm("GetStatusTrad", "StatusTrad", FormMethod.Post))
i.e. "StatusTrad", not "StatusTradController" (unless you have really named your controller StatusTradControllerController)
you can use strongly type view to return selected dropdown value.
create simple class like below
public class myModel
{
public string language { get; set; }
....
....
}
Then use this class/model in View
#model yourProject.Models.myModel
<div class="form-group">
<label class="col-lg-2 control-label">Language</label>
<div class="col-lg-5">
#Html.ValidationMessageFor(m => m.language)
#Html.DropDownListFor(m => m.language, new SelectList(ViewBag.LanguageList, "Text", "Value"), "-- Select --", new { #class = "form-control" })
</div>
</div>
Controller method look like below
[HttpPost]
public ActionResult GetStatusTrad(myModel model)
{
*some code*
}
I have the following view:
#model Microsoft.AspNet.Identity.EntityFramework.IdentityRole
#{
ViewBag.Title = Resources.Edit;
}
<h2>#Resources.EditRole</h2>
#Html.ActionLink(Resources.ListRoles, "Index") | #Html.ActionLink(Resources.ManageUserRoles, "ManageUserRoles")
<hr />
<div class="row">
<div class="col-md-8">
<section id="editRoleForm">
#using (Html.BeginForm("Edit", "Role", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<h4>#Resources.Role</h4>
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Id)
<div class="form-group">
#Html.LabelFor(m => m.Name, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Name, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="#Resources.Save" class="btn btn-default" />
</div>
</div>
}
</section>
</div>
</div>
I also have the following two methods in my RoleController:
//
// GET: /Role/Edit/5
public ActionResult Edit(string Role)
{
var thisRole = context.Roles.Where(r => r.Name.Equals(Role, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();
return View(thisRole);
}
//
// POST: /Role/Edit/5
[HttpPost]
public ActionResult Edit(FormCollection collection)
{
try
{
var thisRole = context.Roles.Where(r => r.Id.Equals(collection["Id"])).FirstOrDefault();
thisRole.Name = collection["Name"];
context.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
Originally, I was trying to use this method instead of the second one:
//
// POST: /Role/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(IdentityRole Name)
{
try
{
context.Entry(Name).State = System.Data.Entity.EntityState.Modified;
context.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
But I never got it to work because the Name parameter was always null -which I still don't know why it happened, so if someone can explain it to me that'll be greatly appreciated.
I wrote then the other method since I saw the use of FormCollection in another example (to create roles) and it seems to work fine, at least it contains the information I need when I debug it. My issue is that although collection["id"] has the right Id for the Role I'm trying to edit, context.Roles is completely empty. This makes no sense to me given that when the first method is called (loading the View for the first time), this line
var thisRole = context.Roles.Where(r => r.Name.Equals(Role, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();
returns the selected role (out of the several that exist and that I can see when I add to watch context.Roles). However, after the view is loaded, the textbox edited and the second method in the controller gets called, context.Roles has nothing in it. Why?
Ok you can use the already built in [Authorize(Roles="RoleType")] filter.
You then have your regular User model and Account controller so you can then authorize users. Once you have authorised them you can set them to a specific role.
E.g. user story: only admins can access action result x
[Authorize(User="Admin")]
Public ActionResult X(){
...
}
That way you simply assign user roles in the model creation.
E.g.
Public Class UserModel
{
int id {get;set;}
string name {get;set;}
string Role {get;set;}
.....
}
Now only users that have been authorised AND are of Role type "Admin" will be able to access that controller.
If you want to edit their role you can do a simple edit user action method
e.g.
[Post]
public actionresult edituser(int? id)
{
if (id == null)
{
return HttpNotFound();
}
using (var db = new UserContext())
{
UserModel editUser = db.UserModel.Find(id);
if (editUser == null)
{
return HttpNotFound();
}
db.User(editModel).State = System.Data.Entity.EntityState.Modified;
db.SaveChanges();
}
RedirectToAction("Action", "Controller");
}
Now any user that is NOT of Role type "Admin" will not be able to access that screen. They will receive a 404 error.
This is actually two questions in one.
First question is about the following:
<div class="form-group">
#Html.LabelFor(model => model.xxx, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.xxx , new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.xxx, "", new { #class = "text-danger" })
</div>
</div>
I don't understand what model => model.xxx means, I know what it's doing but i don't know how to interpret the syntax.
the second question is, if I have - for example -
foreach (var item in Model)
how can I replace
#Html.EditorFor(model => model.xxx , new { htmlAttributes = new { #class = "form-control" } })
with
#Html.EditorFor(item.stringProperty , new { htmlAttributes = new { #class = "form-control" } })
when I try this, it gives me errors, is there an overloaded EditorFor helper that accepts this?
thank you!
One view can has 0 or 1 Model, which's sending from controller.
public class Person
{
public string Name {get;set;}
public int Age {get;set;}
}
public ViewResult Index()
{
Person p = new Person() { Name = "P1", Age = 100};
return View(p);//
}
if your View's name "Index" then you can use second way for View, which contain 2 parameters:
ViewName and model
return View("Index", model: p);
then in your View you can use the model, if it has been implemented that:
#model Person//and remember about namespace
#
{
...
}
#using(Html.BeginForm("ActionName", "controllerName", FormMethod.Post))
{
#Html.EditorFor(model => model.Name); // it create input, check in F12 in your browse - then you can exactly understand.
}
if you want create Editor for item you must use:
#Html.TextBox("YourName")
for example:
#using(Html.BeginForm("Action", "controller")
{
#Html.TextBox("YourName")
<input type="submit" value="ok"/>
}
and in your controllerController:
public ViewResult Action(string YourName)
{
//here you got value for string YourName
return View();
}
and helpfully answer's here:
ASP.NET MVC get textbox input value
Edit, answer about exactly problem (which containt in comment below question):
I have a list, and I want to display an input text box for each item in the list, but I want each text box to have text inside when its created, text from each item in the list (coming from the item's property)
#foreach(var item in Model)
#using(Html.BeginForm("MyMethod", "Controller"))
{
#Html.TextBox("item", item)
<input type="submit" value="ok" />
}
and in your controller add MyMethod:
[HttpPost]
public ViewResult MyMethod(string item)
{
...
}
or
[HttpPost]
public ViewResult MyMethod(int item) //if it's an int
{
...
}
and if you want to have a better security page please read about Html.AntiForgeryToken:
http://msdn.microsoft.com/en-us/library/dd470175(v=vs.118).aspx
#using(Html...())
{
#Html.AntiForgeryToken()
(...)
}
I see you already got the answer of your first question.
For the second one, i think
#Html.EditorFor(item => item.stringProperty , new { htmlAttributes = new { #class = "form-control" } })
will work fine
I have an form with following url:
CreateEntity?officeCodeId=5
When I send form to validate and if validation is fail it returns just CreateEntity url. No officeCodeId=5.
if user click enter on URL or F5 - my site fail - it require missing officecodeId param. I can save it to the session or in the other storage. But I want to have it in the URL
My view:
[HttpGet]
public virtual ActionResult CreateEntity(int? officeCodeId)
{
var model = new CreateViewModel();
FillViewModel(model, officeCodeId);
return View("Create", model);
}
[HttpPost]
protected virtual ActionResult CreateEntity(TEditViewModel model)
{
if (ModelState.IsValid)
{
//Do some model stuff if
}
return View("Create", model);
}
EDIT.
My View:
using (Html.BeginForm("CreateEntity", "Employee", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.HiddenFor(x => x.OfficeCodeId)
<div>
#Html.LabelFor(model => model.FirstName, CommonRes.FirstNameCol)
#Html.TextBoxFor(model => model.FirstName, Model.FirstName)
#Html.ValidationMessageFor(model => model.FirstName)
</div>
<div>
#Html.LabelFor(model => model.LastName, CommonRes.LastNameCol)
#Html.TextBoxFor(model => model.LastName, Model.LastName)
#Html.ValidationMessageFor(model => model.LastName)
</div>
<div> <div class="input-file-area"></div>
<input id="Agreements" type="file" name="Agreements"/>
</div>
}
Edit 2.
Adding:
#using (Html.BeginForm("CreateEntity", "Employee", FormMethod.Post, new { officeCodeId = Model.OfficeCodeId, enctype = "multipart/form-data" }))
Haven`t help.
It produce the following form:
<form action="/PhoneEmployee/CreateEntity" enctype="multipart/form-data" method="post" officecodeid="5">
Solution Is
<form action="#Url.Action("CreateEntity", "Employee")?officecodeid=#Model.OfficeCodeId" enctype="multipart/form-data" method="post">
The problem is your HttpPost action doesn't have any notion of an id parameter. If you want to support a similar URL then make the action signature support that parameter e.g.
[HttpGet]
public ActionResult CreateEntity(int? officeCodeId)
[HttpPost]
public ActionResult CreateEntity(int officeCodeId, EditViewModel model);
Your actions should looks like this:
Actions:
[HttpGet]
public virtual ActionResult CreateEntity(int? officeCodeId)
{
var model = new CreateViewModel();
FillViewModel(model, officeCodeId);
return View("Create", model);
}
[HttpPost]
public virtual ActionResult CreateEntity(ViewModel model)
{
if (model.IsValid) {
// save...
return RedirectToAction("EditEntity", newId!!!);
}
return View("Create", model);
}
Html:
#using (Html.BeginForm()) {
#Html.HiddenFieldFor(m => Model.officeCodeId)
...
}
Your officeId should be in model. And on html form you can store it in hidden field.
Your final answer is excellent and works great, although you can further enhance it to make it more generic by simply including Request.QueryString:
<form action="#Url.Action("CreateEntity", "Employee")?#(Request.QueryString)"
enctype="multipart/form-data" method="POST">
Then use the POST action:
[HttpPost]
protected virtual ActionResult CreateEntity(TEditViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}