MVC BeginCollectionItem - asp.net-mvc

I'm having some issue getting my partial view BeginCollectionItem to save to the database. I have a form which has a dynamic number of "sections" that can be added to the page, and within each of these fields there is a text box where the user can enter the section name.
As far as I can tell the BeginCollectionItem within the partial view is working properly, however I cannot post the info to the database. In my other forms I have used a [bind()] to send the data to the database, is it possible to get this into a list and then post that via a bind?
I've included my code below:
The Model:
namespace project.Models.SetupViewModels
{
public class SOPTopTemplateBuilderViewModel
{
public List<Section> Section { get; set; }
}
public class Section {
public int SectionId { get; set; }
public string SectionText { get; set; }
public string TopTempId { get; set; }
}
}
cshtml:
#model IEnumerable<project.Models.SetupViewModels.Section>
#using (Html.BeginForm("SOPTopTemplateBuilder", "Setup", FormMethod.Post))
{
<div class="main-holder" id="theform">
#foreach(var item in Model)
{
#Html.Partial("_SectionCreator", item)
}
</div>
<button id="add" type="button">Add</button>
<div class="form-group submit-row">
<div class="col-12 float-to-right">
<input type="submit" class="btn btn-default" value="continue" />
</div>
</div>
}
#section Scripts {
<script>
$(document).ready(function () {
var url = '#Url.Action("AddSection")';
var form = $('form');
var recipients = $('#theform');
$('#add').click(function() {
$.post(url, function(response) {
recipients.append(response);
// Reparse the validator for client side validation
form.data('validator', null);
$.validator.unobtrusive.parse(form);
});
});
});
</script>
}
Partial View:
#model project.Models.SetupViewModels.Section
#using HtmlHelpers.BeginCollectionItemCore
#using (Html.BeginCollectionItem("Section"))
{
<div class="new-section">
<div>
<p>New Section</p>
#Html.HiddenFor(m => m.SectionId, new { #class="id" })
#Html.EditorFor(m => m.SectionText, new { #class = "form-control limit-form"})
</div>
</div>
}
Controller:
[HttpPost]
public PartialViewResult AddSection()
{
return PartialView("_SectionCreator", new Section());
}
[HttpGet]
public ActionResult SOPTopTemplateBuilder(){
List<Section> model = new List<Section>();
return View(model);
}
[HttpPost]
public ActionResult SOPTopTemplateBuilder(IEnumerable<Section> soptop)
{
if (ModelState.IsValid)
{}
return View(soptop);
}

Your use of Html.BeginCollectionItem("Section") perpends Section[xxxx] to the name attribute (where xxxx is a Guid) so that you generating inputs with
<input name="Section[xxxx].SectionId" .... />
which posts back to a model containing a collection property named Sections.
Since you already have a model with that property, you can change the POST method to
[HttpPost]
public ActionResult SOPTopTemplateBuilder(SOPTopTemplateBuilderViewModel soptop)
other options include
Using your existing POST method and omitting the "Section" prefix
using Html.BeginCollectionItem("") which will generate
name="[xxxx].SectionId"
Changing the POST method signature to public ActionResult
SOPTopTemplateBuilder(IEnumerable<Section> section) (where the
name of the parameter matches the name of the prefix)
Using a BindAttribute to 'strip' the prefix from the form values
public ActionResult SOPTopTemplateBuilder([Bind(Prefix = "Section")]IEnumerable<Section> soptop)
As a side note, your editing data, so you should always use a view model (say public class SectionViewModel) rather than using data models in your view. - What is ViewModel in MVC?

Related

ASP.NET MVC - Having trouble obtaining pull-down selection in control

I am having trouble accessing a user's pull-down selection from within the controller once the user has selected a value and submitted it via the view.
The pull-down menu is populating correctly and I've written a simple JavaScript function that alerts the user when a selection change is made. Via the alert, I see the RoleID value. I just can't seem to access it in the controller. Please see my code below and help! Thank you in advance.
Model:
public class dbModels
public class UserAccessRequestViewModel
{
dbEntities _dbEntities = new dbEntities();
public IEnumerable<ActiveRoles> Roles { get; set}
public Guid selectedRoleID { get; set:}
public class ActiveRoles
{
public Guid RoleID { get; set; }
public string RoleName { get; set; }
}
public List<ActiveRoles> GetActiveRoles()
{
var ActiveRoles = dbEntities.LU_ROLE.Where(x => x.ACTIVE)
.Select (x => new ActiveRoles { RoleID = x.ROLE_ID, RoleName = x.ROLE_NAME }).ToArray();
return activeRoles.ToList();
}
}
View:
<div class="text-left" style="margin:4px; padding:4px">
<h2>Assign Role</h2>
#Html.DropDownList(model => model.selectedRoleID, new SelectList(Model.Roles, "RoleID", "RoleName"), new { #onchange = getUserRoleID(this)" })
</div>
<div class="form-actions no-color" style="margin:4px; padding:4px">
#using (Html.BeginForm("Approve", "UserAccessRequestManagement", FormMethod.Post))
{
<input type="submit" value="Approve" class="btn btn-default" />
}
#Html.ActionLink("Back to List", "Index")
</div>
Control:
public class UserAccessRequestManagementController : Controller
dbModels.UserAccessRequestViewModel _UserAccessRequestViewModel = new dbModels.UserAccessRequestViewModel();
[HttpPost, ActionName("Approve")]
public ActionResult Approve(Guid id)
{
Guid RoleID = _UserAccessRequestViewModel.selectedRoleID;
}
Your select input should be part of your form.
<div class="form-actions no-color" style="margin:4px; padding:4px">
#using (Html.BeginForm("Approve", "UserAccessRequestManagement", FormMethod.Post))
{
<div class="text-left" style="margin:4px; padding:4px">
<h2>Assign Role</h2>
#Html.DropDownList(model => model.selectedRoleID, new SelectList(Model.Roles, "RoleID", "RoleName"), new { #onchange = getUserRoleID(this)" })
</div>
<input type="submit" value="Approve" class="btn btn-default" />
}
#Html.ActionLink("Back to List", "Index")
</div>
Also you have to make sure what you want to get in your Controller action as a parameter.
If you just need a guid then change the parameter name to selectedRoleID as the name of the input element should match with your action parameter. In case you want to take entire model as parameter then you have to mention it correctly in action defintion.
In case you want to get entire model in your POST action
public class UserAccessRequestManagementController : Controller
dbModels.UserAccessRequestViewModel _UserAccessRequestViewModel = new dbModels.UserAccessRequestViewModel();
[HttpPost, ActionName("Approve")]
public ActionResult Approve(UserAccessRequestViewModel model)
{
Guid RoleID = model.selectedRoleID;
}
OR make it like this In case you just want a selected guid in your POST action
[HttpPost, ActionName("Approve")]
public ActionResult Approve(Guid selectedRoleID)
{
Guid RoleID = selectedRoleID
}

save position from all draggable divs on save mvc

I want to make a table arrangement system for a restaurant admin page.
I want a table index page which shows all tables as divs inside a bigger div(map of the restaurant).
The restaurant van add tables and these tables be added to that index page.
The tables can be dragged with the jquery draggable function.
This page needs to have a save button and if clicked it needs to store all the tables positions to the database.
My model is like this:
public class table
{
public int id { get; set; }
public string tableName { get; set; }
public bool available { get; set; }
public float positionY { get; set; }
public float positionX { get; set; }
}
My Controller which does not have much now.
private BonTempsDbContext db = new BonTempsDbContext();
// GET: tafel
public ActionResult Index()
{
return View(db.Tafel.ToList());
}
// GET: Menu/Create
public ActionResult Create()
{
return View();
}
// POST: Menu/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "id,tafelNaam,beschikbaar,positionY,positionX")] Tafel tafel)
{
if (ModelState.IsValid)
{
db.Tafel.Add(tafel);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(tafel);
}
and my view looks like this:
#model IEnumerable<BonTempsMVC.Table>
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
<div class="VoegToeBtn">
<a href="/table/create">
<span class="btn btn-default">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Create new table
</span>
</a>
</div>
<div id="tablewrapper">
#foreach (var item in Model)
{
<div class="draggable ui-widget-content" id="#Html.DisplayFor(ModelItem => item.id)">
<p>#Html.DisplayFor(ModelItem => item.tablename)</p>
</div>
}
</div>
<script>
$(".draggable").draggable({
snap: ".draggable",
snapMode: "outer"
});
</script>
now there needs to be a button which executes a query that update all the table records with the right positions or only the tables which moved if that is possible.
You can create an input tag on your view page. You can specify the Action method which will be invoked when the button is clicked by specifying the onclick attribute. Also, you can pass parameters so that the method will receive the coordinates:
#using (Html.BeginForm("ActionMethodName","ControllerName",new {param1 = coordinate1, param2 = coordinate2}))
{
... your input, labels, textboxes and other html controls go here
<input class="button" id="Update" type="submit" value="Submit" />
}
Then inside your controller, you can write an Action method which will have 2 parameters; viz; param1 and param2 which will do the task of updating the table parameters by writing a linq query:
public ActionResult ActionMethodName(int param1,int param2)
{
//LINQ query goes here for updating table coordinates
}

Retrieving values from partial view during post method

I have a view which contains a dropdown list and on dropdownlist item being selected I load a partial view. And when the form is submitted I want to be able to get both the values from main view and partial view during form submit.
Here is the main view
#model AdminPortal.Areas.Hardware.Models.CreateModule
#{
ViewBag.Title = "Create Module";
Layout = "~/Views/shared/_BootstrapLayout.basic.cshtml";
}
#Html.ValidationSummary(true)
<fieldset class="form-horizontal">
<legend>Add a Module <small>Create</small></legend>
#using (Html.BeginForm("CreateModule", "Module", new{id="AddModuleForm"}))
{
#Html.ValidationSummary(true)
<div class ="controls">
<div class="input-block-level">#Html.TextBoxFor(model => model.ModuleId, new {#placeholder = "ModuleID"})</div>
<br/>
<div class ="input-block-level" id="selectedModuleTypeName">#Html.DropDownListFor(model => model.SelectedModuleTypeName, Model.TypeNames,"Select Moduletype", new{id = "ModuleList"})</div>
<br/>
<div id="partialDiv"></div>
</div>
<div class="form-actions" id="buttons">
<button type="submit" class="btn btn-primary" id="Submit">Save changes</button>
#Html.ActionLink("Cancel", "ModuleList", null, new { #class = "btn " })
</div>
}
</fieldset>
<div>
#Html.ActionLink("Back to List", "ModuleList")
</div>
<script>
$("#buttons").hide();
$("#ModuleList").on("change", function() {
var modId = $(this).val();
$.get('#Url.Action("GetModulePropertyName", "Module")', { moduleTypeValue: modId }, function(result) {
$("#partialDiv").html(result);
});
//uncomment following section to check if the partial view is working properly
/*.done(function() { alert("done"); })
.fail(function() { alert("fail"); })
.always(function() { alert("completed"); });*/
});
$("#buttons").show();
</script>
and here is the partial view
#model IEnumerable<string>
#foreach(var names in Model)
{
<div class="input-block-level">#Html.TextBoxFor(m=>names, new{Value="", placeholder=names})</div>
<br/>
}
Here is my model
public class CreateModule
{
//Empty form to handle form serialization
public CreateModule()
{
}
[Required]
public string ModuleId { get; set; }
[DataType(DataType.DateTime)]
public DateTime DateEntered { get; set; }
[Required]
public string SelectedModuleTypeName { get; set; }
public IEnumerable<SelectListItem> TypeNames { get; set; }
public List<Property> Properties { get; set; }
}
public class Property
{
public string Name { get; set; }
public string Value { get; set; }
}
Here is the method that script in main view forwards to
[HttpGet]
public ActionResult GetModulePropertyName(string moduleTypeValue)
{
var moduleKindId = _repository.GetModuleKindId(moduleTypeValue);
var modulePropertyNames = _repository.GetModuleKindPropertyNames(moduleTypeValue);
return PartialView("GetModulePropertyName",modulePropertyNames);
}
and finally here is httppost method for the main view
[HttpPost]
public ActionResult CreateModule(CreateModule moduleV)
{
var module = new Module
{
ModuleTypeId = Convert.ToInt64(moduleV.SelectedModuleTypeName),
ModuleId = moduleV.ModuleId,
DateEntered = moduleV.DateEntered,
};
if (ModelState.IsValid)
{
_repository.AddModule(module);
Success("Module added successfully!");
return RedirectToAction("ModuleList", "Module", new {area = "Hardware"});
}
Error("Something went wrong!");
return RedirectToAction("CreateModule", "Module", new { area = "Hardware" });
}
Current situation:
When the form is posted, the properties value of the model that is being passed via partial view is null. I get other values, like typename, Module ID.
What I'd want:
I also want to get the value of properties that is being passed via partial view.
You don't have any input field for the Properties property anywhere in your form. So it will always be null. That's normal.
Here's how you could proceed. Start by setting the correct navigational property so that the helper generates correct names of the corresponding input fields.
Also make sure that you are passing an IEnumerable<Property> model to the partial if you want to be able to get them back correctly:
[HttpGet]
public ActionResult GetModulePropertyName(string moduleTypeValue)
{
var moduleKindId = _repository.GetModuleKindId(moduleTypeValue);
IList<Property> model = ...
return PartialView("GetModulePropertyName", model.ToList());
}
and in your partial view use an editor template:
#model IList<Property>
#{
// This indicates the current navigational context to the helpers
ViewData.TemplateInfo.HtmlFieldPrefix = "Properties";
}
#Html.EditorForModel()
and the last step is to define a custom editor template for the Property class: ~/Views/Shared/EditorTemplates/Property.cshtml (note that the name and location of the template is important)
#model Property
<div class="input-block-level">
#Html.HiddenFor(m => m.Name)
#Html.TextBoxFor(m => m.Value, new { placeholder = Model.Name })
</div>
<br />
Try using the
List<Property>
as a model in your partial view and pass the CreateModule.Properties as model from your View
The problem is model binder can not figure out there
#Html.TextBoxFor(m=>names, new{Value="", placeholder=names})
belongs to as the "names" is not a property on your model class. If you need to bind to the CreateModule.Properties you need to change the partial view to emit textboxes with aproprate names, like this one:
#model IEnumerable<string>
#
{
int i=0;
}
#foreach(var names in Model)
{
<div class="input-block-level">#Html.TextBox("Properties[" + i + "].Value")</div>
<br/>
}

how to add some own forms in a ASP.NET MVC create view?

i have a problem, i had created a controller and a view for adding a new item from a specific model. the view looks like:
#modelModels.UserItem
#{
ViewBag.Title = "New";
}
<h2>New</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Device</legend>
<div class="editor-label">
#Html.LabelFor(model => model.name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.name)
#Html.ValidationMessageFor(model => model.name)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
and the controller:
[HttpPost]
public ActionResult New(UserItem useritem)
{
if (ModelState.IsValid)
{
db.UserItems.AddObject(useritem);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(useritems);
}
how i want to add a dropdown to the form in the view like this:
<select id="Select1">
<option>MARS</option>
</select>
how to access the data from the form after it was submitted in the controller?
Have view model for your page,this view model will be used in your view. So, only include fields from your model that you really need. In Get action you should create this view model and get the needed properties from your model and map them to your view model.
public class UserItemViewModel
{
/* Properties you want from your model */
public string Property1 { get; set; }
public string Property2 { get; set; }
public string Property3 { get; set; }
/* Property to keep selected item */
public string SelectedItem { get; set; }
/* Set of items to fill dropdown */
public IEnumerable<SelectListItem> SelectOptions { get; set; }
/* Fill the SelectListHere. This will be called from index controller */
public void FillOptions()
{
var items = new[] { "Mars", "Venus" }.
Select(x => new SelectListItem { Value = x, Text = x });
SelectOptions= new SelectList(items, "Value", "Text");
}
}
Change controller for receiving ViewModel instead of Model itself.
[HttpPost]
public ActionResult New(UserItemViewModel useritem)
{
/* Repopulate the dropdown, since the values are not posted with model. */
userItem.FillOptions();
if (ModelState.IsValid)
{
/* Create your actual model and add it to db */
// TODO: Map your properties from model to view model.
// Let's say you created a model with name userItemModel
db.UserItems.AddObject(userItemModel);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(useritem);
}
You might need to change Index view controller little.(to fill dropdown)
[HttpGet]
public ActionResult Index()
{
/* Create new viewmodel fill the dropdown and pass it to view */
var viewModel = new UserItemViewModel();
viewModel.FillOptitons();
//TODO : From your model fill the required properties in view model as I mention.
return View(viewModel);
}
And your view,
/* Strongly typed view with viewmodel instead of model itself */
#modelModels.UserItemViewModel
/* This is the dropdown */
#Html.DropDownListFor(m => m.SelectedItem, Model.SelectOptions)
Add that property to your model
Use builtin EditorFor(preffered) or hand-written html to generate client-side input for that property.
Access submitted value by inspecting that property when user submits the form
I like emre's proposition of having a viewModel and I think is the Best solution to your question however just in case you don't want to go that way (you must have a really good reason because it is best) and still want a way to access the values of a form directly you can always use:
var x = Request["myFiledName"];
inside your controller to get to the values passed by your form.

Model value lost on postback

I have the following models:
class A
{
// ...some properties
public B InnerField { get; set; }
}
and
class B
{
public int Id { get; set; }
// ..other properties
}
and a page that has a model Class A and inside the page I have a partial view bound to Class B inside a form.
The value of the Id (in the partial view) is set correctly to the model's Id value (different from 0) but when I submit the page the model has the Id value 0. The Id value is not modified in the component or elsewhere.
Page
...other parts of main page
<%using (Html.BeginForm("ModifyHotel", "Hotel",
FormMethod.Post, new { enctype = "multipart/form-data"}))
{%>
<% Html.RenderPartial("~/Views/Shared/ModifyBaseItem.ascx",
new ModifyItemRequestBaseView() { ItemId = Model.Item.Id });%>
<%}%>
...other parts of main page
Partial View
...other parts of partial view
<br/>
Add Photo: <%:Html.FileBoxFor(x => x.PhotoFile, null)%>
<br/>
Add Video: <%:Html.FileBoxFor(x => x.VideoFile, null)%>
<br/>
<input type="submit" value="Submit changes" />
...other parts of partial view
What can I do to keep the value of the inner model when the post is made?
Thanks,
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
A model = new A() { InnerField = new B() { Id = 5 }};
return View(model);
}
[HttpPost]
public ActionResult Index(B model)
{
//on postback the model should have the value 5 here
return View();
}
}
View:
#model MvcApplication11.Models.A
#using (Html.BeginForm())
{
#Html.Partial("_IndexForm", Model.InnerField)
<input type="submit" />
}
Partial:
#model MvcApplication11.Models.B
#Html.EditorFor(m => m.Id)

Resources