MVC 4 : Passing of selected guests details form View to Controller - asp.net-mvc

My View has list of guests (i got entire list) and on their arrival, I have to mark them as arrived.
I wait for 30 min and submit all details to servers by clicking on submit button.
i will submit, guest ID and arrived status to server
I donot know how to pass last of the active guests from View to server.
My Controller
[HttpPost]
public ActionResult GuestList(GuestListForModel collection)
{
var temp = collection;
return View();
}
MyView
#using (Html.BeginForm("GuestList", "Guest"))
{
#foreach (var saiList in Model.GuestArrivalInfoList)
{
<li class="group">
<div class="IB left">
<img src="images/user-1.png" alt="" class="stdImg rds50" width="100" height="100" />
</div>
<div class="left brRgt nameAge">
<div class="group">
#saiList.FirstName #saiList.LastName
<div class="age">#saiList.Age<span class="font9">yrs</span></div>
</div>
</div>
<footer class="group">
<div class="sheet brRgt left">
<span class="font9 dblock">present</span>
<div>#Model.Present</div>
</div>
<div class="sheet brRgt left">
<span class="font9 dblock">#Model.Absent</span>
<div>1</div>
</div>
<div class="sheet brRgt left">
<span class="font9 dblock">Model.NoAttendance</span>
<div>1</div>
</div>
<input type="submit" value="Save" id="Save" />
</footer>
Model
public class GuestListForModel
{
public bool Status {get;set;} // arrived or not arrived.
public Int GuestID {get;set;}
}
P.S : I could not get satisfactory answer from googling.
Thanks in Advance

There are two changes that you will need to make:
Change the GuestList action to accept a collection:
[HttpPost]
public ActionResult GuestList(ICollection<GuestListForModel> collection)
{
var temp = collection;
//do your processing
return View();
}
Use a for loop to render the guest list
#for(var i = 0; i < Model.GuestArrivalInfoList.Count; i++)
{
#Html.TextBoxFor(m => Model.GuestArrivalInfoList[i].GuestID)
}
Razor/ASP.NET will take care of both setting the correct id in the view model when rendered (e.g. GuestID would have an id of "GuestArrivalInfoList[1].GuestID"), and will also handle the model binding when hitting the action, making sure that the collection of fields are mapped into the collection.
I presume that there are missing / additional details in your view model and GuestListForModel that ensure that you have the correct data going back and forth (e.g. I don't see the GuestID in the view).

Related

How to pass modified model back to Controller in ASP.NET Core

I have the following model:
public class Card
{
[DataType(DataType.Date)]
[BindProperty]
public DateTime Day { get; set; }
[BindProperty]
public string Field { get; set; }
}
The following Controller:
// GET: Card
public async Task<IActionResult> Index(DateTime? day)
{
return View(model);
}
public async Task<IActionResult> Refresh(DateTime? Day, string Field)
{
return RedirectToAction("Index", Day);
}
The following View:
#model Card
<h1>Cards</h1>
<div class="text-center">
<label asp-for="Day" class="control-label"></label>
<input asp-for="Day" class="form-control" />
</div>
<div class="text-center">
<label asp-for="Field" class="control-label"></label>
<select asp-for="Field" class="form-control" asp-items="ViewBag.Fields"></select>
</div>
<form asp-action="Refresh">
#Html.HiddenFor(x => x.Day)
#Html.HiddenFor(y => y.Field)
<input type="submit" value="Refresh" class="btn btn-default" />
</form>
No matter what I change, I always get the initial Day value back and null as the Field, like the Model has never been changed…
So how can I post back the modified model to my controller?
your form is submitting the values from the hidden fields which are rendered on the page when the view first loads and then they are never modified (which is why you are seeing the default initialization for Day and for Field). Your editable fields are outside of the form and are what you're editing but they never get submitted to the server. I think the main takeaway here for you is that forms only know about inputs that exist inside of them (unless you write some javascript magic to handle this but there is no reason to do so in this case)
You need to remove the hidden fields and put your editable fields inside the form as follows:
#model Card
<h1>Cards</h1>
<form method="post" asp-action="Refresh">
<div class="text-center">
<label asp-for="Day" class="control-label"></label>
<input asp-for="Day" class="form-control" />
</div>
<div class="text-center">
<label asp-for="Field" class="control-label"></label>
<select asp-for="Field" class="form-control" asp-items="ViewBag.Fields"></select>
</div>
<input type="submit" value="Refresh" class="btn btn-default" />
</form>
you can also change your controller action to:
[HttpPost]
public async Task<IActionResult> Refresh(Card card)
{
return RedirectToAction("Index", card.Day);
}

How Can I Get ViewData in PartialView in Razor Pages

I'm Using Razor Pages For my App, in one part of my app I've used a partial view here is my codes;
public class Permission
{
[Key]
public int PermissionId { get; set; }
public string PermissionTitle { get; set; }
public int? ParentID { get; set; }
}
public class IndexModel : PageModel
{
public PartialViewResult OnGetCreateRole()
{
var ListPermission = permissionService.AllPermission();
return new PartialViewResult()
{
ViewName = "_PCreateRole", // partial's name
ViewData = new ViewDataDictionary<List<Permission>>(ViewData,
ListPermission)
};
}
}
ViewData is a List of Permission class and i've sent ViewData to partial but i dont know how to get ViewData, also my partial use another model, below is my partial:
#model ToMVC.DataLayer.Entities.User.Role
<div class="row">
<div class="col-md-12">
<form asp-page="CreateRole" method="post">
<div class="form-group">
<label class="control-label">Title</label>
<input asp-for="RoleTitle" class="form-control"/>
<p><span class="text-danger" asp-validation-for="RoleTitle"></span></p>
</div>
<div class="form-group">
<input type="submit" value="submit" class="btn btn-primary" />
</div>
//this part needs ViewData
#foreach (var item in ViewData)
{
}
</form>
</div>
</div>
I want to use ViewData in Foreach loop.
A better solution than ViewData would be to simply make a new ViewModel class containing all the information you need for a view.
public class UserRoleAndPermissions{
public UserRoleAndPermissions(){
Permissions = new List<Permissions>();
}
public List<Permission> Permissions {get;set;}
public ToMVC.DataLayer.Entities.User.Role Role {get;set;}
}
And your view
//check your namespace here - this is just an example
#model ToMVC.DataLayer.UserRoleAndPermissions
<div class="row">
<div class="col-md-12">
<form asp-page="CreateRole" method="post">
<div class="form-group">
<label class="control-label">Title</label>
<input asp-for="RoleTitle" class="form-control"/>
<p><span class="text-danger" asp-validation-for="RoleTitle"></span></p>
</div>
<div class="form-group">
<input type="submit" value="submit" class="btn btn-primary" />
</div>
#foreach (var item in Model.Permissions)
{
}
</form>
</div>
</div>

Adding users to a list MVC

I have a form in which the user is supposed to enter Name and Wage and click a button Add. When the button "Add" is clicked, that user is supposed to be displayed on a list.
This is how I tried it.
Controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using TimeIsMoney.Models;
namespace TimeIsMoney.Controllers
{
public class HomeController : Controller
{
List<UserModel> users = new List<UserModel>();
public ActionResult Index(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
public ActionResult AddUser(UserModel user)
{
users.Add(user);
return View(users);
}
}
}
View:
#model TimeIsMoney.Models.LoginModel
#{
}
#functions{
public string GetAntiForgeryToken()
{
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
return cookieToken + ":" + formToken;
}
}
<div id="main-content" class="col-md-8 col-md-offset-2">
<div class="col-md-12 row">
<h1>Time is money my friend!</h1>
</div>
<div class="col-md-12 row">
<h2>1000kr</h2>
</div>
<div class="col-md-12 row">
<button class="btn" onclick="start()">Start</button>
<button class="btn" onclick="reset()">Reset</button>
</div>
<div class="col-md-12 row">
<form >
<input type="text" placeholder="Name" />
<input type="number" placeholder="Hourly wage" />
<input type="submit" value="Add" onclick="AddUser()" />
</form>
</div>
<div class="col-md-12 row">
<div class="col-md-3 col-md-offset-1">
<label>Name:</label>
<ul>
<li>Dave</li>
<li>Pete</li>
</ul>
</div>
<div class="col-md-4">
<label>Wage:</label>
<ul>
<li>500kr/h</li>
<li>500kr/h</li>
</ul>
</div>
</div>
<br />
<br />
</div>
Model:
namespace TimeIsMoney.Models
{
public class UserModel
{
[Required]
[DataType(DataType.Text)]
[DisplayName("Username")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Text)]
[DisplayName("Wage")]
public string Wage { get; set; }
}
}
Am I on the right path?
How can I move on from here?
UPDATE:
public ActionResult AddUser(UserModel user)
{
var list = Session["myUsers"] as List<UserModel>;
list.Add(user);
return View(list);
}
You're mostly on the right path excluding way you're trying to store your users list.
Since ASP.NET MVC controller instance is created for every request and disposed after view is rendered and passed to the browser - it will be new controller holding new List<UserModel> created on every request.
So you have to store it somewhere else (session variables, file on server's disk, database and so on). Usually database is best choice for this.
In the case you want to store it in session variable, you should add something like this into Global.asax:
protected void Session_Start(object sender, EventArgs e)
{
Session["myUsers"] = new List<UserModel>();
}
and then in your controller's methods you will be able to access this list as
var list = Session["myUsers"] as List<UserModel>;

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.

What to do if action button and form fields are in different sections?

Let's say we have an edit form to create a new user. Now the save button is placed in a different section, the footer.
My problem is that I can't get the edit fields and the save button in one form, because the button is in a different section.
Because of that, I can't submit the form.
What is the best approach to this problem?
_Layout.cshtml
<div class="content">
#RenderBody()
</div>
<div class="footer">
#RenderSection("Footer")
</div>
Index.cshtml
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
#section Footer
{
<input type="submit" value="Save" />
}
#using(Html.BeginForm())
{
<h2>New User</h2>
#Html.EditorForModel()
}
You could call form.Dispose() explicitly, instead of the using statement:
#{ var form = Html.BeginForm() }
<h2>New User</h2>
#Html.EditorForModel()
#section Footer
{
<input type="submit" value="Save" />
#{ form.Dispose(); }
}
Edit
But you have to at least make sure the Body and Footer section are in the same container, for example:
<div class="content">
#RenderBody()
<div class="footer">
#RenderSection("Footer")
</div>
</div>
With the layout as written in the question, the content div (and therefore the form tag) must close before the submit button can ever appear. There's no way this can work logically:
<div class="content">
#RenderBody() ## form opens, and therefore must close here
</div>
<div class="footer">
#RenderSection("Footer") ## submit button is here -- can never be inside the form
</div>
Editorial aside: It seems like a very bad idea to have a form split across multiple partial views. You might call it a code smell -- I'd try to avoid it if possible.
You found a pretty awkward work around. I suggest doing it this way:
In order to distinguish actions of different buttons clicked, create a new property in your model: public string Action { get; set; }
Give you form an id and include a hidden input for your new model property.
<form id="my-form">
#Html.HiddenFor(x => x.Action)
...
</form>
Create buttons in the footer, with the same class, but different values:
<button class="btn-submit" value="action1">Submit</button>
<button class="btn-submit" value="action2">Submit</button>
Use the following JavaScript:
$('.btn-submit').live('click', function() {
// update value of hidden input inside the form
$('#Action').val($(this.val()));
// submit the form
$('#my-form').submit();
});
In your ActionResult perform different actions based on the value of Action property:
public ActionResult WahteverAction(WhateverModel model)
{
if(ModelState.IsValid)
{
if(model.Action == "action1")
{
// do whatever needs to be done for action1
}
if(model.Action == "action2")
{
// do whatever needs to be done for action2
}
}
return View();
}
I found a workaround for my problem. It's not nice, but it works.
I replaced the submit button with an anchor. When the anchor is clicked, a javascript function gets called.
<a name="save" onclick="submitAction(this)"></a>
The javascript function creates a hidden submit button in the form and clicks it.
function submitAction(sender) {
var action = $(sender).attr('name');
$('form').append('<input type="submit" style="display:none" id="tmpSubmit" />');
$('#tmpSubmit').attr('name', action);
$('#tmpSubmit').click();
}

Resources