<HttpPost()> doesn't work in my view page - asp.net-mvc

I have two links say for eg. English and Spanish. When user clicks on english link it should set language to "English" in session.
The following is my aspx code
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<script type="text/javascript">
function SetLanguageSpanish() {
document.getElementById("Home_Language").value = 'Spanish';
document.getElementById("frmHome").submit();
}
function SetLanguageEnglish() {
/*var obj = document.getElementById("LangEnglish");*/
// <% Session("Language") = "English"%>;
document.getElementById("Home_Language").value = 'English';
document.getElementById("frmHome").submit();
}
</script>
<div class="vmenu">
<form id="frmHome" action="Home" method="post"">
<a id="LangEnglish" href="/" onclick="SetLanguageEnglish();" >English</a>
<a id="LangSpanish" href="/" onclick="SetLanguageSpanish();"> Spanish</a>
<h3><%= Html.Label(Model.SubTitle) %></h3>
<ul class="sbe">
<li class="sbe"><%= Model.Menu1%></li>
<li class="sbe"><%= Model.Menu2%></li>
<li class="sbe"><%= Model.Menu3%></li>
<li class="sbe"><%= Model.Menu4%></li>
<li class="sbe"><%= Model.Menu5%></li>
</ul>
<input id="Home_PageName" name="PageName" type="hidden" value="" />
<input id="Home_Language" name="Language" type="hidden" value="" />
<input id="Home_PageTitle" name="PageTitle" type="hidden" value="" />
<input id="Home_SubTitle" name="SubTitle" type="hidden" value="" />
<input id="Home_Menu1" name="Menu1" type="hidden" value="" />
<input id="Home_Menu2" name="Menu2" type="hidden" value="" />
<input id="Home_Menu3" name="Menu3" type="hidden" value="" />
<input id="Home_Menu4" name="Menu4" type="hidden" value="" />
<input id="Home_Menu5" name="Menu5" type="hidden" value="" />
</form>
</div>
</asp:Content>
and my controller class code is as follow:
Public Class HomeController
Inherits System.Web.Mvc.Controller
' GET: /Home
<OutputCache(Duration:=1800, VaryByParam:="none")>
Function Index() As ActionResult
Try
Dim oHome As New Home.Home
Dim oHomeModel As New Home.HomeVM
If (HttpContext.Session("Language") Is Nothing) Then
HttpContext.Session("Language") = "English"
End If
oHomeModel.Language = HttpContext.Session("Language").ToString()
Return View("Index", GetCaption(oHomeModel))
Catch ex As Exception
Return Nothing
End Try
End Function
<HttpPost()>
<OutputCache(Duration:=1800)>
Function Index(ByVal oHomeModel As Home.HomeVM) As ActionResult
Try
If ((oHomeModel.Language IsNot Nothing) Or (oHomeModel.Language.ToString() <> "")) Then
HttpContext.Session("Language") = oHomeModel.Language.ToString()
End If
Return View("Index", GetCaption(oHomeModel))
Catch ex As Exception
Return Nothing
End Try
End Function
For some reason when i click on any link on my view page. The Controller class get is getting fired. due to this the value in session is not getting refreshed. Can anyone suggest why this is happening?
The following is my model class.
Namespace Home
Public Class HomeVM
'The following property are used as parameter
Public Property PageName As String
Public Property Language As String
'The following are the form caption peroperty
Public Property PageTitle As String
Public Property SubTitle As String
Public Property Menu1 As String
Public Property Menu2 As String
Public Property Menu3 As String
Public Property Menu4 As String
Public Property Menu5 As String
End Class

It looks to me like you are mixing up server side and client side code.
<% Session("Language") = "English"%>;
The above line you have in JavaScript gets executed when the page first loads which prints nothing to the page. If you look at the source you should see nothing other than the semi-colon ;.
When you click the link that calls SetLanguageSpanish() there is nothing to be executed in that function and the page is directed to '/'. Have a look at the page source.
Edit
Here is some updated code which will submit a hidden input with the id "language" and a value of the specified by the function parameter.
HTML
<form id="frmHome" method="post">
English
Spanish
<input type="hidden" id="language" name="language" value="" />
</form>
JavaScript
function SetLanguage(language) {
document.getElementById("language").value = language;
document.getElementById("frmHome").submit();
}
VB.NET I am not a VB guy so double check the syntax here
<HttpPost()>
<OutputCache(Duration:=1800)>
Function Index(ByVal language As string) As ActionResult
HttpContext.Session("Language") = language
'Do stuff
Return this.View()
End Function

Related

ViewBag passes values in GET parameters in MVC

I am new to MVC.NET and I am stopped at some point while passing data from controller to view. I have two action, one is for GET and another is for POST. When I am setting ViewBag values in my POST method action, it redirects me to View but passes the values using GET in the URL hence ViewBag values are not accessible in view.
Here is snapshot of the same:
View:
<div>
<p>#ViewData["FileName"]</p>
<p>#ViewData["myName"]</p>
<p>#ViewBag.myAdd</p>
<p>#ViewBag.someData</p>
</div>
<div>
<form id="myForm" action="~/Test/Index">
<input type="text" name="myName"/>
<input type="text" name="myAdd" />
<input type="file" name="myFile"/>
<input type="submit"/>
</form>
</div>
CONTROLLER
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(HttpPostedFileBase file, FormCollection data)
{
ViewBag.FileName = Convert.ToString(file.FileName);
ViewBag.myName = Convert.ToString(data["myName"]);
ViewBag.myAdd = Convert.ToString(data["myAdd"]);
ViewBag.someData = "someData";
return View();
}
On submit of form, it redirects me to http://localhost:65077/Test/Index?myName=mYname&myAdd=MyAdddress&myFile=432f7018-d505-4b0b-8cba-505d62b5472d.png
it would be great if someone can help and explain the same to me.
thanks in advance.
Per default form-data is appended to the URL when send back to the server(GET-method). You have to change this by useing the method attribute:
<form id="myForm" action="~/Test/Index" method="post">
<input type="text" name="myName"/>
<input type="text" name="myAdd" />
<input type="file" name="myFile"/>
<input type="submit"/>
</form>

mvc form cannot create an abstract class

I have a form with 2 select box. But when I click the submit button, there's an error saying "Cannot create an abstract class". I have no idea what is going wrong here
#using (Html.BeginForm("Add", "Admin")) {
<input type="submit" value="Add" class="btn btn-warning small-text custom-btn" />
<div class="select-box">
<select name="users[]" multiple size="35">#{ Html.RenderAction("UserList", "GroupsAndUsers"); }</select>
</div>
<div class="select-box">
<p class="blue-text bold-text">Groups</p>
<select name="group" size="35">#{ Html.RenderAction("GroupList", "GroupsAndUsers"); }</select>
</div>
<input type="hidden" name="RedirectToUrl" value="~/GroupsAndUsers/AddUsers" />
}
Admin/Add
[HttpPost]
public ActionResult Add(Array userIDs, int groupID, string RedirectToUrl)
{
return Redirect(RedirectToUrl);
}
Array is an abstract class, so MVC has no idea which specific Array implementation to create when binding the parameters. Try something like:
public ActionResult Add(int[] userIDs, int groupID, string RedirectToUrl)

MVC4 Not Posting Object With Nested Recursive Lists

I'm done tearing my hair out, so I'm asking for help. I've build a short recursive HTML Helper to display a list of lists. I want to return any edits back to the controller. The child of a child keeps coming back null. When I remove the recursion and just force through a 2nd layer of a "for loop", it seems to work. Any clue? I'm about to throw in the towel on RAZOR and just learn to do all this in Jquery...
Based on my code, the problem resides here when the model is posted back (you'll see this in the comments in the post ActionResult method):
Node.Name is ok
Node.Children[0].Name is ok
Node.Children[1].Name is ok
Node.Children[1].Children = null (HERE LIES THE PROBLEM!)
CONTROLLER CODE
public ActionResult Y()
{
Node driverTree = new Node() { Name="Level1", Children = new List<Node>() };
driverTree.Children.Add(new Node() { Children = null, Name = "Level2A" });
driverTree.Children.Add(new Node() {Name ="Level2B", Children = new List<Node> {new Node{Name="Level3A", Children=null},
new Node{Name="Level3B", Children=null}}
});
return View(driverTree);
}
[HttpPost]
public ActionResult Y(Node Node)
{
//Keeps returning:
// Node.Name is ok
// Node.Children[0].Name is ok
// Node.Children[1].Name is ok
// Node.Children[1].Children = null (HERE LIES THE PROBLEM!)
int x = 5; //
return View();
}
}
public class Node
{
public string Name { get; set; }
public List<Node> Children { get; set; }
public bool IsChecked { get; set; }
}
VIEW CODE
#model JqueryUITests.Controllers.Node
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
<script src="../../Scripts/jquery-ui-1.10.3.min.js" type="text/javascript"></script>
<script src="../../Scripts/jquery-ui.unobtrusive-2.1.0.min.js" type="text/javascript"></script>
}
#helper ShowTree(List<JqueryUITests.Controllers.Node> children)
{
<ul>
#for (int i = 0; i < children.Count;i++ )
{
<li>
#Html.EditorFor(x => children[i].Name)
#if (children[i].Children != null)
{
#ShowTree(children[i].Children)
}
</li>
}
</ul>
}
#using (Html.BeginForm())
{
<ul id="tree">
<li>
#Html.EditorFor(x=>Model.Name)
#ShowTree(Model.Children)
</li>
</ul>
<p>
<input type="submit" value="Create" />
</p>
}
HTML CODE
<form action="/X/Y" method="post"> <ul id="tree">
<li>
<input class="text-box single-line" id="Name" name="Name" type="text" value="Level1" />
<ul>
<li>
<input class="text-box single-line" id="children_0__Name" name="children[0].Name" type="text" value="Level2A" />
</li>
<li>
<input class="text-box single-line" id="children_1__Name" name="children[1].Name" type="text" value="Level2B" />
<ul>
<li>
<input class="text-box single-line" id="children_0__Name" name="children[0].Name" type="text" value="Level3A" />
</li>
<li>
<input class="text-box single-line" id="children_1__Name" name="children[1].Name" type="text" value="Level3B" />
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>
<input type="submit" value="Create" />
</p>
The list is rendering an item for each node, but it is not rendering the name attribute for each node properly.
Razor handles lists by using the name attribute to create arrays when the inputs are posted back and the model re-created. Eg
List<Foo> AllMyFoos { new Foo { Id = 1 }, new Foo { Id = 2 }, new Foo { Id = 3 }}
Would be rendered (as per your example above) like this:
<input type="text" name="AllMyFoos[0].Id" />
<input type="text" name="AllMyFoos[1].Id" />
<input type="text" name="AllMyFoos[2].Id" />
Under the hood the model binding in Razor and MVC then recreates the list of Foo objects and passes it to your controller method.
The issue with your recursive method is that when you call it with the list of child nodes, the index of each child node is being rendered and you are losing the info that is defining it as a child of another node.
Does that make sense?
Have a look at this SO question and read the answer about display templates for collection types - it's a better way of achieving what you're after.
SO Display Templates Answer
Problem is clear:
#Html.EditorFor(x => children[i].Name)
Normally x represents model and then properties are of format x.children[i].Name. Next level would be x.children[i].children[j].Name. EditorFor derives id and name of <input /> field from that expression. In your case expression is always children[i].Name, so it breaks mapping of id/name relative to your root Model.
I'm not sure if there is a good way to make recursive rendering like you want. Perhaps using non-lambda version Html.Editor(string expression), where you would construct expression as a string, taking care of nesting level in your code (ex: #Html.Editor("Children[1].Children[2].Name")).
You would be responsible to generate proper value "Children[1].Children[2].Name".
Expanding on dav83's remark about a display template. It should look something like this.
File
Views\DisplayTemplates\Node.cshtml
#model JqueryUITests.Controllers.Node
<li>
#Html.EditorFor(x => x.Name)
#if (Model.Children != null)
{
<ul>
#Html.Editor(x => x.Children)
</ul>
}
</li>
Now in your view put.
#model JqueryUITests.Controllers.Node
#section Scripts
{
#Scripts.Render("~/bundles/jqueryval")
<script src="../../Scripts/jquery-ui-1.10.3.min.js" type="text/javascript"></script>
<script src="../../Scripts/jquery-ui.unobtrusive-2.1.0.min.js" type="text/javascript"></script>
}
#using (Html.BeginForm())
{
<ul id="tree">
#Html.EditorForModel()
</ul>
<p>
<input type="submit" value="Create" />
</p>
}
MVC will render the HTML using the Node DisplayTemplate for your model. Additionally, when you call #Html.EditorFor(x => x.Children), it will use the display template to render each item in the collection. This is where you get your recursion to build the entire tree.
Additionally, it will keep track of where it is in the tree and name the HTML elements correctly, allowing your tree to post as you would expect.

Accepting params or raw data in controller?

I was wondering if it would be possible having a "params" argument in a controller function, or something similar which would allow me to process X amount of entries in my form.
For instance, I have a form which has X amount of "name" elements, which are auto-generated through jQuery. An example of these name elements could be the following:
<input type="text" name="studentName1"></input>
<input type="text" name="studentName2"></input>
<input type="text" name="studentName3"></input>
Now, there's a different amount of student names every time, so this makes it quite complex for me to handle the form data in my controller. I had something like the following 2 examples in mind, but of course they wouldn't work in reality.
[HttpPost]
public ActionResult PostStudentNames(params string[] studentNames)
Or:
[HttpPost]
public ActionResult PostStudentNames(string[] formValues)
Can I achieve something similar to that?
I just want to chime in with a different approach you can use for this. If it's more convenient, you can model bind directly to collections of primitive or complex types. Here's 2 examples:
index.cshtml:
#using (Html.BeginForm("ListStrings", "Home"))
{
<p>Bind a collection of strings:</p>
<input type="text" name="[0]" value="The quick" /><br />
<input type="text" name="[1]" value="brown fox" /><br />
<input type="text" name="[2]" value="jumped over" /><br />
<input type="text" name="[3]" value="the donkey" /><br />
<input type="submit" value="List" />
}
#using (Html.BeginForm("ListComplexModel", "Home"))
{
<p>Bind a collection of complex models:</p>
<input type="text" name="[0].Id" value="1" /><br />
<input type="text" name="[0].Name" value="Bob" /><br />
<input type="text" name="[1].Id" value="2" /><br />
<input type="text" name="[1].Name" value="Jane" /><br />
<input type="submit" value="List" />
}
Student.cs:
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
}
HomeController.cs:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult ListStrings(List<string> items)
{
return View(items);
}
public ActionResult ListComplexModel(List<Student> items)
{
return View(items);
}
}
ListStrings.cshtml:
#foreach (var item in Model)
{
<p>#item</p>
}
ListComplexModel.cshtml:
#foreach (var item in Model)
{
<p>#item.Id. #item.Name</p>
}
The first form simply binds a list of strings. The second, binds the form data to a List<Student>. By using this approach, you can let the default model binder do some of the tedious work for you.
Updated for comment
Yes you can do that too:
Form:
#using (Html.BeginForm("ListComplexModel", "Home"))
{
<p>Bind a collection of complex models:</p>
<input type="text" name="[0].Id" value="1" /><br />
<input type="text" name="[0].Name" value="Bob" /><br />
<input type="text" name="[1].Id" value="2" /><br />
<input type="text" name="[1].Name" value="Jane" /><br />
<input type="text" name="ClassId" value="13" /><br />
<input type="submit" value="List" />
}
Controller action:
public ActionResult ListComplexModel(List<Student> items, int ClassId)
{
// do stuff
}
Mathias,
This works perfectly well without recourse to the params object. your form controls:
<input type="text" name="studentName" />
<input type="text" name="studentName" />
<input type="text" name="studentName" />
<input type="text" name="professorName" />
You would use the FormCollection object, which will contain all your form elements as either comma separated lists (if a control array) or as single properties. In the above example, this is what we'd get:
[HttpPost]
public ActionResult PostStudentNames(FormCollection formValues)
{
// basic check for rogue commas inside input controls
// would need far more sophistication in a #real# app :)
var valueStudents = formValues["studentName"].Split(',')
.Where(x => x.Length > 0).ToArray();
var valueProfessor = formValues["professorName"];
// other stuff
}
etc... At least, this is my recollection of this from a recent project. :)
<input type="text" name="studentName[0]"></input>
<input type="text" name="studentName[1]"></input>
<input type="text" name="studentName[2]"></input>
public ActionResult PostStudentNames(string[] studentName)
{
}

object gets passed to action as string

I have a form that has a hidden field wich stores a object. This object is a RoutesValues (I want to store a reference because when I process the form I want to redirect to a route). The action that processes the form is:
public ActionResult Añadir(string userName, string codigoArticulo, string resultAction, string resultController, object resultRouteValues, int cantidad)
{
processForm(codigoArticulo, cantidad);
if (!ModelState.IsValid)
TempData["Error"] = #ErrorStrings.CantidadMayorQue0;
if (!string.IsNullOrWhiteSpace(resultAction) && !string.IsNullOrWhiteSpace(resultController))
return RedirectToAction(resultAction, resultController, resultRouteValues);
return RedirectToAction("Index", "Busqueda", new {Area = ""});
}
and my form is:
#using (Html.BeginForm("Añadir", "Carrito", FormMethod.Get, new { #class = "afegidorCarrito" }))
{
<fieldset>
<input type="hidden" name="codigoArticulo" value="#Model.CodiArticle" />
<input type="hidden" name="resultController" value="#Model.Controller" />
<input type="hidden" name="resultAction" value="#Model.Action" />
<input type="hidden" name="resultRouteValues" value="#Model.RouteValues" />
<input type="text" name="cantidad" value="1" class="anadirCantidad" />
<input type="submit" />
</fieldset>
}
the problem I have is that resultRouteValues gets passed as a string instead of an object. Is there any way to fix this?
Thanks.
No, there is no easy way if RouteValues is a complex object. You will have to serialize the object into some text representation into this hidden field and then deserialize it back in your controller action. You may take a look at MvcContrib's Html.Serialize helper.

Resources