How to create html table from model class - c#

I am new in MVC and learning MVC. Now I do not want to use any grid extension, rather I want to generate tabular UI just by HTML table. So I was looking for code and found a hint.
This code is not full code. Here is what I got.
<% if (Model.Count() > 0)
{ %>
<table width="35%">
<thead><tr><th>Species</th><th>Length</th></tr></thead>
<% foreach (var item in Model)
{ %>
<tr>
<td><%= item.FishSpecies%></td>
<td align="center"><%=item.Length%></td>
</tr>
<% } %>
</table>
<% }
else
{ %>
No fish collected.
<%} %>
The problem is I am not being able to visualize how the model class should look like and how it is populated from controller. Viewbag is not used in code, rather generating table directly from model class.
So can anyone write a small complete code for me just to create a HTML table and populate with model directly without using viewbag?
Need code for model, controller and view too.

Your model actually needs to be a IEnumerable<Model>. So you might have a model:
public class Model
{
public string FishSpecies { get; set; }
public int Length { get; set; }
public static IEnumerable<Model> Load() { ... }
}
and then in your controller's action:
var list = Model.Load();
return View(list);
and then in the view you need to define the model at the very top:
#model System.Collections.IEnumerable<My.Namespace.Model>
Now, these two lines aren't going to work:
<td><%= item.FishSpecies%></td>
<td align="center"><%=item.Length%></td>
they need to be something more like this:
<td>#Html.DisplayFor(m => item.FishSpecies)</td>
<td>#Html.DisplayFor(m => item.Length)</td>

If you want to iterate through your model and create your table, first of all change the #model of your view like this:
#model IEnumerable<myPrj.Models.EntityName>
Then, you should change your action method to supply model items to your view:
public ActionResult Index()
{
return View(db.EntityNames.ToList());
}
and finally, iterate your model and create your table:
<table id="tblNews">
<tr>
<th>
#Html.DisplayNameFor(model => model.Property1)
</th>
// ...
<th>
#Html.DisplayNameFor(model => model.Propertyn)
</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Property1)
</td>
// ...
<td>
#Html.DisplayFor(modelItem => item.Propertyn)
</td>
</tr>
}
</table>
and about the model? Model is nothing but a class with some properties:
public class MyModel
{
[Display(Name = "Property 1")]
[Required(ErrorMessage = "Property cannot be empty")]
public int Property1 { get; set; }
// ...
[Display(Name = "Property n")]
[Required(ErrorMessage = "Property cannot be empty")]
public string Propertyn { get; set; }
}

Related

Why enum values are not showing in View

I want to allow the user to download the report, when it is approved by a supervisor. At the moment, I'm working with manager account, where he can check many reports and change their state to either verified or denied, but I don't understand why the report states enum list is not displaying, even though it is shown in the console.
HTML code
Model:
public class Report
{
public int ID { get; set; }
[Display(Name = "Report Name")]
public string reportName { get; set; }
public virtual User reportManager { get; set; }
[Display(Name = "State")]
public ReportState reportState { get; set; }
public byte[] reportData { get; set; }
}
public enum ReportState
{
Accepted,
Pending,
Denied
}
Controller:
public async Task<IActionResult> Index()
{
ViewBag.Reports = await _context.Reports.ToListAsync();
ViewBag.ReportStates = new SelectList(Enum.GetNames(typeof(ReportState)));
return View();
}
#model variable_pay_system.Models.Report
#{
ViewData["Title"] = "Reports";
}
<div class="container">
<h5>Reports</h5>
<table>
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.reportName)
</th>
<th>
#Html.DisplayNameFor(model => model.reportState)
</th>
</tr>
</thead>
<tbody>
#foreach (Report report in ViewBag.Reports)
{
<tr>
<td>
#Html.DisplayFor(modelItem => report.reportName)
</td>
<td>
<select asp-for="#report.reportState" asp-items="Html.GetEnumSelectList<ReportState>()"></select>
</td>
</tr>
}
</tbody>
</table>
If you are going to use enums in View, you can show these enums as a list. and I recommend using it by printing it to a dropdown. EnumDropDownListFor()
Model :
public enum ReportState
{
Accepted,
Pending,
Denied
}
View:
#Html.EnumDropDownListFor(model => model.ReportState)
Using Tag Helper (ASP.NET MVC 6):
<select asp-for="#Model.SelectedValue" asp-items="Html.GetEnumSelectList<ReportState>()">
Ok so the problem was because, I was missing this code at the end.
<script>
$(document).ready(function () {
$('select').formSelect();
});

How do I display a list from SQL database when my table is not enumerable? I can use details view fine, but not an index view

I am having difficulty getting my Index view to display the values I have from the SQL database.
The main issue is that I cannot use #foreach (var item in Model) {... because my table is not created as Enumerable (I think). I run into the error message System.NullReferenceException: 'Object reference not set to an instance of an object.' pointing at Model in the foreach statement expression.
I am wondering if I need to set my table up as an Enumerable list, then display each item. Or if I am missing something with regards to my Index view. Or maybe I need to pass something through the Index return View()?
Here is the Image Model:
namespace Uploadimage.Models
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Web;
public partial class Image
{
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public string City { get; set; }
public string Province { get; set; }
public string Phone { get; set; }
[Required]
[RegularExpression(#"[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\.[A-Za-z]{2,4}",
ErrorMessage = "Email doesn't look like a valid email address.")]
public string Email { get; set; }
[Compare("Email", ErrorMessage = "Emails do not match.")]
public string ConfirmEmail { get; set; }
[DisplayName("Upload File")]
public string ImagePath { get; set; }
public HttpPostedFileBase ImageFile { get; set; }
}
}
Here is the controller:
public class ImageController : Controller
{
[HttpGet]
public ActionResult Add()
{
return View();
}
[HttpPost]
public ActionResult Add(Image imageModel)
{
string fileName = Path.GetFileNameWithoutExtension(imageModel.ImageFile.FileName);
string extension = Path.GetExtension(imageModel.ImageFile.FileName);
fileName = fileName + DateTime.Now.ToString("yymmssfff") + extension;
imageModel.ImagePath = "~/Image/" + fileName;
fileName = Path.Combine(Server.MapPath("~/Image/"), fileName);
imageModel.ImageFile.SaveAs(fileName);
using(LoginDBEntities db = new LoginDBEntities())
{
db.Images.Add(imageModel);
db.SaveChanges();
}
ModelState.Clear();
return View();
}
[HttpGet]
public ActionResult View(int id)
{
Image imageModel = new Image();
using (LoginDBEntities db = new LoginDBEntities())
{
imageModel = db.Images.Where(x => x.Id == id).FirstOrDefault();
}
return View(imageModel);
}
public ActionResult Index()
{
return View();
}
}
And lastly, my Index View:
#model IEnumerable<Uploadimage.Models.Image>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.FirstName)
</th>
<th>
#Html.DisplayNameFor(model => model.LastName)
</th>
<th>
#Html.DisplayNameFor(model => model.City)
</th>
<th>
#Html.DisplayNameFor(model => model.Province)
</th>
<th>
#Html.DisplayNameFor(model => model.Phone)
</th>
<th>
#Html.DisplayNameFor(model => model.Email)
</th>
<th>
#Html.DisplayNameFor(model => model.ImagePath)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.FirstName)
</td>
<td>
#Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.City)
</td>
<td>
#Html.DisplayFor(modelItem => item.Province)
</td>
<td>
#Html.DisplayFor(modelItem => item.Phone)
</td>
<td>
#Html.DisplayFor(modelItem => item.Email)
</td>
<td>
#Html.DisplayFor(modelItem => item.ImagePath)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
#Html.ActionLink("Details", "Details", new { id=item.Id }) |
#Html.ActionLink("Delete", "Delete", new { id=item.Id })
</td>
</tr>
}
</table>
I have tried thoroughly looking this up but have found things that don't specifically apply to me.
Or I don't know what to look up.
Two things:
Your Index controller action will need to load the images to pass to the view. At a minimum to serve a collection of images as the view's "model":
public ActionResult Index()
{
using (LoginDBEntities db = new LoginDBEntities())
{
var images = db.Images.ToList();
return View(images);
}
}
Then in the view you will need to check whether you actually get any results, then extract your labels from the first result if there are any, or display a suitable message if there are no images:
<!-- ... -->
#if (Model.Any())
{
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.ElementAt(0).FirstName)
</th>
<th>
#Html.DisplayNameFor(model => model.ElementAt(0).LastName)
</th>
...
</tr>
#for (int count = 0; count < Model.Count; count++) {
<tr>
<td>
#Html.DisplayFor(model => model.ElementAt(count).FirstName)
</td>
<td>
#Html.DisplayFor(model => model.ElementAt(count).LastName)
</td>
...
<td>
#Html.ActionLink("Edit", "Edit", new { id=#Model.ElementAt(count).Id }) |
#Html.ActionLink("Details", "Details", new { id=#Model.ElementAt(count).Id }) |
#Html.ActionLink("Delete", "Delete", new { id=#Model.ElementAt(count).Id })
</td>
</tr>
}
</table>
}
else
{
<p> No Images. </p>
}
The above is written from memory so it will likely have some syntactic issues but it should point you in the right direction.
Typically when working with collections like search results I define a view model class for the results page that itself contains the collection of results. The page's model becomes that wrapper view model which can contain details about the page (such as things like current search criteria, lookup values for searches, etc.) along with the collection of results. (typically a PagedList to support pagination)
I.e.
[Serializable]
public class ImageIndexViewModel
{
public string NameSearchString { get; set; }
public ICollection<ImageViewModel> Results { get; set; } = new List<ImageViewModel>();
}
Where ImageViewModel is a serializable POCO view model to represent just the details about images that the view will display. Often the views don't need everything about a data row, and in cases where the data row has navigation properties and extra fields we don't need to display, serializing entities results in lazy load calls or simply sending a lot of extra data that isn't needed.
You don't have anything in your Index method which gets a list of entities.
Your Index method in your controller should look something like:
public ActionResult Index()
{
var model = db.Images.ToList();
return View(model);
}

MVC passing the selected object from a List view page

I have a little serach box that returns results from a database. That works fine. The results are in a List page and display correctly. However, I need to take the selected object and pass it to my controller. I am getting NULL values when I debug it, and an empty results page.
Here is the model:
public class CodeSnip
{
public short Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Code { get; set; }
public LangType Language { get; set; }
public string Creator { get; set; }
}
public class ListOfCodeSnips : List<CodeSnip>
{
public CodeSnip CodeSnip { get; set; }
}
public enum LangType
{
CSharp,
css,
HTML,
JavaScript,
Perl,
PHP,
Python,
Ruby,
SQL,
VB,
XML,
Other
}
Here is the controller method (which does nothing atm):
[HttpPost]
public ActionResult Display(CodeSnip snip)
{
return View(snip);
}
Here is what I have for a view. Again, it posts only NULLS for the object values:
#model Models.ListOfCodeSnips
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.CodeSnip.Title)
</th>
<th>
#Html.DisplayNameFor(model => model.CodeSnip.Description)
</th>
<th>
#Html.DisplayNameFor(model => model.CodeSnip.Language)
</th>
<th>
#Html.DisplayNameFor(model => model.CodeSnip.Creator)
</th>
</tr>
#using (Html.BeginForm("Display", "Home", FormMethod.Post))
{
foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
<td>
#Html.DisplayFor(modelItem => item.Language)
</td>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
<td>
#Html.DisplayFor(modelItem => item.Creator)
</td>
<td>
<input type="submit" value ="Display"/>
</td>
</tr>
}
}
</table>
so, in short, what I want to do is take the selected model-item from the view and pass it into my controllers Display method, but as I said, all I get are nulls.
I have looked around and all i find are examples f how to pass a List of objects. I tried monkeying with those, but got nothing.
Thanks for your time.
Since you said that you have tried with ActionLink and it did not work, here is how it would work.. instead of passing the type that you are looking for as a parameter for the Display action, pass the ID of the record.
So it would look something like this:
Controller Action
[HttpGet]
public ActionResult Display(int id)
{
var snip = db/* connection string */.TableName.Find(id);
return View(snip);
}
ActionLink
#Html.ActionLink("Display", "Display", "ControllerName", new { id = item.Id }, null )
// Or if you want a button that acts as a link, and not just a plain link
<input type="button" class="btn" value="Display" onclick="location.href='#Url.Action("Display", "ControllerName", new { id = item.Id })'" />
Let me know if this helps!
There are two problems with your code:
1) You do not render any <input> elements in which you send the selected values back to the controller. Use Html.HiddenFor or Html.EditorFor in addition to Html.DisplayFor.
2) In order for the MVC Modelbinder to be able to bind your list, use a for loop instead of foreach.
See also MVC Form submit not transferring the updated model back to action method for List
for (var i = 0; i < Model.Count(); i++) {
<tr>
<td>
#Html.DisplayFor(m => m[i].Title)
#Html.HiddenFor(m => m[i].Title)
</td>
#* etc ... *#
<tr>
}
PS: this loop would post all displayed CodeSnips, i.e. the receiving action would have the signature public ActionResult Display(ListOfCodeSnips postData).

How can I show just distinct names (from my model) in my view?

Problem
How can I return back just the distinct names in my view? I can't cast the string returned by my distinct() method back to an Inumerable object.
For simplification, if you see a person named John Smith in the database, we assume all quotes from John Smith are from the same person.
Code
Data
id name quote
1 Finn Mathematical
2 Jake Aw, man!
3 Shredder Tonight, I dine on turtle soup.
4 Shredder Nooooo
Model
namespace Quotes_Sample.Models
{
public class QuoteDB
{
public int Id { get; set; }
public string name { get; set; }
public string quote { get; set; }
public bool IsSelected { get; set; }
}
public class QuoteDBContext : DbContext
{
public DbSet<QuoteDB> Quotes { get; set; }
}
}
Controller
private QuoteDBContext db = new QuoteDBContext();
// GET: Quotes
public ActionResult Index()
{
return View(db.Quotes.ToList());
}
View
#model IEnumerable<Quotes_Sample.Models.QuoteDB>
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<p>
#Html.ActionLink("Create New", "Create")
</p>
#using (Html.BeginForm("SubmitSelected", "Quotes", FormMethod.Post, new { encType = "multipart/form-data", name="myform"}))
{
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.name)
</th>
</tr>
#foreach (var item in (IEnumerable<Quotes_Sample.Models.QuoteDB>)Model.Select(x => x.name).Distinct())
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.name)
</td>
</tr>
}
</table>
}
</body>
</html>
Error
An exception of type 'System.InvalidCastException' occurred in App_Web_cied5w21.dll but was not handled in user code
Additional information: Unable to cast object of type '<DistinctIterator>d__81`1[System.String]' to type 'System.Collections.Generic.IEnumerable`1[Quotes_Sample.Models.QuoteDB]'.
Attempts
I can just remove the casting and have duplicates, but it's really
ugly.
I thought about writing a temp variable that remembers last
name in loop, if same it breaks. That is just plain gross? Seems hacky too.
Just having 1 name to 1 quote relationship in my database, but that avoids the problem completely.
This, which actually brought me to this problem.
This below:
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.name)
</th>
</tr>
#foreach (string name in Model.Select(x => x.name).Distinct())
{
<tr>
<td>
#Html.DisplayText(name);
</td>
</tr>
}
</table>
But that really weirds me out. It gives me the following:
name
;
;
;
Thanks for your patience and assistance.
You can get Distinct records by name using:
Model.GroupBy(x => x.name).Select(x => x.First())
But you should do this in your Controller, not View.
Also you can use DistinctBy method:
Model.DistinctBy(x => x.name)

Why is the ViewModel child object null in MVC 2?

I have a web app where I want to show a form for a Test object. Different Test instances can have different schemas. I can display this pretty nicely, but it won't populate all the data from the form back into my model.
Here are my model classes:
public class EnterTestData
{
public string StudyId { get; set; }
public Test Test { get; set; }
}
public sealed class Test
{
public string Identity { get; set; }
public string Name { get; set; }
public IEnumerable<TestField> Fields { get; set; }
}
public sealed class TestField
{
public string Identity { get; set; }
public string Name { get; set; }
public string Value { get; set; }
public string Type { get; set; }
}
Here is the relevant portion of my View:
<% Html.BeginForm("PostTestData", "StudiesUserInterface"); %>
<table>
<%
foreach (var testField in Model.Test.Fields)
Html.RenderPartial("UserControls/TestFieldUserControlascx", testField);
foreach (var category in Model.Test.Categories)
{
%>
<tr>
<td colspan="2" style="font-style: italic; text-align: center;">
<%=category.Name %>
</td>
</tr>
<%
foreach (var testField in category.Fields)
Html.RenderPartial("UserControls/TestFieldUserControlascx", testField);
}
%>
<tr>
<td colspan="2" style="text-align: right;">
<input type="submit" name="newsletter" value="Enter Result" />
</td>
</tr>
</table>
<% Html.EndForm(); %>
And the partial view for the actual text boxes:
<tr>
<td>
<%= Model.Name %>
</td>
<td>
<%
switch (Model.Type)
{
case "date":
case "text":
case "number":
%>
<%= Html.TextBox(Model.name, Model.Value) %>
<% break;
default: %><%= Html.Label("Unknown data type") %><% break;
}
%>
</td>
</tr>
Update Controller methods:
public ActionResult EnterTestData(string studyId, string testId)
{
var testDefinition = ServiceKitLocator.GetStudyService().GetTestDefinition(testId);
return View(new EnterTestData { StudyId = studyId, Test = testDefinition });
}
public ActionResult PostTestData(EnterTestData model)
{
//I'm just putting a break point here and checking the model in the debugger for now
return RedirectToAction("Index");
}
The problem is that Test is null when it comes back to my Controller. How do I get it to be populated? Why is it null?
I think the problem is the Html.TextBox element.
If it's correct that the model in your view is type of "Test" but in your controller action you want to bind to type of "EnterTestData" which has a property of type "Test" named "Test"
Then your TextBox should be initializied like
Html.TextBox("Test.Name", Model.Value)
The important part is the name parameter. The modelbinder matches this name with the properties of the model type in your post action, in your case "EnterTestData".
You can also use an editor template view. Which does the same as your partial view.
In your project go to Views\Shared\ and create a folder named EditorTemplates, if not exists.
In this folder create a partial view and name it like the class/type the template should be for. In your case "TestField.ascx". Actually you can copy and rename your existing partial view.
In you main view you have to change 2 things:
- use for instead of foreach
- call the editor template in the loops
like:
for(int i = 0; i < Model.Test.Fields.Count(); i++)
Html.EditorFor(Model => Model.Test.Fields[i]);
In the template view you have to change one thing:
- user TextBoxFor instead of TextBox
like:
Html.TextBoxFor(Model => Model.Value)
I use this pattern often. It makes it easy to bind complex models.
You may have a look at the generated HTML

Categories

Resources