Using Html.BeginCollectionItem Properly - c#

I am new to MVC/Razor/Web and am hoping to eventually be able to edit a list of items, right now I am just trying to display these items using Html.BeginCollectionItem and it is not working (no error, just does not display my items.) I have listed my code below:
Model:
namespace EditList
{
public class GiftModel
{
public string Name { get; set; }
public double Price { get; set; }
}
}
Controller:
namespace EditList
{
public class GiftController : Controller
{
public ActionResult Index()
{
GiftModel[] initalData = new[]
{
new GiftModel{Name = "Tall Hat", Price = 39.95},
new GiftModel{Name = "Long Cloak", Price = 120.00}
};
return View(initalData);
}
}
}
Main View:
#{
Layout = null;
}
#model IEnumerable<GiftModel>
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title></title>
</head>
<body>
<h2>Gift List</h2>
What do you want for your birthday?
#using(Html.BeginForm())
{
<div id="EditorRows">
#foreach (var item in Model)
{
Html.RenderPartial("GiftEditorRow", item);
}
</div>
<input type="submit" value="Finished"/>
}
</body>
</html>
Partial View:
#model GiftModel
<div class="editorRow">
#using(Html.BeginCollectionItem("gifts"))
{
<div>HELLO</div>
Html.DisplayFor(m => m.Name);
Html.TextBoxFor(m => m.Name);
//Html.TextBoxFor(m => m.Price, new { size = 4 });
}
</div>
One thing to note is i did step into my main view and there is data in "items", but the partial view does not display the textboxes ect.. Im new to web code so i'm hoping I am missing something simple (I have looked at many tutorials and maybe i'm just overlooking something)
Let me know if there is any additional info I can provide.

Related

Show validation messages on partial views in asp.net core MVC

I have the following model in order to show validations on multiselect list
using Microsoft.AspNetCore.Mvc.Rendering;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace AccountManagementUI.Models
{
public class NewMembersAddViewModel
{
public string GroupId { get; set; }
public string GroupName { get; set; }
public List<SelectListItem> membersList { get; set; }
[Required(ErrorMessage ="Please select atlast 1 employee")]
public List<Guid> selectedMembersId { get; set; }
}
}
My Controller post method is as follows:
[HttpPost]
public IActionResult GetNewMembers(NewMembersAddViewModel groupMemberData)
{
if (ModelState.IsValid)
{
AddMembersToGroup addMembersToGroup = new AddMembersToGroup();
addMembersToGroup.GroupId = groupMemberData.GroupId;
foreach (var memberId in groupMemberData.selectedMembersId)
{
addMembersToGroup.UserIds.Add(memberId.ToString());
}
_gateway.AddMembersToGroup(addMembersToGroup);
return RedirectToAction("GroupMembers", "Group", new { groupId = groupMemberData.GroupId });
}
else
{
return PartialView("_GetNewMembers", groupMemberData);
}
}
My view is as below:
#model AccountManagementUI.Models.NewMembersAddViewModel
<!DOCTYPE html>
<html lang='en'>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Active Directory Management Portal</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="" />
<meta name="keywords" content="" />
<!-- Latest compiled and minified CSS -->
<!-- Pagelevel Initializations Of Plugins -->
</head>
<body>
<form class="console-panel grid-stack-item-content" asp-action="GetNewMembers" asp-controller="Group" method="post">
<input type="hidden" asp-for="GroupId" />
<input type="hidden" asp-for="GroupName" />
<div class="console-panel-body pl-0 pr-0">
<div class="console-form-body ">
<div class="row">
<div class="col-lg-12 col-md-12">
<div class="form-group row">
<label class="col-2 col-form-label">Members</label>
<div class="col-10">
<select asp-for="selectedMembersId" asp-items="Model.membersList" multiple="multiple" placeholder="Select Members" onchange="console.log($(this).children(':selected').length)" class="search-box form-control">
</select>
<span asp-validation-for="selectedMembersId" class="text-danger"></span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="console-footer">
<button class="btn btn-sm btn-info" type="submit">Submit</button>
<a id="addMembersCancel" class="btn btn-sm btn-secondary" onclick="AddNewMembersCancel()">Cancel</a>
</div>
</form>
<script>
$('.search-box').SumoSelect({ csvDispCount: 3, search: true, searchText: 'Enter here.' });
</script>
<script>
function AddNewMembersCancel() {
$('#addNewMemberModal').modal('hide');
}
</script>
</body>
</html>
I have given validations of selecting at least one member but when I click submit button without selecting member then it redirects to new page and shows the message there. I want to show the message on the same partial view when clicked submit?
You need to add your validation scripts jquery.validate.min.js and jquery.validate.unobtrusive.min.js.
Client-side validation
The partial view _ValidationScriptsPartial.cshtml from shared folder can be added to your view:
<partial name="_ValidationScriptsPartial" />
You have <form> element with a submit button which by default will cause you page to reload on submit.
In order to show the error on the same page when you click submit, you need to prevent the default behavior of the form.
First, you will need some kind of id on your form element. Let's say the id is new-members-form.
<form id="new-members-form" class="console-panel grid-stack-item-content" asp-action="GetNewMembers" asp-controller="Group" method="post">
...
</form>
Then, you need to select the form and tell it to stop executing the default behavior on form submit.
With JQuery:
$("new-members-form").on("submit", (event) => { event.preventDefault(); });
Or plain JS:
document.getElementById("new-members-form").addEventListener("submit", function(event) {
event.preventDefault();
});
When first time to view or failed to add, remember to set the memberList.
Codes of controller:
//Members to SelectListItem
public IList<SelectListItem> GetMembers()
{
var members = new List<Member>
{
new Member {Id = Guid.NewGuid(), Name = "Ada"},
new Member {Id = Guid.NewGuid(), Name = "Brain"},
new Member {Id = Guid.NewGuid(), Name = "Cater"},
};
var memberListItem = members
.Select(x => new SelectListItem { Text = x.Name, Value = x.Id.ToString() })
.ToList();
return memberListItem;
}
[Route("/members")]
public IActionResult _GetNewMembers()
{
var model = new NewMembersAddViewModel {
membersList = GetMembers()
};
return View(model);
}
[Route("/api/Group/GetNewMembers")]
[HttpPost]
public IActionResult GetNewMembers(NewMembersAddViewModel groupMemberData)
{
if (ModelState.IsValid)
{
//Success
return RedirectToAction("GroupMembers", "Group", new { groupId = groupMemberData.GroupId });
}
else
{
//when failed, set the memberList again.
groupMemberData.membersList = GetMembers();
return PartialView("_GetNewMembers", groupMemberData);
}
}
Codes of view are same as you.

On Post action method the List<className> and SelectList properties of a ViewModel are always null [duplicate]

I have a really wierd issue with MVC.
My models keeps getting submitted empty.
And it's probably really simple, but I just can't find the issue.
My model looks like this:
public class LoginModel
{
public string Username;
public string Password;
}
My controller like this:
[HttpGet]
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(LoginModel loginTest)
{
if (loginTest.Username != "x" && loginTest.Password != "y")
{
ModelState.AddModelError("a", "Login failed.");
return View(loginTest);
}
else
{
return RedirectToAction("Home", "Welcome");
}
}
And the view is also very simple, like this.
#model LoginSolution.Models.LoginModel
#{
Layout = null;
}
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Login</title>
</head>
<body>
#using (Html.BeginForm("Login", "Home"))
{
<div> <span>Username : </span>
#Html.EditorFor(model => model.Username)
<br />
<span>Password : </span>
#Html.EditorFor(model => model.Password)
<br />
#Html.ValidationSummary()
<br />
<input type="submit" value="Login" name="Login" />
</div>
}
</body>
</html>
This is not a question about security or best practice.
This is just a question regarding why the model keeps returning itself as empty upon submitting.
Your model contains fields, not properties (no getter/setter) so the model binder cannot set the values. Change your model to
public class LoginModel
{
public string Username { get; set; }
public string Password { get; set; }
}

mvc 5 - Passing a list of checked items from view to controller [duplicate]

This question already has answers here:
Post an HTML Table to ADO.NET DataTable
(2 answers)
Closed 6 years ago.
I'm trying to pass a list of items that have their checkbox checked. The problem is that when I press submit, my controller does not receive anything from the view ( my items of type IEnumerable are null )
Here is my view :
#model IEnumerable<MyApp.Models.MyClass>
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
</head>
<body>
<div>
#using (Html.BeginForm())
{
foreach(var item in Model)
{
Html.CheckBoxFor(modelItem => item.Checked);
Html.DisplayFor(modelItem => item.Url);
<br/>
}
<input type="submit" value="Submit" />
}
</div>
</body>
</html>
This my model :
namespace MyApp.Models
{
public class MyClass
{
public string Url;
public bool Checked;
public MyClass(string item)
{
Url = item;
}
public MyClass()
{
}
}
}
And this is my controller :
[HttpPost]
public ActionResult Something(IEnumerable<MyClass> items)
{
//bla bla bla
}
for my case I will do this way.
foreach(var item in Model)
{
<input type="checkbox" name="removefromcart" value="#(item.Id)" />
Html.DisplayFor(modelItem => item.Url);
<br/>
}
In my controller class
public ActionResult UpdateWishlist(FormCollection form)
{
var allIdsToRemove = form["removefromcart"] != null
? form["removefromcart"].Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(int.Parse)
.ToList()
: new List<int>();
You can try this.
foreach(var item in Model)
{
<input type="checkbox" name="removefromcart" value="#item.Checked" />
Html.DisplayFor(modelItem => item.Url);
<br/>
}
Then your controller can be modified to this.
[HttpPost]
public ActionResult Something(string[] removefromcart) //you will get all the values as string array.
{
//bla bla bla
}
Here is a similar answer that might help you

Ajax postbacks with cascading ListBox in ASP.NET MVC3

First off, I am an ASP.NET MVC noob. It's my first project with ASP.NET MVC, so I am still learning. My background is mostly in WPF and XAML for the past two years.
So here is my problem: I have three cascading ListBoxes. The second listbox data is dependent on the first, and the third is dependent on the second. I want to use Ajax refreshes to fill the data in each list.
Here is my Index.cshtml:
#model WebApplication.Models.DevelopmentModel
<!DOCTYPE html>
<html>
<head>
<title>Dashboard</title>
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
</head>
<body class="body" scroll="auto">
<div class="page">
<div class="content">
<div id="lists">
#Html.Partial("DevelopmentListsView", Model)
</div>
</div>
</div>
</body>
</html>
My DevelopmentListsView.cshtml looks like this:
#model WebApplication.Models.DevelopmentModel
#using (Ajax.BeginForm("Index", "Development", new AjaxOptions() { UpdateTargetId = "lists" } ))
{
#Html.ListBoxFor(m => m.SelectedApplication, new SelectList(ViewBag.Applications), new { onchange = "this.form.submit();" })
#Html.ListBoxFor(m => m.SelectedVersion, new SelectList(ViewBag.Versions), new { onchange = "this.form.submit();" })
#Html.ListBoxFor(m => m.SelectedFlow, new SelectList(ViewBag.Flows) )
}
My Model looks like:
public class DevelopmentModel
{
public string SelectedApplication { get; set; }
public string SelectedVersion { get; set; }
public string SelectedFlow { get; set; }
}
And my Controller looks like this:
public class DevelopmentController : Controller
{
//
// GET: /Development/
public ActionResult Index()
{
FillViewBag();
return View(new DevelopmentModel());
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(DevelopmentModel model)
{
FillViewBag(model);
return PartialView("DevelopmentListsView", model);
}
private void FillViewBag(DevelopmentModel model = null)
{
//Magic to get all three lists dependent on the available data in the model:
ViewBag.Applications = applications;
ViewBag.Versions = versions;
ViewBag.Flows = flows;
}
}
Now, I want to use Ajax callbacks to retrieve the data, so it won't refresh every time, but when I click one of the Listbox items, the page then only shows the DevelopmentListsView view after that, not refreshing anything..
Can someone tell me what I am doing wrong?
Thanks for looking!
Figured out my own question:
I had two errors:
I missed the jquery script include in the Index.cshtml:
<script src="#Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
And I used the wrong submit (it should have been the jQuery submit):
$(this.form).submit()
The submit placed inside my models
#model WebApplication.Models.DevelopmentModel
#using (Ajax.BeginForm("Index", "Development", new AjaxOptions() { UpdateTargetId = "lists" } ))
{
#Html.ListBoxFor(m => m.SelectedApplication, new SelectList(ViewBag.Applications), new { onchange = "$(this.form).submit()" })
#Html.ListBoxFor(m => m.SelectedVersion, new SelectList(ViewBag.Versions), new { onchange = "$(this.form).submit()" })
#Html.ListBoxFor(m => m.SelectedFlow, new SelectList(ViewBag.Flows) )
}
Hope this helps someone some day ;).

Model is null?

I've read a lot of similar posts about null model but my case is very very simple and still the model on Create action is null. What am I doing wrong???
Here is the situation: One main view, two strongly typed partial views inside, each binded to a public property of the main model. Any help is appreciated.
models:
public class SimpleModel1
{
public IEnumerable<string> SomeStrings1 { get; set; }
}
public class SimpleModel2
{
public IEnumerable<string> SomeStrings2 { get; set; }
}
public class ComplexModel
{
public SimpleModel1 model1 { get; set; }
public SimpleModel2 model2 { get; set; }
public IEnumerable<string> SomeStringsComplex { get; set; }
}
int he controller:
public ActionResult Create()
{
ComplexModel complex = new ComplexModel();
complex.model1 = new SimpleModel1();
complex.model1.SomeStrings1 = new List<string> { "a1", "a2", "a3"};
complex.model2 = new SimpleModel2();
complex.model2.SomeStrings2 = new List<string> { "b1", "b2", "b3" };
complex.SomeStringsComplex = new List<string> { "c1", "c2", "c3" };
return View(complex);
}
[HttpPost]
public ActionResult Create(ComplexModel model)
{
if (ModelState.IsValid)
{
var test = model.SomeStringsComplex;
}
return View();
}
Views:
2 strong partial views -each for model
<%# Control Language="C#"
Inherits="System.Web.Mvc.ViewUserControl<MvcApp1.Models.SimpleModel2>" %>
<fieldset>
<legend>Fields</legend>
<% foreach (string item in Model.SomeStrings2)
{%>
<p>
<label for="Title">Item Title:</label>
<%= Html.TextBox(item,item)%>
</p>
<%
}
%>
</fieldset>
1 main view:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<MvcApp1.Models.ComplexModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Create
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Create</h2>
<% using (Html.BeginForm()) {%>
<fieldset>
<div> Own values
<% foreach (string item in Model.SomeStringsComplex)
{%>
<p>
<label for="Title">Item Title:</label>
<%= Html.TextBox(item,item) %>
</p>
<%
}
%>
</div>
<div>Simple values 1
<%Html.RenderPartial("SimpleModelView1", this.ViewData.Model.model1, new ViewDataDictionary()); %>
</div>
<div>Simple values 2
<%Html.RenderPartial("SimpleModelView2", Model.model2, new ViewDataDictionary()); %>
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<% } %>
I'm going to take a wild stab and say your Model validation is failing when posting a ComplexModel back to the server
Your Model validation doesn't have to fail at all. You're not returning anything from inside the if block so you're always returning a View with no Model associated:
if (ModelState.IsValid)
{
var test = model.SomeStringsComplex;
}
return View(); // View is called with no Model data
Judging by your code, that would cause the Create view to be instantiated with no Model. That can be fixed fairly simply:
[HttpPost]
public ActionResult Create(ComplexModel model)
{
if (ModelState.IsValid)
{
var test = model.SomeStringsComplex;
// Do something to Create the object
RedirectToAction("Index");
}
// Model State is invalid, return so the user can correct
return View(model);
}
Aren't you supposed to send something to the view, on that line?
return View();
I think the problem is how you are specifying your text fields - the model binder does not know how to assign them back to the properties that they came from.
You may want to read this Haacked article that describes how to use model binding for enumerable properties:
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
Finally I found a solution. The data posted to Create method should look s.th. like this:
[HttpPost]
public ActionResult Create(ComplexModel mainModel, SimpleModel nestedModel, SimpleModel2 nested2Model)
{
if (ModelState.IsValid)
{
var main = mainModel;
var nested1 = nestedModel;
var nested2 = nested2Model;
}
return View();
}
The nested parameters are not null when the models are binded to partial views. If used in the main form through main model than the values are in the instance of the main model. I've also changed IEnumerable<string> to IList<string> and then rendered the string values with
for (int i = 0; i < Model.Strings.Count; i++)
{ %>
<%: Html.EditorFor(m => m.Strings[i])%>
<% }
Thanks all

Categories

Resources