I would like to use generate IDs depending on the models variable name.
I have two models
public class ConceptViewModel
{
[AllowHtml]
public string Content { get; set; }
[AllowHtml]
public string EnglishContent { get; set; }
public string Instructions { get; set; }
}
and
public class InputDataCollectionViewModel
{
public int Id { get; set; }
public ConceptModel Definition { get; set; }
public ConceptModel Publication { get; set; }
public bool Exist { get; set; }
}
I'm using EditorTemplates for ConceptViewModel:
#model Models.ConceptViewModel
<tr>
<td>#Html.LabelFor(m => m, htmlAttributes: new { #class = "control-label" })</td>
<td hidden>
#Html.TextBoxFor(m => Content, (#Model.Exist ? null : (object)new { #disabled = "disabled" }))
#Html.TextBoxFor(m => m.EnglishContent)
</td>
<td hidden>#Html.TextBoxFor(m => m.Instructions)</td>
<td><button type="button" class="btn btn-default" onclick="openEditor('UNIQUE_NAME_DEPENDING_ON_MODEL_NAME');">Edit</button></td>
</tr>
and the view:
#model Models.InputDataCollectionViewModel
<div class="container">
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
{
<div class="form-group">
#Html.HiddenFor(model => model.Id)
<table class="table table-hover table-condensed">
<tbody>
#Html.EditorFor(model => model.Definition)
#Html.EditorFor(model => model.Publication)
</tbody>
<tfoot></tfoot>
</table>
<div class="row">
<div class="col-md-12">
<button class="btn btn-default" type="submit">Save</button>
</div>
</div>
</div>
}
}
which generates html containing:
<label class="control-label" for="Definition">
<input id="Definition_Content" name="Definition.Content" type="text" value="" />
<input id="Definition_EnglishContent" name="Definition.EnglishContent" type="text" value="" />
<input id="Definition_Instructions" name="Definition.Instructions" type="text" value="..." />
and the same for the other ConceptViewModels. So from this I can easily reference it because of the unique IDs (Definition_, Publication_ and so forth for other inputs I have elsewhere).
Now my question is, how would I get the strings containg "Definition", "Publication" so I could use it with the generated buttons. What I want is:
openEditor('Definition');
openEditor('Publication');
in the onclick.
I've managed to find the solution. The Html helper #Html.IdFor(m => m)
Related
I have this thing running at my application.
I have 3 tables, 1 main table and 2 other tables that are needed to create the main table, the main table depends on the 2 tables, they are connected with constraints.
I have the main table: Word
Then I have the two other tables: Difficulties and Category.
I created the Difficulty: Easy and then I created the Category: Nature.
So now, I would be able to create as many words as I could with those 2 attributes, but it gives me an error.
I can only create a word if I create this way
Dificulty -> Category -> Word
or
Category -> Difficulty -> Word
.
I can't create a word without making that path and I don't know why.
The values are stored in a database that will be called in 2 ComboBoxes, 1 for the Difficulty and the other one for Category.
If I wanted to create a word I would need to create a new category and a new difficulty otherwise it will return as null.
This is my Model View:
public partial class palavra
{
public int id_pal { get; set; }
public Nullable<int> LETRAS { get; set; }
public string PISTA { get; set; }
public int id_cat { get; set; }
public int id_dificuldade { get; set; }
public string nomepalavra { get; set; }
public virtual categoria categoria { get; set; }
public virtual dificuldade dificuldade { get; set; }
}
This is my Controller:
public ActionResult Index()
{
var palavras = db.palavras.Include(p => p.categoria).Include(p => p.dificuldade);
return View(palavras.ToList());
}
// GET: palavras/Create
public ActionResult Create()
{
ViewBag.id_cat = new SelectList(db.categorias, "id_cat", "TIPO_CATEGORIA");
ViewBag.id_dificuldade = new SelectList(db.dificuldades, "id_dificuldade", "TIPO_DIFICULDADE");
return View();
}
// POST: palavras/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_pal,LETRAS,PISTA,id_cat,id_dificuldade,nomepalavra")] palavra palavra)
{
if (ModelState.IsValid)
{
db.palavras.Add(palavra);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.id_cat = new SelectList(db.categorias, "id_cat", "TIPO_CATEGORIA", palavra.id_cat);
ViewBag.id_dificuldade = new SelectList(db.dificuldades, "id_dificuldade", "TIPO_DIFICULDADE", palavra.id_dificuldade);
return View(palavra);
}
This is my view:
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.nomepalavra)
</th>
<th>
#Html.DisplayNameFor(model => model.LETRAS)
</th>
<th>
#Html.DisplayNameFor(model => model.PISTA)
</th>
<th>
#Html.DisplayNameFor(model => model.categoria.TIPO_CATEGORIA)
</th>
<th>
#Html.DisplayNameFor(model => model.dificuldade.TIPO_DIFICULDADE)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.nomepalavra)
</td>
<td>
#Html.DisplayFor(modelItem => item.LETRAS)
</td>
<td>
#Html.DisplayFor(modelItem => item.PISTA)
</td>
<td>
#Html.DisplayFor(modelItem => item.categoria.TIPO_CATEGORIA)
</td>
<td>
#Html.DisplayFor(modelItem => item.dificuldade.TIPO_DIFICULDADE)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.id_pal }) |
#Html.ActionLink("Details", "Details", new { id=item.id_pal }) |
#Html.ActionLink("Delete", "Delete", new { id=item.id_pal })
</td>
</tr>
}
Whenever I run my code to insert I get this error:
An exception of type
'System.Data.Entity.Infrastructure.DbUpdateException' occurred in
EntityFramework.dll but was not handled in user code
Additional information: An error occurred while updating the entries.
See the inner exception for details.
When I check the Details it appears that the "Category" table and the "Difficulty" table are null but the others fields are all with the info that I gave them.
My Database Schema is the following:
Category connects to Word and Difficulty connects to Word.
Both Category and Difficulty are a 1 to many relationship with the
table Word.
if you are use entity framework you need to defined your constraint at your model there is 2 options, code first or database first
here my example with database first
Database First
after you generate your model from db, easy step to create create update delete with scaffolding
Scaffolding crud
with scaffolding you much easier to create crud, and if any constraint
example above you create work with selecting Difficulty / Category yes your dropdownlist will be created automatically
example my codes menu creator controller
// GET: SystemMenus/Create
public IActionResult Create()
{
ViewData["ParentId"] = new SelectList(_context.SystemMenus, "Id", "Name");
ViewData["Color"] = new SelectList(OptionDropdown.GetBackgroundColor(), "Value", "Text");
ViewData["Size"] = new SelectList(OptionDropdown.GetSize(), "Value", "Text");
ViewData["Module"] = new SelectList(OptionDropdown.GetModule(), "Value", "Text");
return View();
}
// POST: SystemMenus/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 async Task<IActionResult> Create([Bind("Id,Name,Controller,Action,ParentId,Icon,Module,Description,FixHeader,Color,Size")] SystemMenu systemMenu)
{
if (ModelState.IsValid)
{
_context.Add(systemMenu);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["ParentId"] = new SelectList(_context.SystemMenus, "Id", "Name", systemMenu.ParentId);
ViewData["Color"] = new SelectList(OptionDropdown.GetBackgroundColor(), "Value", "Text", systemMenu.Color);
ViewData["Size"] = new SelectList(OptionDropdown.GetSize(), "Value", "Text", systemMenu.Size);
ViewData["Module"] = new SelectList(OptionDropdown.GetModule(), "Value", "Text", systemMenu.Module);
return View(systemMenu);
}
and here my model with recursive constraint parentid
public class SystemMenu
{
public SystemMenu()
{
Details = new HashSet<SystemMenu>();
}
[Key]
public int Id { get; set; }
[StringLength(50)]
public string Name { get; set; }
[StringLength(50)]
public string Controller { get; set; }
[StringLength(50)]
public string Action { get; set; }
[ForeignKey("ParentId")]
public SystemMenu Parent { get; set; }
[Display(Name = "Parent")]
public int? ParentId { get; set; }
[StringLength(50)]
public string Icon { get; set; }
[StringLength(50)]
public string Module { get; set; }
[StringLength(200)]
[DataType(DataType.MultilineText)]
public string Description { get; set; }
public bool FixHeader { get; set; }
[StringLength(50)]
public string Color { get; set; }
[StringLength(50)]
public string Size { get; set; }
public ICollection<SystemMenu> Details { get; set; }
}
here's my views
enter code
#model NCFramework.Models.System.SystemMenu
#{
ViewData["Title"] = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Controller" class="control-label"></label>
<input asp-for="Controller" class="form-control" />
<span asp-validation-for="Controller" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Action" class="control-label"></label>
<input asp-for="Action" class="form-control" />
<span asp-validation-for="Action" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ParentId" class="control-label"></label>
<select asp-for="ParentId" class ="form-control" asp-items="ViewBag.ParentId">
<option value="">Select</option>
</select>
</div>
<div class="form-group">
<label asp-for="Icon" class="control-label"></label>
<input asp-for="Icon" class="form-control" />
<span asp-validation-for="Icon" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Module" class="control-label"></label>
<select asp-for="Module" class="form-control" asp-items="ViewBag.Module">
<option value="">Select</option>
</select>
<span asp-validation-for="Module" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Description" class="control-label"></label>
<input asp-for="Description" class="form-control" />
<span asp-validation-for="Description" class="text-danger"></span>
</div>
<div class="form-group">
<div class="mt-checkbox-list">
<label class="mt-checkbox mt-checkbox-outline">
#Html.DisplayNameFor(model => model.FixHeader)
<input asp-for="FixHeader" class="checkbox-inline" />
<span></span>
</label>
</div>
</div>
<div class="form-group">
<label asp-for="Color" class="control-label"></label>
<select asp-for="Color" class="form-control" asp-items="ViewBag.Color">
<option value="">Select</option>
</select>
<span asp-validation-for="Color" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Size" class="control-label"></label>
<select asp-for="Size" class="form-control" asp-items="ViewBag.Size">
<option value="">Select</option>
</select>
<span asp-validation-for="Size" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
here
the example with asp.net core mvc also you can try with visual studio, these 3 files generated by scaffolding with defined model first
hoe this helps
cheers
I am quite new to ASP.NET so I am probably struggling with this more than I should be. I have two custom classes, defined as follows:
public class Job
{
public int ID { get; set; }
public Address Pickup { get; set; }
public Address Dropoff { get; set; }
public string Data { get; set; }
}
public class Address
{
public int ID { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string AddressLine3 { get; set; }
[Required]
public string City { get; set; }
public string Postcode { get; set; }
}
I am having them displayed on the Job's index page by getting all the jobs and returning them as a list, then in the Index.cshtml I am able to extract some fields for the addresses to return something meaningful:
#model IEnumerable<MvcJobs.Models.Job>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Pickup) Address
</th>
<th>
#Html.DisplayNameFor(model => model.Dropoff) Address
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model) {
<tr>
<td>
<pre>#Html.DisplayFor(modelItem => item.Pickup.AddressLine1)
#Html.DisplayFor(modelItem => item.Pickup.AddressLine2)
#Html.DisplayFor(modelItem => item.Pickup.AddressLine3)
#Html.DisplayFor(modelItem => item.Pickup.City)
#Html.DisplayFor(modelItem => item.Pickup.Postcode)</pre>
</td>
<td>
<pre>#Html.DisplayFor(modelItem => item.Dropoff.AddressLine1)
#Html.DisplayFor(modelItem => item.Dropoff.AddressLine2)
#Html.DisplayFor(modelItem => item.Dropoff.AddressLine3)
#Html.DisplayFor(modelItem => item.Dropoff.City)
#Html.DisplayFor(modelItem => item.Dropoff.Postcode)</pre>
<a asp-action="Addresses/Edit" asp-route-id="#item.Dropoff.ID">Edit</a>
</td>
<td>
<a asp-action="Edit" asp-route-id="#item.ID">Edit</a>
</td>
</tr>
}
</tbody>
</table>
This displays each job in a table with their addresses properly formatted and displayed. It wasn't difficult to figure out the generated Index.cshtml file and adjust it accordingly. Unfortunately, the generated Edit.cshtml file is very differently formatted:
#model MvcJobs.Models.Job
<form asp-action="Edit">
<div class="form-horizontal">
<h4>Job</h4>
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="ID" />
<div class="form-group">
<label asp-for="Data" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Data" class="form-control" />
<span asp-validation-for="Data" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
</form>
As the data is accessed and returned in a different way, I don't know how to go about displaying the attributes for each address, allowing them to be edited and then saving them. Any advice at all is welcome. Basically, I'd like the form to display not only the Job details but also the details for each address, and when I hit save it updates the job and both addresses associated with it.
Thanks.
I am writing a web page with MVC and Entity Framework.
I have an order with line items attached and want to return a complex object to the controller for processing.
I have now included all the code.
My view:
#model BCMManci.ViewModels.OrderCreateGroup
#{
ViewBag.Title = "Create";
}
<h2>New Order</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<h4>#Html.DisplayFor(model => model.Order.Customer.FullName)</h4>
<table>
<tr>
<td><b>Order Date:</b> #Html.DisplayFor(model => model.Order.OrderDate)</td>
<td><b>Status:</b> #Html.DisplayFor(model => model.Order.OrderStatus.OrderStatusName)</td>
</tr>
<tr>
<td colspan="2">
<b>Notes</b>
#Html.EditorFor(model => model.Order.Notes, new { htmlAttributes = new { #class = "form-control" } })
</td>
</tr>
</table>
#Html.ValidationMessageFor(model => model.Order.Notes, "", new { #class = "text-danger" })
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<table class="table table-striped table-hover">
<thead>
<tr>
<td>Name</td>
<td>Price</td>
<td>Discount</td>
<td>Total</td>
<td>Quantity</td>
</tr>
</thead>
<tbody>
#foreach (var product in Model.ProductWithPrices)
{
<tr>
<td>
#Html.DisplayFor(modelItem => product.ProductName)
</td>
<td>
#Html.DisplayFor(modelItem => product.SellingPrice)
</td>
<td>
#Html.DisplayFor(modelItem => product.DiscountPrice)
</td>
<td>
#Html.DisplayFor(modelItem => product.TotalPrice)
</td>
<td>
#Html.EditorFor(modelItem => product.Quantity, new { htmlAttributes = new { #class = "form-control" } })
</td>
</tr>
}
</tbody>
</table>
<input type="submit" value="Create" class="btn btn-default" />
}
<div class="btn btn-danger">
#Html.ActionLink("Cancel", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Order,ProductWithPrices,Order.Note,product.Quantity")] OrderCreateGroup order)
{
try
{
if (ModelState.IsValid)
{
db.Orders.Add(order.Order);
foreach (var orderItem in order.ProductWithPrices.Select(item => new OrderItem
{
OrderId = order.Order.OrderId,
ProductId = item.ProductId,
Quantity = item.Quantity,
ItemPrice = item.SellingPrice,
ItemDiscount = item.DiscountPrice,
ItemTotal = item.TotalPrice
}))
{
db.OrderItems.Add(orderItem);
}
db.SaveChanges();
return RedirectToAction("ConfirmOrder", new {id = order.Order.OrderId});
}
}
catch (DataException /* dex */)
{
//TODO: Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}
ViewBag.Products = db.Products.Where(model => model.IsActive == true);
PopulateDropdownLists();
return View(order);
}
Data Source:
public class OrderCreateGroup
{
public OrderCreateGroup()
{
ProductWithPrices = new List<ProductWithPrice>();
}
public Order Order { get; set; }
public ICollection<ProductWithPrice> ProductWithPrices { get; set; }
}
public class ProductWithPrice : Product
{
public decimal SellingPrice { get; set; }
public decimal DiscountPrice { get; set; }
public int Quantity { get; set; }
public decimal TotalPrice { get; set; }
}
However, the values that are entered on the form are not being passed, through. So I can't access them in the controller. The 'productWithPrices' collection is null although there is Data in it on the web page.
I have tried making it asyc and also tried changing the ActionLink button like below but it didn't get to the controller.
#Html.ActionLink("Create", "Create", "Orders", new { orderCreateGoup = Model }, null)
This is the controller but it now doesn't make sense as the parameter passed in the datasource for the page.
public ActionResult Create(OrderCreateGroup orderCreateGoup)
Please, can you give me direction on the best way of doing this?
In your OrderCreateGroup class initialize the collection to an empty list.
public class OrderCreateGroup
{
public OrderCreateGroup()
{
ProductWithPrices = new List<ProductWithPrice>();
}
public Order Order { get; set; }
public ICollection<ProductWithPrice> ProductWithPrices { get; set; }
}
You'll need to add #Html.HiddenFor(m => m.SellingPrice) and similarly for other bound fields that are using DisplayFor if you want to post them back to the controller.
Note: For your benefit, try to have a look at the generated HTML code when your page is rendered in the browser and see what tags are generated inside the <form> tag with a name attribute.
make sure you bind the appropriate property from the complex object, like the following:
#model BCMManci.ViewModels.OrderCreateGroup
...
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
...
<div class="form-group">
#Html.LabelFor(model => model.LastName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.OrderCreateGroup.Order.Quantity, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.OrderCreateGroup.Order.Quantity, "", new { #class = "text-danger" })
</div>
</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>
Note:model.OrderCreateGroup.Order.Quantity would be one the your order's property.
hope this helps.
I'm new to MVC and have spent several hours trying to find a solution to this issue. I'm trying to set up an invoicing system for some rental properties. The property manger provides an excel file that contains the current water readings. I have to subtract the water readings from last months to find out the monthly usage. The view I've made does this and shows all the units simultaneously. What I'm having problems with is passing the uploaded file along with the model data (last months readings) to the controller. The file shows up but none of the other data.
In the view I have two submit buttons. One to upload the file to be integrated into the model data and the other to create the new records based on the previous and current(uploaded) data. Below are the relevant model, view and controllers.
Ultimately the billing manger would see last month's data, upload the new data, review and verify there are no errors and then submit the data for the new invoices.
If there is better way to accomplish that then what I'm trying here please let me know. This just seemed like it would be easier that recreating model data with all the linq queries. Thanks in advance for your help!
Model:
public partial class UtilityData
{
public DateTime bEntryDate { get; set; }
public string bPrevDate { get; set; }
public int bID { get; set; }
//public int residenceCount { get; set; }
public IEnumerable<UtilEntry> utilData { get; set; }
public HttpPostedFileBase UploadFile { get; set; }
}
public partial class UtilEntry
{
public int rID { get; set; }
public long? WaterReading { get; set; }
public int ResNumber { get; set; }
public long? prevWaterReading { get; set; }
public decimal wDifference { get; set; }
public int GrnUpper { get; set; }
public int GrnLower { get; set; }
public int YelUpper { get; set; }
public int YelLower { get; set; }
}
View:
#model PropertiesAdminSite.Models.UtilityData
#{
ViewBag.Title = "CreateNewCycle";
}
<h2>New Residence Utilities</h2>
#using (Html.BeginForm("Upload", "ImportWater", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="control-group">
#Html.TextBoxFor(m => m.UploadFile, new { type = "file"})
#*<input type="file" class="btn btn-info" name="postedFile"/>*#
</div>
<div class="control-group">
<input type="submit" class="btn btn-info" value="Upload" />
</div>
<div class="col-lg-12 visible-lg">
<br>
<span style="color:green">#ViewBag.Message</span>
</div>
}
#using (Html.BeginForm("IndexMulti", "Utilities", FormMethod.Post))
{
#Html.AntiForgeryToken()
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="row">
<div class="col-lg-12">
<div class="panel panel-default">
<div class="panel-heading">
#Html.LabelFor(model => model.bEntryDate, htmlAttributes: new { #class = "control-label col-md-1" })
#Html.DisplayFor(model => model.bEntryDate)
</div>
<!-- /.panel-heading -->
<div class="panel-body">
<div class="dataTable_wrapper">
<!--div id="dataTables-example_wrapper" class="dataTables_wrapper form-inline dt-bootstrap no-footer">-->
<div class="row">
<div class="col-sm-12">
<table class="table table-striped table-bordered table-hover dataTable no-footer" id="dataTables-Bills" role="grid" aria-describedby="dataTables-example_info">
<!-- /table headers-->
<thead>
<tr role="row">
<th>#Html.DisplayNameFor(model => model.utilData.First().ResNumber)</th>
<th>#Html.DisplayNameFor(model => model.utilData.First().WaterReading)</th>
<th>
#Html.DisplayNameFor(model => model.utilData.First().prevWaterReading)
#* TODO: fix date format *#
#Html.DisplayFor(model => model.bPrevDate)
</th>
<th>#Html.DisplayNameFor(model => model.utilData.First().wDifference)</th>
<th>Actions</th>
</tr>
</thead>
<!-- /table body-->
<tbody>
#foreach (var item in Model.utilData)
{
<tr role="row">
<td>
#Html.DisplayFor(modelItem => item.ResNumber, null, "residence_" + item.rID)
#Html.HiddenFor(model => item.GrnLower, new { id = "grnLower_" + item.rID })
#Html.HiddenFor(model => item.GrnUpper, new { id = "grnUpper_" + item.rID })
#Html.HiddenFor(model => item.YelLower, new { id = "yelLower_" + item.rID })
#Html.HiddenFor(model => item.YelUpper, new { id = "yelUpper_" + item.rID })
</td>
<td>
#Html.EditorFor(model => item.WaterReading, null, "waterReading_" + item.rID)
</td>
<td>
<span id="#string.Format("prevWater_{0}",item.rID)">
#Html.DisplayFor(model => item.prevWaterReading, null, "prevWater_" + item.rID)
</span>
#Html.HiddenFor(model => item.prevWaterReading, new { id = "hprevWater_" + item.rID })
</td>
<td>
<span id="#string.Format("hdifference_{0}",item.rID)">
#Html.DisplayFor(model => item.wDifference)
</span>
#Html.HiddenFor(model => item.prevWaterReading, new { id = "hdifference_" + item.rID })
</td>
<td>
#Html.ActionLink("View History", "ExportDataIndex", "ExportData", new { rID = item.rID, bId = Model.bID }, null) |
<a href="#Url.Action("ExportToExcel", "ExportData", new { rID = item.rID, bId = Model.bID })" class="btn btn-success">
<i class="fa fa-file-excel-o" aria-hidden="true" title="Export to Excel"></i>
</a> |
<a href="#Url.Action("ChartData", "Utilities", new { rID = item.rID, bId = Model.bID })" class="btn btn-info">
<i class="fa fa-bar-chart" aria-hidden="true" title="Water Usage History"></i>
</a>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</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>
}
Controller:
// GET: ImportWater
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Upload([Bind(Include = "bEntryDate,bPrevDate,bID,utilData,UploadFile")]UtilityData uData) //<----The file gets uploaded but none of the Model data from the view.
{
HttpPostedFileBase postedFile = uData.UploadFile;
if (postedFile != null && postedFile.ContentLength > 0)
{
string fileName = postedFile.FileName;
string fileContentType = postedFile.ContentType;
byte[] fileBytes = new byte[postedFile.ContentLength];
var data = postedFile.InputStream.Read(fileBytes, 0, Convert.ToInt32(postedFile.ContentLength));
using (var package = new ExcelPackage(postedFile.InputStream))
{
//Todo: read file and insert data
}
ViewBag.Message = "File uploaded successfully.";
}
return View(uData);
}
I now understand what the issue was; I didn't fully understand how the POST worked. I thought the form would always send the full model object back and that is not the case. I created hidden items to capturethe model data I wanted to post back.
#using (Html.BeginForm("Upload", "ImportWater", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="control-group">
#Html.TextBoxFor(m => m.UploadFile, new { type = "file"})
#*<input type="file" class="btn btn-info" name="postedFile"/>*#
</div>
<div class="control-group">
<input type="submit" class="btn btn-info" value="Upload" />
</div>
<div class="col-lg-12 visible-lg">
<br>
<span style="color:green">#ViewBag.Message</span>
#Html.HiddenFor(model => model.bID)
#Html.HiddenFor(model => model.bEntryDate)
#Html.HiddenFor(model => model.bPrevDate)
#for (int i = 0; i < Model.utilData.Count(); i++)
{
#Html.HiddenFor(model => model.utilData[i].ResNumber)
#Html.HiddenFor(model => model.utilData[i].GrnLower)
#Html.HiddenFor(model => model.utilData[i].GrnUpper)
#Html.HiddenFor(model => model.utilData[i].prevWaterReading)
#Html.HiddenFor(model => model.utilData[i].rID)
#Html.HiddenFor(model => model.utilData[i].WaterReading)
#Html.HiddenFor(model => model.utilData[i].wDifference)
#Html.HiddenFor(model => model.utilData[i].YelLower)
#Html.HiddenFor(model => model.utilData[i].YelUpper)
}
</div>
}
I'm having trouble binding the selected value of a drop down list to the correct property in my view model. I can't see what I am doing wrong here. I've put the code that should help show what I'm doing below. I've omitted some things such as the population of the 'AllFolders' property of the view model, as it's just a simple List with an object called ImageGalleryFolder.
Every time the form posts back, the ParentFolderId property is null without fail. This is driving me crazy and I've wasted a lot of time trying to work it out.
Can anyone see something I'm doing wrong?
This is the view model
public class ImageGalleryFolderViewModel
{
[Required]
public string Title { get; set; }
public int Id { get; set; }
public string CoverImageFileName { get; set; }
public HttpPostedFileBase UploadedFile { get; set; }
public string ParentFolderId { get; set; }
public IList<ImageGalleryFolder> AllFolders { get; set; }
}
Here is the view code
#using Payntbrush.Presentation.Demo.MVC3.Areas.Admin
#model Payntbrush.Presentation.Demo.MVC3.Areas.Admin.Models.ImageGalleryFolderViewModel
#{
ViewBag.Title = "Create A New Gallery Folder";
}
<h2>#ViewBag.Title</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((string)ViewBag.Action + "Folder", "Portfolio", FormMethod.Post, new { Id = "CreateFolder", enctype = "multipart/form-data" }))
{
#Html.ValidationSummary(true)
if(((string)ViewBag.Action).ToLower() == FormConstants.Edit.ToLower())
{
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.CoverImageFileName)
#Html.HiddenFor(m => m.ParentFolderId)
}
<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.UploadedFile)
</div>
<div class="editor-field">
<input type="file" name="UploadedFile"/>
#Html.ValidationMessageFor(model => model.UploadedFile)
</div>
{
// Count > 1 is important here. If there is only 1 folder, then we still don't show the drop down
// as a child folder can't have itself as it's own parent.
}
if(#Model.AllFolders.Count > 1)
{
<div class="editor-label">
Choose a parent folder (optional)
</div>
<div class="editor-field">
#Html.DropDownListFor(m => m.ParentFolderId, new SelectList(Model.AllFolders, "Id", "Title"))
</div>
}
<p>
<input type="submit" value="Save" />
</p>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
I've ommitted my view, but this is what my form looks like when rendered in the browser. The form looks good from what I can see?
<form Id="CreateFolder" action="/SlapDaBass/Portfolio/EditFolder/1" enctype="multipart/form-data" method="post">
<input data-val="true" data-val-number="The field Id must be a number." data-val-required="The Id field is required." id="Id" name="Id" type="hidden" value="1" />
<input id="CoverImageFileName" name="CoverImageFileName" type="hidden" value="" />
<input id="ParentFolderId" name="ParentFolderId" type="hidden" value="" />
<div class="editor-label">
<label for="Title">Title</label>
</div>
<div class="editor-field">
<input class="text-box single-line" data-val="true" data-val-required="The Title field is required." id="Title" name="Title" type="text" value="Test" />
<span class="field-validation-valid" data-valmsg-for="Title" data-valmsg-replace="true"></span>
</div>
<div class="editor-label">
<label for="UploadedFile">UploadedFile</label>
</div>
<div class="editor-field">
<input type="file" name="UploadedFile"/>
<span class="field-validation-valid" data-valmsg-for="UploadedFile" data-valmsg-replace="true"></span>
</div>
<div class="editor-label">
Choose a parent folder (optional)
</div>
<div class="editor-field">
<select id="ParentFolderId" name="ParentFolderId">
<option value="1">Test</option>
<option value="2">Test 2</option>
</select>
</div>
<p>
<input type="submit" value="Save" />
</p>
</form>
And this is the controller action:
[HttpPost]
public ActionResult EditFolder(int id, ImageGalleryFolderViewModel model)
{
if (ModelState.IsValid)
{
Services.PortfolioService.UpdateFolder(model.MapToDomainModel(), model.UploadedFile);
return Home;
}
return View();
}
change the data type of the ParentFolderId
public class ImageGalleryFolderViewModel
{
[Required]
public string Title { get; set; }
public int Id { get; set; }
public string CoverImageFileName { get; set; }
public HttpPostedFileBase UploadedFile { get; set; }
public int ParentFolderId { get; set; }
public IList<ImageGalleryFolder> AllFolders { get; set; }
}
also use the Html helper for the dropdownlist
<%:
Html.DropDownListFor(
model => model.ParentFolderId ,
new SelectList(
new List<Object>{
new { value = 1 , text = "Test" },
new { value = 2 , text = "Test2" },
new { value = 3 , text = "Test3"}
},
"value",
"text"
)
)
%>
i hope you are strongly typing your view like
public ActionResult EditFolder()
{
return View(new ImageGalleryFolderViewModel());
}
Please refer below link for the bind drop down list. It will be very helpful to you.
ASP.NET MVC - drop down list selection - partial views and model binding
Here if you do not want to create property in model for the List of items, than you can also store it in a ViewData or ViewBag. Please find sample code below.
<%= Html.DropDownList("Category.CategoryId", new SelectList((
IEnumerable<ProductManagement.Models.Category>)ViewData["CategoryList"],
"CategoryId", "CategoryName"))%>
You're creating a Hidden input for ParentFolderId with an empty value. This is probably overriding the value that the DropDownList is trying to post. Remove this line:
#Html.HiddenFor(m => m.ParentFolderId)
you have 2 element for ParentFolderId
one of them is hidden field
#Html.HiddenFor(m => m.ParentFolderId)
second is select element
#Html.DropDownListFor(m => m.ParentFolderId, new SelectList(Model.AllFolders, "Id", "Title"))
and modelbinder bind the first matched element value to model.
You have to remove hidden field