Cannot pass viewmodel with list to controller-action (MVC 5) - c#

I am having problem passing a viewmodel that contains a list of "files" from my view to my controller(action). The viewmodel seems to be passed to the actionresult with all the propetries, except the list (List Files). The list just stays null, even if the model contains files when i submit the form.
What do i do wrong? I will paste my c# code from my controller, model, viewmodel and my RazorHTML.
File-model:
public class File
{
[Key]
public int ID { get; set; }
[Display(Name = "Filnamn")]
public string Filename { get; set; }
//[Display(Name = "Beskrivning")]
//public string Description { get; set; }
[Display(Name = "Filtyp")]
public string ContentType { get; set; }
public byte[] Data { get; set; }
public int Size { get; set; }
//public string CreatorId { get; set; }
public string CreatorID { get; set; }
[Required]
[Display(Name = "Skapare")]
[ForeignKey("CreatorID")]
public virtual ApplicationUser Creator { get; set; }
//public int DocumentId { get; set; }
public int DocumentID { get; set; }
[Required]
[Display(Name = "Dokument")]
[ForeignKey("DocumentID")]
public virtual Document Document { get; set; }
}
Viewmodel:
public class CreateDocumentViewModel
{
public CreateDocumentViewModel()
{
CheckboxUsers = new List<CheckBoxListUser>();
CheckboxGroups = new List<CheckBoxListGroup>();
CheckboxTags = new List<CheckBoxListTags>();
Files = new List<File>();
}
[Display(Name = "Beskrivning")]
public string Description { get; set; }
[Display(Name = "Titel")]
public string Name { get; set; }
[DataType(DataType.MultilineText)]
[Display(Name = "Markdown")]
public string Markdown { get; set; }
HttpPostedFileBase FileToAdd { get; set; }
[Display(Name="Användare")]
public List<CheckBoxListUser> CheckboxUsers { get; set; }
[Display(Name = "Grupper")]
public List<CheckBoxListGroup> CheckboxGroups { get; set; }
[Display(Name = "Taggar")]
public List<CheckBoxListTags> CheckboxTags { get; set; }
[Display(Name = "Filer")]
public ICollection<File> Files { get; set; }
public string FilesJson { get; set; }
}
View:
#model MarkdownManagerNew.Viewmodels.CreateDocumentViewModel
#{
ViewBag.Title = "CreateDocument";
}
<h2>Nytt dokument</h2>
#Html.BeginForm("CreateFile", "User", new { model = Model, test = Model.Name, files = Model.Files }, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control", placeholder = "Titel" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-10">
#Html.EditorFor(model => model.Description, new { htmlAttributes = new { #class = "form-control", placeholder = "Beskrivning" } })
#Html.ValidationMessageFor(model => model.Description, "", new { #class = "text-danger" })
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div class="form-group col-md-6 markdownEditorForm">
<div class="col-md-12">
#Html.EditorFor(model => model.Markdown, new { htmlAttributes = new { #class = "form-control markdownEditor" } })
#Html.ValidationMessageFor(model => model.Markdown, "", new { #class = "text-danger" })
</div>
</div>
<div class="col-sm-6 markdownResult"></div>
</div>
</div>
<div class="row">
<dl class="dl-horizontal">
<dt>
#Html.DisplayNameFor(model => model.CheckboxGroups)
</dt>
#for (int i = 0; i < Model.CheckboxGroups.Count; i++)
{
<dd>
#Html.CheckBoxFor(x => x.CheckboxGroups[i].IsChecked)
#Html.HiddenFor(x => x.CheckboxGroups[i].ID)
#Html.HiddenFor(x => x.CheckboxGroups[i].Display)
#Html.DisplayFor(x => x.CheckboxGroups[i].Display)
</dd>
}
<dt>
#Html.DisplayNameFor(model => model.CheckboxUsers)
</dt>
#for (int i = 0; i < Model.CheckboxUsers.Count; i++)
{
<dd>
#Html.CheckBoxFor(x => x.CheckboxUsers[i].IsChecked)
#Html.HiddenFor(x => x.CheckboxUsers[i].ID)
#Html.HiddenFor(x => x.CheckboxUsers[i].Display)
#Html.DisplayFor(x => x.CheckboxUsers[i].Display)
</dd>
}
<dt>
#Html.DisplayNameFor(model => model.CheckboxTags)
</dt>
#for (int i = 0; i < Model.CheckboxTags.Count; i++)
{
<dd>
#Html.CheckBoxFor(x => x.CheckboxTags[i].IsChecked)
#Html.HiddenFor(x => x.CheckboxTags[i].ID)
#Html.HiddenFor(x => x.CheckboxTags[i].Display)
#Html.DisplayFor(x => x.CheckboxTags[i].Display)
</dd>
}
</dl>
</div>
<div class="form-group">
#Html.Label("File", new { #class = "control-label col-md-2" })
<div class="col-md-10">
<input type="file" id="File" name="upload" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Ladda upp fil" class="btn btn-default" />
</div>
</div>
<div class="form-group">
<div class="col-md-10">
#if (Model.Files.Count > 0)
{
<h3>#Html.DisplayNameFor(model => model.Files)</h3>
<table class="table table-striped table-bordered">
<tr>
<th>Filename</th>
<th>Filetype</th>
<th>Size(bytes)</th>
</tr>
#foreach (var file in Model.Files)
{
<tr>
<td>#file.Filename</td>
<td>#file.ContentType</td>
<td>#file.Size</td>
</tr>
}
</table>
}
</div>
</div>
<div class="form-group">
<div class="col-md-offset-1 col-md-10">
<input type="submit" value="Skapa" class="btn btn-default" />
</div>
</div>
</div>
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Controller
[HttpPost]
public ActionResult CreateFile(CreateDocumentViewModel viewModel)
{
var file = repo.CreateFile(upload, GetCurrentUser());
viewModel.Files.Add(file);
TempData["viewModel"] = viewModel;
//return RedirectToAction("CreateDocument", file);
return RedirectToAction("CreateDocument", new { files = viewModel.Files });
}
-EDIT-
I have also tried adding editorfields for every property in every file like this:
#for (int i = 0; i < Model.Files.Count; i++)
{
<div>
#Html.LabelFor(x => x.Files[i].DocumentID)
#Html.EditorFor(x => x.Files[i].DocumentID)
#Html.ValidationMessageFor(x => x.Files[i].DocumentID)
#Html.LabelFor(x => x.Files[i].CreatorID)
#Html.EditorFor(x => x.Files[i].CreatorID)
#Html.ValidationMessageFor(x => x.Files[i].CreatorID)
#Html.LabelFor(x => x.Files[i].ID)
#Html.EditorFor(x => x.Files[i].ID)
#Html.ValidationMessageFor(x => x.Files[i].ID)
#Html.LabelFor(x => x.Files[i].ContentType)
#Html.EditorFor(x => x.Files[i].ContentType)
#Html.ValidationMessageFor(x => x.Files[i].ContentType)
#Html.LabelFor(x => x.Files[i].CreatorID)
#Html.EditorFor(x => x.Files[i].CreatorID)
#Html.ValidationMessageFor(x => x.Files[i].CreatorID)
#Html.LabelFor(x => x.Files[i].Data)
#Html.EditorFor(x => x.Files[i].Data)
#Html.ValidationMessageFor(x => x.Files[i].Data)
#Html.LabelFor(x => x.Files[i].Filename)
#Html.EditorFor(x => x.Files[i].Filename)
#Html.ValidationMessageFor(x => x.Files[i].Filename)
#Html.LabelFor(x => x.Files[i].Size)
#Html.EditorFor(x => x.Files[i].Size)
#Html.ValidationMessageFor(x => x.Files[i].Size)
</div>
}

Your files are of type ICollection thus each file information needs to be present within the form to be able to be posted to the controller in the populated viewmodel. Currently you have no fields for this collection and such the ICollection is empty when posted to the controller. You can create an custom EditorFor to display the File data as well as post this data to the controller.
Please refer to this tutorial on how to do this:
http://blog.learningtree.com/en/editing-collections-in-asp-net-mvc/
In your code you are doing something similar for the checkboxes:
#for (int i = 0; i < Model.CheckboxUsers.Count; i++)
{
<dd>
#Html.CheckBoxFor(x => x.CheckboxUsers[i].IsChecked)
#Html.HiddenFor(x => x.CheckboxUsers[i].ID)
#Html.HiddenFor(x => x.CheckboxUsers[i].Display)
#Html.DisplayFor(x => x.CheckboxUsers[i].Display)
</dd>
}
Note the hidden fields, this is what is passing the data to the controller for the list of checkboxes. I would reccomend creating a custom EditorFor for the files. Or more simply repopulate the collection of files again in your controller from the model.

But the Files collection is not in any inputs. They are basically just labels, therefore they will not post to server. Since they do not change by user, you can just load the files from DB again in your action.

Related

Need ASP.NET MVC create view page to change information provided by user to foreign key values

I am working on creating a website that accesses a table in SQL Management Server. The user needs to be able to view, edit, delete, and create new entries within the table using my site. The table that is being used is called DeviceUnderTest and it has these columns: DeviceUnderTest (pk), DeviceUnderTest (nk), Notes (nk), FaultApplication (fk), Firmware (fk), Hardware (fk), Power (fk), Location (fk), PreEventTime(nk), HandleRate (nk). So far, I have created the model that connects to the main DeviceUnderTest table, as well as the other tables that the foreign key columns reference. I have created the controller, as well as an index, details, delete, and create view page. My index view is a table, and I figured out how to replace all the foreign key values with their corresponding tables from the tables they reference. The one thing I can not figure out is the create page. Users will enter the information in the form of the values that the fk columns reference, rather than the foreign key values themselves. I can not figure out how to change the user input back to the correct foreign key value so that the entry can be correctly added to the table noth in the sql server as well as within my site. If anyone has any suggestions, they are much appreciated.
Controller Code:
// GET: Circuit/Create
public ActionResult Create()
{
return View();
}
// POST: Circuit/Create
[HttpPost]
public ActionResult Create(DeviceUnderTest device)
{
try
{
dbModel.DeviceUnderTests.Add(device);
dbModel.SaveChanges();
// TODO: Add insert logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}
Create View Code:
#model Template_Website.Models.DeviceUnderTest
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>DeviceUnderTest</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.pkDeviceUnderTest, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.pkDeviceUnderTest, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.pkDeviceUnderTest, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.nkDeviceUnderTest, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.nkDeviceUnderTest, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.nkDeviceUnderTest, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.nkNotes, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.nkNotes, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.nkNotes, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.FaultApplication.nkFaultApplication, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.FaultApplication.nkFaultApplication, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.FaultApplication.nkFaultApplication, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Firmware.nkFirmware, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Firmware.nkFirmware, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Firmware.nkFirmware, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Hardware.nkHardware, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Hardware.nkHardware, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Hardware.nkHardware, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.fkPower, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.fkPower, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.fkPower, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Location.nkLocation, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Location.nkLocation, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Location, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.nkPreEventTime, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.nkPreEventTime, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.nkPreEventTime, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.nkHandleRating, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.nkHandleRating, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.nkHandleRating, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<<button type="submit" class="btn btn-default " value="Create" onclick="return confirm('Are you sure?')">Create</button>
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Index View Code:
#model IEnumerable<Template_Website.Models.DeviceUnderTest>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
</th>
<th>
</th>
<th>
</th>
<th>
</th>
<th>
#Html.DisplayNameFor(model => model.pkDeviceUnderTest)
</th>
<th>
#Html.DisplayNameFor(model => model.nkDeviceUnderTest)
</th>
<th>
#Html.DisplayNameFor(model => model.nkNotes)
</th>
<th>
#Html.DisplayNameFor(model => model.FaultApplication.nkFaultApplication)
</th>
<th>
#Html.DisplayNameFor(model => model.Firmware.nkFirmware)
</th>
<th>
#Html.DisplayNameFor(model => model.Hardware.nkHardware)
</th>
<th>
#Html.DisplayNameFor(model => model.fkPower)
</th>
<th>
#Html.DisplayNameFor(model => model.Location.nkLocation)
</th>
<th>
#Html.DisplayNameFor(model => model.nkPreEventTime)
</th>
<th>
#Html.DisplayNameFor(model => model.nkHandleRating)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.CheckBoxFor(modelItem => item.Selected)
</td>
<td>
<input type="button" value="Edit" class="btn btn-default" onclick="#("window.location.href='" + #Url.Action("Edit", "Circuit", new { id = item.pkDeviceUnderTest } ) + "'");" />
</td>
<td>
<input type="button" value="Details" class="btn btn-default" onclick="#("window.location.href='" + #Url.Action("Details", "Circuit", new { id = item.pkDeviceUnderTest }) + "'");" />
</td>
<td>
<input type="button" value="Delete" class="btn btn-danger" onclick="#("window.location.href='" + #Url.Action("Delete", "Circuit", new { id = item.pkDeviceUnderTest }) + "'");" />
</td>
<td>
#Html.DisplayFor(modelItem => item.pkDeviceUnderTest)
</td>
<td>
#Html.DisplayFor(modelItem => item.nkDeviceUnderTest)
</td>
<td>
#Html.DisplayFor(modelItem => item.nkNotes)
</td>
<td>
#Html.DisplayFor(modelItem => item.FaultApplication.nkFaultApplication)
</td>
<td>
#Html.DisplayFor(modelItem => item.Firmware.nkFirmware)
</td>
<td>
#Html.DisplayFor(modelItem => item.Hardware.nkHardware)
</td>
<td>
#Html.DisplayFor(modelItem => item.fkPower)
</td>
<td>
#Html.DisplayFor(modelItem => item.Location.nkLocation)
</td>
<td>
#Html.DisplayFor(modelItem => item.nkPreEventTime)
</td>
<td>
#Html.DisplayFor(modelItem => item.nkHandleRating)
</td>
</tr>
}
</table>
DeviceUnderTest Model Code:
namespace Template_Website.Models
{
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
public partial class DeviceUnderTest
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public DeviceUnderTest()
{
this.TestRecords = new HashSet<TestRecord>();
}
public bool Selected { get; set; }
[Required]
public int pkDeviceUnderTest { get; set; }
[Required]
public string nkDeviceUnderTest { get; set; }
[Required]
public string nkNotes { get; set; }
public Nullable<int> fkFaultApplication { get; set; }
public Nullable<int> fkFirmware { get; set; }
public Nullable<int> fkHardware { get; set; }
public Nullable<int> fkPower { get; set; }
public Nullable<int> fkLocation { get; set; }
public Nullable<int> nkPreEventTime { get; set; }
public Nullable<int> nkHandleRating { get; set; }
public virtual FaultApplication FaultApplication { get; set; }
public virtual Firmware Firmware { get; set; }
public virtual Hardware Hardware { get; set; }
public virtual Location Location { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<TestRecord> TestRecords { get; set; }
}
public class DeviceUnderTestModel
{
public List<DeviceUnderTest> device { get; set; }
}
}
Hardware Model Code:
namespace Template_Website.Models
{
using System;
using System.Collections.Generic;
public partial class Hardware
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Hardware()
{
this.DeviceUnderTests = new HashSet<DeviceUnderTest>();
}
public int pkHardware { get; set; }
public string nkHardware { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<DeviceUnderTest> DeviceUnderTests { get; set; }
}
}
Users will enter the information in the form of the values that the fk columns reference, rather than the foreign key values themselves.
Why do you want to avoid using the foreign key values? I notice you are using EditorFor but can't you simply use DropdownFor or even a JavaScript-powered autocomplete widget? This way, your users would enter the desired text but the values would be mapped in the background.
For an example of how to use DropdownFor, please see the MSDN docs: https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/working-with-the-dropdownlist-box-and-jquery/examining-how-aspnet-mvc-scaffolds-the-dropdownlist-helper
For an example of using an autocomplete, there is a nice answer here which uses jQuery UI: Autocomplete dropdown in MVC5?
Using the first example for DropdownFor and applying it to your code, one of your model properties would continue to look the same:
public Nullable<int> fkFaultApplication { get; set; }
While you also create a ViewBag item to contain your SelectList which maps your texts and values:
ViewBag.FaultApplicationId = new SelectList(db.FaultApplications, "FaultAppId", "Name");
Finally, you could make use of these properties in your view as follows:
#Html.DropDownList("FaultApplicationId", String.Empty)
Note that the second parameter, String.Empty, "is the text to display when no item is selected."
Just like user1477388 says, you can use dropdowns for each item. This way the view displays the text, but the value sent back is the id.
For example using hardware. I'm using a viewmodel so we dont present the database models to the view. The VM has the properties you want in the specific view. In this case we have the Device Under Test stuff, a select list and a HardwareId.
public class DeviceUnderTestViewModel
{
public int pkDeviceUnderTest { get; set; }
[Required]
public string nkDeviceUnderTest { get; set; }
[Required]
public string nkNotes { get; set; }
public int HardwareId { get; set; } //pkHardware
public IEnumerable<SelectListItem> HardwareSelectList { get; set; } //dropdown of Hardware
}
In the controller, populate the select lists and attach to the viewmodel:
public ActionResult Index()
{
DeviceUnderTestViewModel vm = new DeviceUnderTestViewModel();
vm.HardwareSelectList = db.Hardware.ToList().Select(d => new SelectListItem()
{
Text = d.nkHardware,
Value = d.pkHardware.ToString()
});
// populate all the other dropdowns
return View();
}
In the view we render the select list:
<div class="form-group">
#Html.LabelFor(model => model.HardwareId)
#Html.DropDownListFor(model => model.HardwareIdId, Model.HardwareSelectList, "Select Hardware...", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.HardwareIdId, "", new { #class = "text-danger" })
</div>
DropDownListFor takes the value to set first, here we are setting the hardwareid which we will save to the DeviceUnderTest as the pkHardware, then we have the populate list of Hard items for the dropdown, third is an optional string place holder, then lastly I add a bootstrap class for formatting.
Then return the model to the controller and save it:
public ActionResult SaveDevice(DeviceUnderTestViewModel model)
{
using (var db = new dbContext())
{
DeviceUnderTest device = new DeviceUnderTest()
{
pkHardware = model.HardwareId,
// continue with other items
};
db.Entry<device>.State = EntiyState.Added;
db.SaveChanges();
}
return View();
}

MVC dynamic data passing to controller

I am creating a MVC application. I am having a view with a form to fill, but it seems that controller does not get data that was entered (it is null when debugging).
My controller:
public ActionResult AddGroupsQty(AddGroupsQtyViewModel value)
{
var model = new AddGroupsQtyViewModel();
model.subject_id = value.subject_id;
model.qty = value.qty;
ClassDeclarationsDBEntities1 entities1=new ClassDeclarationsDBEntities1();
var subj = entities1.Subjects
.Where(b => b.class_id == model.subject_id)
.FirstOrDefault();
model.subject_name = subj.name;
if (ModelState.IsValid)
{
int maxId = 0;
int total = 0;
total = entities1.Groups.Count();
if (total == 0)
{
maxId = 0;
}
else
{
maxId = entities1.Groups.Max(u => u.group_id);
}
for (int i = 0; i < value.qty; i++)
{
var teacher = entities1.Users
.Where(b => b.email.Replace(" ", String.Empty) == model.teacher_emails[i].Replace(" ", String.Empty))
.FirstOrDefault();
var group=new Models.Group(value.subject_id, maxId+1, model.group_names[i], teacher.user_id);
entities1.Groups.Add(group);
entities1.SaveChangesAsync();
}
return RedirectToAction("OperationSuccess", "Account");
}
return View(model);
}
My View model:
public class AddGroupsQtyViewModel
{
public int qty { get; set; }
public int subject_id { get; set; }
public string subject_name { get; set; }
[Required]
[Display(Name = "Name of group")]
public List<string> group_names { get; set; }
[Required]
[Display(Name = "Email of teacher")]
public List<string> teacher_emails { get; set; }
}
and the View:
#using System.IdentityModel.Configuration
#using System.Web.UI.WebControls
#model ClassDeclarationsThsesis.Models.AddGroupsQtyViewModel
#{
ViewBag.Title = "AddGroupsQty";
}
<h2>Add Groups to #Model.subject_name</h2>
#if (Model != null)
{
using (Html.BeginForm("AddGroupsQty", "Account", new { qty = Model.qty, Model.subject_id }, FormMethod.Post, new { #class = "form-horizontal", role = "form", }))
{
#Html.AntiForgeryToken()
<h4>Insert data</h4>
<hr />
<table>
<tr>
<th>
#Html.ValidationSummary("", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.group_names, new { #class = "col-md-2 control-label" })
</div>
</th>
<th>
#Html.ValidationSummary("", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.teacher_emails, new { #class = "col-md-2 control-label" })
</div>
</th>
</tr>
#for (int i = 0; i < Model.qty; i++)
{
<tr>
<th>
<div class="form-group">
<div class="col-md-10">
#Html.TextBoxFor(m => m.group_names[i], new { #class = "form-control" })
</div>
</div>
</th>
<th>
<div class="form-group">
<div class="col-md-10">
#Html.TextBoxFor(m => m.teacher_emails[i], new { #class = "form-control" })
</div>
</div>
</th>
</tr>
}
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Submit" />
</div>
</div>
}
}
The problem is that when running the application, filling the form and submitting by pressing the button, it throws a null exception in controller that both lists:
List teacher_emails
and:
List group_names
are null. I do not see any error in my form. How can I solve it?
model.teacher_emails and model.group_names are always null, because model is new.
var model = new AddGroupsQtyViewModel();
Try using value.teacher_emails and value.group_names instead.
Also tidy that code up. A blank line after the variable declaration, and you would have spotted it yourself.

How to accepts list of objects mvc create view

I got two model classes like this
public class JobViewModel
{
public int Id { get; set; }
public float Price { get; set; }
public int JobSubCategoryId { get; set; }
public string jobDescription { get; set; }
public List<Machine> Machines { get; set; }
public int SpecialRequirementId { get; set; }
}
public class Machine
{
public int Id { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public string Brand { get; set; }
}
Now, When a user creates a new "Job" he must be able to create multiple "Machine" with it. How can I make this possible inside my create view?
As well as Stephen's method, you can achieve all this relatively simply, without partial views.
In the first instance, you should revise your model a little. Add to Machine:
// Foreign key
public int JobID { get; set; }
// Navigation properties
public virtual Job Job{ get; set; }
The Job model, you have not shown, but it needs to be:
public class Job
{
public int Id { get; set; }
public float Price { get; set; }
public int JobSubCategoryId { get; set; }
public string JobDescription { get; set; }
public int SpecialRequirementId { get; set; }
public virtual List<Machine> Machines { get; set; }
}
Here is my complete JobViewModel:
public class JobViewModel
{
public JobViewModel()
{
Machines = new List<Machine>();
}
public int Id { get; set; }
public float Price { get; set; }
public int JobSubCategoryId { get; set; }
public string JobDescription { get; set; }
public int SpecialRequirementId { get; set; }
public List<Machine> Machines { get; set; }
public string NewMachineBrand { get; set; }
public string NewMachineType { get; set; }
public string NewMachineName { get; set; }
public void AddMachine()
{
Machine tmp = new Machine { Brand = NewMachineBrand, Type = NewMachineType, Name = NewMachineName };
Machines.Add(tmp);
NewMachineBrand = NewMachineType = NewMachineName = null;
}
public Job GetJob()
{
Job job = new Job();
job.JobDescription = JobDescription;
job.Price = Price;
job.JobSubCategoryId = JobSubCategoryId;
job.SpecialRequirementId = SpecialRequirementId;
job.Machines = new List<Machine>();
foreach (Machine m in Machines)
{
job.Machines.Add(m);
}
return job;
}
}
When creating your create view based on JobViewModel, you will need to add two things that are not defaulted for you, firstly a table to hold the new Machines, and secondly a button to add each machine in turn.
My complete create.cshtml view looks like this:
#model JobMachinesMVC.Models.JobViewModel
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Job</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Price, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Price, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Price, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.JobSubCategoryId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.JobSubCategoryId, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.JobSubCategoryId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.JobDescription, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.JobDescription, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.JobDescription, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.SpecialRequirementId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.SpecialRequirementId, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.SpecialRequirementId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.NewMachineBrand, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.NewMachineBrand, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.NewMachineBrand, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.NewMachineType, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.NewMachineType, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.NewMachineType, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.NewMachineName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.NewMachineName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.NewMachineName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<table>
<thead>
<tr>
<th style="text-align:right">
#Html.DisplayNameFor(model => model.Machines.FirstOrDefault().Brand)
</th>
<th style="text-align:right">
#Html.DisplayNameFor(model => model.Machines.FirstOrDefault().Name)
</th>
<th style="text-align:right">
#Html.DisplayNameFor(model => model.Machines.FirstOrDefault().Type)
</th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.Machines.Count; i++)
{
<tr>
<td style="text-align:right">#Html.HiddenFor(m => m.Machines[i].Id)#Html.DisplayFor(m => m.Machines[i].Brand)#Html.HiddenFor(m => m.Machines[i].Brand)</td>
<td style="text-align:right">#Html.DisplayFor(m => m.Machines[i].Name)#Html.HiddenFor(m => m.Machines[i].Name)</td>
<td style="text-align:right">#Html.DisplayFor(m => m.Machines[i].Type)#Html.HiddenFor(m => m.Machines[i].Type)</td>
</tr>
}
</tbody>
</table>
</div>
<div class="form-group">
<input type="submit" value="Add Machine" name="addmachine" class="btn btn-default" />
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
A couple of things to note here. I always include a #Html.HiddenFor in such a sub-table, because #Html.DisplayFor items can be lost when posting back to the controller. Secondly, there are two input type="submit" on the same View. One is given a name attribute. This is so that the Controller can distinguish between the two clicks.
The relevant lines from my controller are these:
// GET: Jobs/Create
public ActionResult Create()
{
JobViewModel job = new JobViewModel();
return View(job);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(JobViewModel jobvm)
{
if (Request.Form["addmachine"] != null)
{
jobvm.AddMachine();
ModelState.Remove("NewMachineName");
ModelState.Remove("NewMachineType");
ModelState.Remove("NewMachineBrand");
return View(jobvm);
}
if (ModelState.IsValid)
{
Job job = jobvm.GetJob();
db.Jobs.Add(job);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(jobvm);
}
If "addmachine" is clicked, the new machine values get added to the Machines List, get reset and the form is redisplayed. Note you need to set the ModelState even though the ViewModel sets the values to null, otherwise you old values persist in the view. If Create is clicked, the model is checked for ValidState, and the job is saved. What about the Machine table? Because the models are set up as outlined above, internally MVC knows that it has to save the values to Machine as well.
Please note that the above illustration is very crude. I have applied no styling apart from that which you get "out of the box". You will want to tidy this up (a lot!), but I hope I have given you a good start in one way to approach this problem.

MVC 5 Razor - Label isn't updated from class annotation

I have a simple example.
Two class. User and Company like :
public class User() {
public int UserID { get; set; }
[Display(Name = "User name")]
public string Name { get; set; }
[Display(Name = "Company")]
public int CompanyID { get; set; }
public virtual Company Company { get; set; }
}
public class Company() {
public int CompanyID { get; set; }
[Display(Name = "Company")]
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
}
My problem is in the Create and the Edit views of the User.
The label for "Name" is displayed correctly in "User name" but the label for "CompanyID" stay displayed at "CompanyID" (the drop down list is created correctly with all Companies). I want the label display "Company" like I make it in the class.
I've try to change my view but all I do block compilation so I'm lost.
I'm begginer in MVC so excuse me if it easy to do but I don't see it.
Edit (add Create View code) :
#model Intranet3.Models.User
#{
ViewBag.Title = "Add a user";
}
#using (Html.BeginForm(null, null, FormMethod.Post, htmlAttributes: new { #class = "form-horizontal form-bordered" })) {
#Html.AntiForgeryToken()
<div class="row-fluid">
<div class="span12">
<div class="box">
<div class="box-title">
<h3>
<i class="icon-table"></i>
New
</h3>
</div>
<div class="box-content nopadding">
<div class="form-horizontal">
#Html.MyValidationSummary()
<div class="control-group #Html.ClassErrorFor(model => model.Name)">
#Html.LabelFor(model => model.Name, new { #class = "control-label" })
<div class="controls">
#Html.EditorFor(model => model.Name)
#Html.MyValidationMessageFor(model => model.Name)
</div>
</div>
<div class="control-group #Html.ClassErrorFor(model => model.CompanyID)">
#Html.LabelFor(model => model.CompanyID, "CompanyID", new { #class = "control-label" })
<div class="controls">
#Html.DropDownList("CompanyID", String.Empty)
#Html.MyValidationMessageFor(model => model.CompanyID)
</div>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">Create</button>
<button onclick="location.href='#Url.Action("Index","User")'" type="button" class="btn">Cancel</button>
</div>
</div>
</div>
</div>
</div>
</div>
}
Edit 2 :
Problem solved by delete the string force in Labels.
So this :
#Html.LabelFor(model => model.CompanyID, "CompanyID", new { #class = "control-label" })
Need to be
#Html.LabelFor(model => model.CompanyID, new { #class = "control-label" })
Why have u passed parameter CompanyId
#Html.LabelFor(model => model.CompanyID, "CompanyID", new { #class = "control-label" })
Should be
#Html.LabelFor(model => model.CompanyID, new { #class = "control-label" })
#Html.TextBoxFor(c => c.CompanyID, new { data_bind = "text:Contacts.FirstName", #class = "form-control" })
If you have knockoutjs binding

File is not posted to ActionResult. How to upload files on MVC c#?

How to get the data from the file fields?
My class Movie:
public class Movie
{
public int ID { get; set; }
[Display(Name = "Movie Title")]
[Required(ErrorMessage = "The Title Field Is Required.")]
public string Title { get; set; }
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
[Display(Name = "Release Date")]
public DateTime ReleaseDate { get; set; }
[Required(ErrorMessage = "The Genere Field Is Required.")]
public string Genre { get; set; }
[DisplayFormat(DataFormatString = "{0:F3}")]
public decimal Price { get; set; }
public List<Image> Images = new List<Image>();
public List<File> Files = new List<File>();
public List<Link> Links = new List<Link>();
public Movie()
{
ID = 0;
Price = 0;
Title = "movie";
Genre = "דרמה";
ReleaseDate = DateTime.Now;
var image1 = new Image
{
ID = 0,
FileName = ""
};
var image2 = new Image
{
ID = 0,
FileName = ""
};
var image3 = new Image
{
ID = 0,
FileName = ""
};
Images.Add(image1);
Images.Add(image2);
Images.Add(image3);
}
}
I have an editor template for Image:
#model BermanCRM.Models.Image
<div class="fl">
<h3>
Image</h3>
<p>
#Html.LabelFor(x => x.FileName)
#Html.TextBoxFor(x => x.FileName, new { type = "file" })
</p>
<p>
#Html.LabelFor(x => x.Order)
#Html.EditorFor(x => x.Order)
</p>
</div>
My movie create view:
#model BermanCRM.Models.Movie
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm("Create", "Movies", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Movie</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Title)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Title)
#Html.ValidationMessageFor(model => model.Title)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ReleaseDate)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ReleaseDate)
#Html.ValidationMessageFor(model => model.ReleaseDate)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Genre)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Genre, new SelectList(#ViewBag.Generelist, "Text", "Value"), "--select--")
#Html.ValidationMessageFor(model => model.Genre)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Price)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Price)
#Html.ValidationMessageFor(model => model.Price)
</div>
<h2>Images</h2>
#Html.EditorFor(model => model.Images)
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
My Controller:
public ActionResult Create()
{
ViewBag.Generelist = listGeners;
return View(new Movie());
}
//
// POST: /Movies/Create
[HttpPost]
public ActionResult Create(Movie movie, IEnumerable<HttpPostedFileBase> files)
{
try
{
movie.Insert();
foreach (HttpPostedFileBase file in files)
{
file.SaveAs(Path.Combine(Server.MapPath("~/Upload"), Path.GetFileName(file.FileName)));
}
return RedirectToAction("Index");
}
catch
{
return View();
}
}
By looking your code the Action Result HttpPostedFileBase declaration name "files" and File upload control name is not same.
<input id="files" type="file" name="files" />
IEnumerable<HttpPostedFileBase> files
name="files" and HttpPostedFileBase files Must be same...
Keep the same name.. it will work.,

Categories