Since English is not my native language, I'm very sorry for any mistake I will exihbit in the next senteces.
I created a database with products (produtos) and types of Products (Categoria)
My objective was to create a relationship (codefirst) in MVC from One to Many.
Where a type of product (Categoria) can have multiple Products (Produtos)
So I had created this:
namespace HardwareWarehouse.Models
{
public class Produto
{
public Produto()
{
}
[Key]
public int IDProduto { get; set; }
[Required]
[StringLength(50)]
public string Nome { get; set; }
[Required]
public double Preco { get; set; }
[Required]
public double Peso { get; set; }
[Required]
[StringLength(255)]
public string Descricao { get; set; }
public int Imagem { get; set; }
public DateTime UltimaAtualizacao { get; set; }
public int Stock { get; set; }
public int CategoriaID { get; set; }
public virtual Produto Produtos { get; set; }
}
}
And for the Category/type of Product (Categoria), I created this..
namespace HardwareWarehouse.Models
{
public class Categoria
{
public Categoria()
{
Produtos = new List<Produto>();
}
[Key]
public int CategoriaID { get; set; }
[Required]
[StringLength(50)]
public string Nome { get; set; }
public virtual ICollection<Produto> Produtos { get; set; }
}
}
So I think my relationship between both tables are fine so far.
Next, I had created a controller, and used a default view to store some data.
Which looks like this:
And this is my default view that shows that info..
#model IEnumerable<HardwareWarehouse.Models.Produto>
#{
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>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Nome)
</th>
<th>
#Html.DisplayNameFor(model => model.Preco)
</th>
<th>
#Html.DisplayNameFor(model => model.Peso)
</th>
<th>
#Html.DisplayNameFor(model => model.Descricao)
</th>
<th>
#Html.DisplayNameFor(model => model.Imagem)
</th>
<th>
#Html.DisplayNameFor(model => model.UltimaAtualizacao)
</th>
<th>
#Html.DisplayNameFor(model => model.Stock)
</th>
<th>
#Html.DisplayNameFor(model => model.CategoriaID)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Nome)
</td>
<td>
#Html.DisplayFor(modelItem => item.Preco)
</td>
<td>
#Html.DisplayFor(modelItem => item.Peso)
</td>
<td>
#Html.DisplayFor(modelItem => item.Descricao)
</td>
<td>
#Html.DisplayFor(modelItem => item.Imagem)
</td>
<td>
#Html.DisplayFor(modelItem => item.UltimaAtualizacao)
</td>
<td>
#Html.DisplayFor(modelItem => item.Stock)
</td>
<td>
#Html.DisplayFor(modelItem => item.CategoriaID)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.IDProduto }) |
#Html.ActionLink("Details", "Details", new { id=item.IDProduto }) |
#Html.ActionLink("Delete", "Delete", new { id=item.IDProduto })
</td>
</tr>
}
</table>
</body>
</html>
The Millions dollar question is?
How do I make the view to show the Categoria.Nome instead of the CategoriaID number on that view?
Thanks in advance..
Adding to Alisson answer, ViewModels are used to send multiple models to the view
Here is the Link
ViewModel in ASP.NET MVC
Related
still learning MVC (slowly) which I am enjoying but having some trouble with partial views. If anyone could point me in the right direction that would be great!
This is the error I am getting:
InvalidOperationException: The model item passed into the dictionary
is of type
'System.Collections.Generic.List1[mvc_project.Models.Contact]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[mvc_project.ViewModels.ViewContactsVM]'.
I created the partial view using the List template.
Chose my model ViewContactsVM
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace mvc_project.ViewModels
{
public class ViewContactsVM
{
[Key]
public int ContactID { get; set; }
public int ClientID { get; set; }
[DisplayName("Contact Name")]
public string ContactName { get; set; }
[DisplayName("Contact Phone Number")]
public string ContactPhoneNumber { get; set; }
[DisplayName("Contact Email Address")]
public string ContactEmailAddress { get; set; }
[DisplayName("Job Title")]
public string JobTitle { get; set; }
public string AddedBy { get; set; }
}
}
Controller code:
public ActionResult _ContactList(int id)
{
using (var context = new mvc_projectEntities())
{
var data = context.Contacts.Where(x => x.ClientID == id).ToList();
return View(data);
}
}
_ContactsList Partial View Page:
#model IEnumerable<mvc_project.ViewModels.ViewContactsVM>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.ClientID)
</th>
<th>
#Html.DisplayNameFor(model => model.ContactName)
</th>
<th>
#Html.DisplayNameFor(model => model.ContactPhoneNumber)
</th>
<th>
#Html.DisplayNameFor(model => model.ContactEmailAddress)
</th>
<th>
#Html.DisplayNameFor(model => model.JobTitle)
</th>
<th>
#Html.DisplayNameFor(model => model.AddedBy)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.ClientID)
</td>
<td>
#Html.DisplayFor(modelItem => item.ContactName)
</td>
<td>
#Html.DisplayFor(modelItem => item.ContactPhoneNumber)
</td>
<td>
#Html.DisplayFor(modelItem => item.ContactEmailAddress)
</td>
<td>
#Html.DisplayFor(modelItem => item.JobTitle)
</td>
<td>
#Html.DisplayFor(modelItem => item.AddedBy)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.ContactID }) |
#Html.ActionLink("Details", "Details", new { id=item.ContactID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.ContactID })
</td>
</tr>
}
</table>
Details View Page:
#{
ViewBag.Title = "Details";
}
<h2>Details</h2>
#Html.Action("_ContactList")
It is necessary to pass the ViewContactsVM type to the view, as it defined in the view data model. Therefore try:
public ActionResult _ContactList(int id)
{
using (var context = new mvc_projectEntities())
{
var data = context.Contacts
.Where(x => x.ClientID == id)
.Select(c => new mvc_project.ViewModels.ViewContactsVM() {
ContactID = c.ContactID,
ClientID = c.ClientID,
ContactName = c.ContactName
...
})
.ToList();
return View(data);
}
}
I have standards views generated with scaffolding my model from Main table.
Now instead of standard Details view which shows details of single record based on Main table I want to show list (like in Index view) of all rows where this id appears in another table (called Audit table).
So when user clicks Details in Index view of Main table (model) it should get list of records from Audit table where that id appears.
Main table model:
public partial class Main
{
public int id{ get; set; }
public int SocID { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public string User { get; set; }
}
Audit table model:
public partial class Audit
{
public int idAudit{ get; set; }
public int id{ get; set; }
public int SocID { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public string User { get; set; }
public string DateOfChange { get; set; }
public string Operation { get; set; }
}
So I've modified Details class in my Main model Controller to return all records from Audit table based on id from Main table in following way:
public IActionResult Details(int? id)
{
if (id == null)
{
return NotFound();
}
var Audit = (from c in _context.Audit
where c.id == id
select new Audit
{
SocID = c.SocID,
Name = c.Name,
Title = c.Title,
User = c.User,
DateOfChange = c.DateOfChange,
Operation = c.Operation
}).ToList();
if (Audit == null)
{
return NotFound();
}
return View(Audit);
}
And my View for Details class looks like this now:
#model IEnumerable<Audit>
#{
ViewData["Title"] = "Audit ....";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="table-responsive tableFixHead">
<table class="table table-striped table-hover mx-auto w-auto" id="nkz10table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.SocID)
</th>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Title)
</th>
<th>
#Html.DisplayNameFor(model => model.User)
</th>
<th>
#Html.DisplayNameFor(model => model.DateOfChange)
</th>
<th>
#Html.DisplayNameFor(model => model.Operation)
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.SocID)
</td>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
<td>
#Html.DisplayFor(modelItem => item.User)
</td>
<td>
#Html.DisplayFor(modelItem => item.DateOfChange)
</td>
<td>
#Html.DisplayFor(modelItem => item.Operation)
</td>
</tr>
}
</tbody>
</table>
</div>
EDIT
In Index view I'm calling Details with:
#model IEnumerable<Audit>
....
<a asp-action="Details" asp-route-id="#item.id">Details</a>
when I click Details for specific id which has several records in Audit table I get
404 - page not found.
What am I doing wrong?
Problem was with definition of controller, here is what worked for me in the end:
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var audit= _context.audit
.Where(m => m.Nkz10Pk == id);
if (audit== null)
{
return NotFound();
}
return View(audit);
}
Can someone tell what is wrong in my code? I've been searching several days a solution for this, but can't find any. Values from Form doesn't pass to view, only values that are "hardcoded" like OrderDate, CompanyID and so on, is added to database.
I'm making my first MVC onlineshop and struggling with it quite a lot.
EDIT: Data to view is coming from db.Selection but I need to save data to model db.Cart with additional parameters. Maybe FormCollection is not right way to do this, but what is?
NVCONTROLLER:
public ActionResult Index()
{
return View(db.Selection);
}
public ActionResult AddtoCart()
{
return View("AddtoCart");
}
[HttpPost]
[ValidateAntiForgeryToken()]
public ActionResult AddtoCart(FormCollection form)
{
string Author = form["Author"];
string ISBN = form["ISBN"];
string BookName = form["BookName"];
string Publisher = form["Publisher"];
string Price = form["Price"];
var cart = new Cart();
cart.Orderdate = DateTime.Now;
cart.CompanyID = 12345;
cart.ISBN = Convert.ToInt32(ISBN);
cart.BookName = BookName;
cart.Price = Convert.ToInt32(Price);
cart.IsInCart = true;
cart.Author = Author;
cart.Publisher = Publisher;
cart.SentToJvs = false;
cart.Reference = "NV kevät 1999";
db.Cart.Add(cart);
db.SaveChanges();
return RedirectToAction("Index");
}
VIEW
#model IEnumerable<STGchannelMVC.Models.Selection>
#using STGchannelMVC.Controllers
#{
ViewBag.Title = "Index";
}
#using (Html.BeginForm("AddtoCart", "NV", FormMethod.Post))
{
<p>
Lisää tuotteet ostoskoriin
</p>
#Html.AntiForgeryToken()
<div class="form-group">
<table class="table">
<tr>
<th hidden>
#Html.DisplayNameFor(model => model.BookID)
</th>
<th>
#Html.DisplayNameFor(model => model.ISBN)
</th>
<th>
#Html.DisplayNameFor(model => model.Author)
</th>
<th>
#Html.DisplayNameFor(model => model.BookName)
</th>
<th>
#Html.DisplayNameFor(model => model.Publisher)
</th>
<th>
#Html.DisplayNameFor(model => model.Price)
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td hidden>
#Html.DisplayFor(modelItem => item.BookID)
</td>
<td>
#Html.DisplayFor(modelItem => item.ISBN)
</td>
<td>
#Html.DisplayFor(modelItem => item.Author)
</td>
<td>
#Html.DisplayFor(modelItem => item.BookName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Publisher)
</td>
<td>
#Html.DisplayFor(modelItem => item.Price)
</td>
<td>
<button type="submit" class="btn btn-primary">Add to Cart</button>
</td>
</tr>
}
</table>
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
MODEL
public class Cart
{
public int OrderID { get; set; }
public Nullable<System.DateTime> Orderdate { get; set; }
[Display(Name = "CompanyID")]
public Nullable<long> CompanyID { get; set; }
[Display(Name = "ISBN")]
public Nullable<long> ISBN { get; set; }
[Display(Name = "BookName")]
public string BookName { get; set; }
[Display(Name = "Price")]
public Nullable<decimal> Price { get; set; }
public Nullable<bool> IsInCart { get; set; }
[Display(Name = "Author")]
public string Author { get; set; }
[Display(Name = "Publisher")]
public string Publisher { get; set; }
public Nullable<bool> SentToJvs { get; set; }
public string Reference { get; set; }
}
This question/answer might explain it better: MVC4 Razor - #Html.DisplayFor not binding to model
Basically you have two options:
In your view, you can keep using #Html.DisplayFor() but add #Html.HiddenFor() inside the foreach loop. Doesn't make much sense since there's no way for the values to be changed by the user.
#foreach (var item in Model)
{
#Html.HiddenFor(modelItem => item.BookID)
#Html.HiddenFor(modelItem => item.ISBN)
#Html.HiddenFor(modelItem => item.Author)
#Html.HiddenFor(modelItem => item.BookName)
#Html.HiddenFor(modelItem => item.Publisher)
#Html.HiddenFor(modelItem => item.Price)
<tr>
<td hidden>
#Html.DisplayFor(modelItem => item.BookID)
</td>
<td>
#Html.DisplayFor(modelItem => item.ISBN)
</td>
<td>
#Html.DisplayFor(modelItem => item.Author)
</td>
<td>
#Html.DisplayFor(modelItem => item.BookName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Publisher)
</td>
<td>
#Html.DisplayFor(modelItem => item.Price)
</td>
<td>
<button type="submit" class="btn btn-primary">Add to Cart</button>
</td>
</tr>
}
Or instead of using #Html.DisplayFor(), use #Html.TextboxFor because DisplayFor does not render an input element that the user can interact with while TextboxFor does.
How would I do to change the color of the table row when my "Active" property is switch to false?
I do not use the "Active" property in the table because it does not make sense. So how would I do to check if the property is false and with that change the color of the table?
My ViewModel
public class CodigosDeOperacaoViewModel
{
[Key]
public Guid Id { get; set; }
[Required(ErrorMessage ="Campo Obrigatório")]
[MaxLength(10)]
public string Code{ get; set; }
[Required(ErrorMessage = "Campo Obrigatório")]
public string Description{ get; set; }
[ScaffoldColumn(false)]
public bool Active{ get; set; }
}
MyView
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Code)
</th>
<th>
#Html.DisplayNameFor(model => model.Description)
</th>
<th>Actions</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Code)
</td>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
</tr>
}
</table>
Your view model is a bit strange and the code you posted won't work. You will need to update it so that it contains data about your rows. Something like this
public class CodigosDeOperacaoViewModel
{
//ROWs data
public ICollection<RowData> RowItems {get;set;}
}
public class RowData
{
[Key]
public Guid Id { get; set; }
[Required(ErrorMessage ="Campo Obrigatório")]
[MaxLength(10)]
public string Code{ get; set; }
[Required(ErrorMessage = "Campo Obrigatório")]
public string Description{ get; set; }
[ScaffoldColumn(false)]
public bool Active{ get; set; }
}
<table class="table">
<tr>
<th>
Code
</th>
<th>
Description
</th>
<th>Actions</th>
</tr>
#foreach (var item in Model.RowItems)
{
<tr #(item.Active? "active": string.Empty)>
<td>
#Html.Display(item.Code)
</td>
<td>
#Html.Display(item.Description)
</td>
</tr>
}
Im newbie to mvc.I have a menusettings page where user can add menu according to his role.For that I want to create a view.How can I bind the model to view properly so that i can save the settings.My concern is inorder to access ienumerable item in the viewmodel,viewmodel should also be ienumerable.Here i cannot convert the viewmodel to ienumerable.
Any help will be greatly appreciated
ModelClass is
public partial class Role
{
public int Id { get; set; }
public string RoleName { get; set; }
public Nullable<int> ParentID { get; set; }
public virtual ICollection<MenuRole> MenuRoles { get; set; }
}
public partial class Menu
{
public int Id { get; set; }
public string MenuName { get; set; }
public string NavigateUrl { get; set; }
public Nullable<int> ParentID { get; set; }
public virtual ICollection<MenuRole> MenuRoles { get; set; }
}
ViewModel Is
public class MenuRoleVM
{
public int? RoleId { get; set; }
public SelectList RoleList { get; set; }
public IEnumerable<Menu> MenuList { get; set; }
}
My controller is
public class MenuSettingsController : Controller
{
public ActionResult Add()
{
var _menuRoleVM = new MenuRoleVM
{
RoleList = new SelectList(_db.Roles.ToList(), "Id", "RoleName"),
MenuList = _db.Menus
.Where(m => m.NavigateUrl != "#").ToList()
};
return View(_menuRoleVM);
}
}
Below example will help. Here, I have the model ContactModel with properties - FirstName, LastName and Email.
#model IEnumerable<CrazyContacts.Models.ContactModel>
#{
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.Email)
</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.Email)
</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>
Thanks for your input #Stephen.
I got the code running by the adding the view as below
#foreach (var item in Model.MenuList.Select((x, i) => new {Data=x,Index=i }))
{
<tr>
<td>#(item.Index+1)</td>
<td>#item.Data.MenuName</td>
</tr>
}