ASP MVC - How use two models in one single view - asp.net-mvc

I have a web page that have two task
1) add new products
2) display existing products in a table (refer below image)
also i have two classes for each above task
for display existing details and save new product
public class VMProduct
{
public List<Product>ProductList { get; set; }
}
public class Product
{
public String ProductID { get; set; }
public String ProductName { get; set; }
public String Uom1 { get; set; }
public String Uom2 { get; set; }
public String ProductCategoryID { get; set; }
}
my problem is i can display data by using VMProduct.ProductList model but how i can save new product items using "product" model ?
how can i use both Models in One view?
if i use "product" model's properties in VMProduct model it is duplication codes right?
can anyone have a Solution for this?
thanks in advance

It looks like VMProduct is already a custom view model (I'm mostly guessing by its name), so just add a property to it of the type you need:
public class VMProduct
{
public List<Product> ProductList { get; set; }
public Product NewProduct { get; set; }
}
Though, unless I'm misunderstanding something about your UX, this might not even be necessary. The model used to render a view doesn't necessarily need to be the same model received from any given form on that view. So you might continue to render the view using the VMProduct object you have now, and the form for adding a product can still post an instance of Product to the controller action which invokes the add operation. Something like:
public ActionResult Add()
{
// create your view model
return View(someVMProduct);
}
[HttpPost]
public ActionResult Add(Product newProduct)
{
// add the product to the backing data store
return RedirectToAction("Add");
}

Here explained how to implement 2 model in 1 view in MVC 4. Sometimes its required to implement a page where 2 forms exist in a single view (page) like Login & Registration in a same view.
You can get complete tutorial here : http://dotnetawesome.blogspot.com/2013/09/2-model-in-1-view-in-mvc-4.html
Here is the sample code :
A ViewModel Created :
public class UsersLoginRegister
{
public User User { get; set; }
public Login Login { get; set; }
}
User Entity
public partial class User
{
public int UserID { get; set; }
[Required]
public string Username { get; set; }
[Required]
[DataType(System.ComponentModel.DataAnnotations.DataType.Password)]
public string Password { get; set; }
[Required]
public string FullName { get; set; }
}
Login Entity
public class Login
{
[Required]
public string UserName { get; set; }
[Required]
[DataType( System.ComponentModel.DataAnnotations.DataType.Password)]
public string Password { get; set; }
}
In the view (Html) code :
<table>
<tr>
<td><b>Login</b></td>
<td><b>Register</b></td>
</tr>
<tr>
<td>
#using (Html.BeginForm("Login", "Home", FormMethod.Post))
{
<table>
<tr>
<td>Username : </td>
<td>#Html.TextBoxFor(a => a.Login.Username)</td>
</tr>
<tr>
<td>Password : </td>
<td>#Html.EditorFor(a => a.Login.Password)</td>
</tr>
<tr>
<td></td>
<td> <input type="submit" value="Submit" /></td>
</tr>
</table>
}
</td>
<td>
#using (Html.BeginForm("Register", "Home", FormMethod.Post))
{
<table>
<tr>
<td>Fullname : </td>
<td>#Html.TextBoxFor(a => a.User.FullName)</td>
</tr>
<tr>
<td>Username : </td>
<td>#Html.TextBoxFor(a => a.User.Username)</td>
</tr>
<tr>
<td>Password : </td>
<td>#Html.EditorFor(a => a.User.Password)</td>
</tr>
<tr>
<td></td>
<td>
<input type="submit" value="Submit" />
</td>
</tr>
</table>
}
</td>
</tr>

Related

FullName from Identity is not working asp .net core 3.0

I am working on a small project and trying to display the fullname (which added by me on in ApplicationUser Model inhireted from IdentityUser) and below the Model:
public class ApplicationUser : IdentityUser
{
public int PersonnalNumber { get; set; }
public string FullName { get; set; }
}
here is SystemDetails Model have relationship with Application User:
public class SystemDetails
{
[Key]
public int Id { get; set; }
[Required]
[Display(Name = "System Name")]
public string SystemName { get; set; }
[Required]
[Display(Name = "Admin Name")]
public string SystemAdminId { get; set; }
public string ServerNames { get; set; }
[ForeignKey("SystemAdminId")]
public virtual ApplicationUser ApplicationUser { get; set; }
}
and here is the Index View:
#if (Model.Count() > 0)
{
<table class=" table table-striped border">
<tr class="table-secondary">
<th>
#Html.DisplayNameFor(m => m.SystemName)
</th>
<th>
#Html.DisplayNameFor(m => m.SystemAdminId)
</th>
<th></th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(m => item.SystemName)
</td>
<td>
#Html.DisplayFor(m => item.ApplicationUser.FullName)
</td>
<td>
<partial name="_TableButtonPartial" model="#item.Id" />
</td>
</tr>
}
Everything working fine except that FullName is not displayed on index page, I can display SystemAdminId which is the registered user Id but when i try to display the FullName using Applicationuser.FullName it is not displaying any thing! it is saved correctly on the database when I create record on SystemDetails Controller as below:
SystemAdminId
Here is create action:
[HttpPost,ActionName("Create")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> CreatePost()
{
List<ApplicationUser> userList = new List<ApplicationUser>();
if (!ModelState.IsValid)
{
return NotFound();
}
userList = await (from user in _db.ApplicationUser select user).ToListAsync();
ViewBag.usersList = userList;
_db.SystemDetails.Add(SystemDetails);
await _db.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
Appreciating any help
In controller , when querying the SystemDetails list you should load the related ApplicationUser :
using Microsoft.EntityFrameworkCore;
var result = _dbContext.SystemDetails.Include(f => f.ApplicationUser).ToList();

Using Include in Entity Framework Core

Question: How and where the extension method "Include", used in the Index() action method, in the following PostController used in the Inxex.cshtml view shown below? As I understand _context.Posts.Include(p => p.Blog) means include all posts that relate to blogs table. But I don't see use of blog class or blogId property in the Index.cshtml view below?
Background: In an ASP.NET MVC Core - Code First project I'm following this ASP.NET official site tutorial where they have following Model classes for Blog (parent) and Post (child). I then created a controller (shown below) using MVC Controller with Views, using Entity Framework wizard where I selected Post model in the Model dialogbox.:
Model:
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
namespace EFGetStarted.AspNetCore.NewDb.Models
{
public class BloggingContext : DbContext
{
public BloggingContext(DbContextOptions<BloggingContext> options)
: base(options)
{ }
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
}
PostController:
public class PostsController : Controller
{
private readonly BloggingContext _context;
public PostsController(BloggingContext context)
{
_context = context;
}
// GET: Posts
public async Task<IActionResult> Index()
{
var bloggingContext = _context.Posts.Include(p => p.Blog);
return View(await bloggingContext.ToListAsync());
}
}
Index.cshtml view for Index() action in PostController:
#model IEnumerable<ASP_Core_Blogs.Models.Post>
#{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Content)
</th>
<th>
#Html.DisplayNameFor(model => model.Title)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Content)
</td>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
<td>
<a asp-action="Edit" asp-route-id="#item.PostId">Edit</a> |
<a asp-action="Details" asp-route-id="#item.PostId">Details</a> |
<a asp-action="Delete" asp-route-id="#item.PostId">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Since you're not accessing the Blog property, it's better not to use Include(p => p.Blog). This will add an extra join that isn't required.
However, if you'll reference it in each table row, then it's preferred to include it to avoid lazy loading issues.

Insert value on different models in one view MVC 4

I have a model course, component, subcomponent and evaluation:
public class Course
{
public virtual int CourseId { get; set; }
(...)
public virtual List<Component> Components { get; set; }
public virtual List<UserCourse> Users { get; set; }
}
public class Component
{
public virtual int ComponentId { get; set; }
public virtual string Type { get; set; }
public virtual string Name { get; set; }
public virtual Course Course { get; set; }
public virtual List<Subcomponent> Subcomponents { get; set; }
}
public class Subcomponent
{
public virtual int SubcomponentId { get; set; }
public virtual int ComponentId { get; set; }
public virtual string TypeSub { get; set; }
public virtual string NameSub { get; set; }
(...)
public virtual int ParentId { get; set; }
public virtual Subcomponent Parent { get; set; }
public virtual List<Subcomponent> Childs { get; set; }
public virtual Component Component { get; set; }
}
public class Evaluation
{
public virtual int EvaluationId { get; set; }
public virtual int ComponentId { get; set; }
public virtual Component Component { get; set; }
public virtual int UserCourseId { get; set; }
public virtual UserCourse User { get; set; }
public virtual int Grade { get; set; }
}
I need to give a grade to each subcomponent (or component if the component doesn't have subcomponents) to each user.
First i have a view that shows the users and the grades already given, like an index:
#model SGP.Models.Course
<table>
<tr>
<th>
Users
</th>
<th>
Grades
</th>
</tr>
#foreach (var x in Model.Users)
{
<tr>
<td>
#Html.DisplayFor(modelItem => x.User.UserName)
</td>
#foreach (var y in x.Evaluation)
{
<td>
<table>
<tr>
<td>
<b>#Html.DisplayFor(modelItem => y.Component.NameComp)</b>
</td>
</tr>
<tr>
<td>
#Html.DisplayFor(modelItem => y.Grade)
</td>
</tr>
</table>
</td>
}
<td>
#Html.ActionLink("Give grade", "Comps", "Evaluation", new { id = x.UserCourseId }, null)
</td>
</tr>
}
</table>
Then when press "Give grade", i have a search box to select the component wich the grade will be given.
The search will give a list of subcomponents of that component. Each component can have one or more subcomponents so the final grade to the component is the average of the subcomponents grades.
The view of the subcomponents:
#model SGP.Models.Component
<table>
<tr>
<th>
#Html.DisplayFor(model => model.NameComp)
</th>
<th>
Grade
</th>
</tr>
#foreach (var item in Model.Subcomponents) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.NameSub)
</td>
<td>
???????
</td>
</tr>
}
</table>
But in this view i need to get the UserCourseId previously selected and give a grade to the user in the selected subcomponent. How can i do that?
Thanks
EDIT:
Give grade:
#using (Html.BeginForm("ListaSubs", "Evaluation", FormMethod.Post))
{
<table>
<tr>
<th>
#Html.TextBox("text")
</th>
<td>
<input type="submit" id="ButtonSearch" value="Search" />
</td>
</tr>
</table>
}
And controller:
[HttpPost, ActionName("ListaSubs")]
public ActionResult ListaSubs(string text)
{
var util = (db.Subcomponents.Where(m =>
m.ComponentId.Equals(text)));
return View("view", util);
}
What I do in this case, is to define a wrapper class and use it for view's model. Something like this:
public class MyWrapperClass
{
public Course _Course { get; set; }
public Component _Component { get; set; }
...
}
Then in your view, you define your model as:
#model MyWrapperClass
For some controls you can simply use the following format for the properties:
#Html.HiddenFor(model => model._Course.CourseId)
But in some cases, you probably have to create EditorTemplate or DisplayTemplate for each subclass and use the templates in the view.
To use Editor template, you need to use a UIHint attribute in the class definition.
Here is some links on how to use editor template in razor:
How to add and use a razor editor template
https://www.simple-talk.com/dotnet/asp-net/extending-editor-templates-for-asp-net-mvc/

Display UserName and Role in MVC4

I want to create an easy Admin panel for editing users roles. First I want to display users list with their roles.
First I edited some things in AccountModel:
Context:
public class UsersContext : DbContext
{
public UsersContext()
: base("DefaultConnection")
{
}
public DbSet<UserProfile> UserProfiles { get; set; }
public DbSet<UserRole> UserRole { get; set; }
}
User profile:
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string EmailId { get; set; }
public string Details { get; set; }
public virtual ICollection<UserRole> UserRole { get; set; }
}
User roles:
[Table("webpages_Roles")]
public class UserRole
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int RoleId { get; set; }
public string RoleName { get; set; }
public virtual ICollection<UserProfile> UserProfile { get; set; }
}
After that I created UserController with ActionResult Index:
public ActionResult Index()
{
using (var db = new UsersContext())
{
return View(db.UserProfiles.Include("UserRole").ToList());
}
}
And View:
#model IEnumerable<AnimeWeb.Models.UserProfile>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
User Name
</th>
<th>
User Role
</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.UserName)
</td>
<td>
#Html.DisplayFor(modelItem => item.UserRole)
</td>
</tr>
}
Partial View for UserRole looks like this:
#model AnimeWeb.Models.UserRole
<tr>
#Html.DisplayFor(model => model.RoleName),
</tr>
When I try to execute it I get InnerException error: "Invalid object name 'dbo.UserRoleUserProfiles'.". I dont quite get it. Could someone explain me why is this happening and how to resolve this?
Seems like the problem lies here
public virtual ICollection<UserRole> UserRole { get; set; }
And you don't have a mapping for these classes, so the default setting creates a UserRoleUserProfiles table(or class) and it doesn't exist in the database, so the problem occurs.
You can try remove this line of code and then try run the project again

ASP MVC3 Error - There is no ViewData item of type 'IEnumerable<SelectListItem>' that has the key

I've researched this a bit and haven't found an answer that quite deals with a similar situation or MVC3. In the ViewModel I'm using I have a Lists of a separate model (List<AgentId> which is a list of the AgentId model).
In the Create page for this controller, I need an input section for 5 items to be added to this list. However, before the page even load, I receive this error message:
There is no ViewData item of type 'IEnumerable<SelectListItem>' that has the key 'BankListAgentId[0].StateCode'.
Here is the ViewModel I am using:
public class BankListViewModel
{
public int ID { get; set; }
public string ContentTypeID1 { get; set; }
public string CreatedBy { get; set; }
public string MANonresBizNY { get; set; }
public string LastChangeOperator { get; set; }
public Nullable<System.DateTime> LastChangeDate { get; set; }
public List<BankListAgentId> BankListAgentId { get; set; }
public List<BankListStateCode> BankListStateCode { get; set; }
}
And here is the section of the view that's having the issues:
<fieldset>
<legend>Stat(s) Fixed</legend>
<table>
<th>State Code</th>
<th>Agent ID</th>
<th></th>
<tr>
<td>
#Html.DropDownListFor(model => model.BankListAgentId[0].StateCode,
(SelectList)ViewBag.StateCode, " ")
</td>
<td>
#Html.EditorFor(model => model.BankListAgentId[0].AgentId)
#Html.ValidationMessageFor(model => model.BankListAgentId[0].AgentId)
</td>
</tr>
<tr>
<td>
#Html.DropDownListFor(model => model.BankListAgentId[1].StateCode,
(SelectList)ViewBag.StateCode, " ")
</td>
<td>
#Html.EditorFor(model => model.BankListAgentId[1].AgentId)
#Html.ValidationMessageFor(model => model.BankListAgentId[1].AgentId)
</td>
<td id="plus2" class="more" onclick="MoreCompanies('3');">+</td>
</tr>
</table>
</fieldset>
I believe #Html.DropDownListFor() is expecting an IEnumerable<SelectListItem>, you can bind it the following way:
In your ViewModel:
public class BankListViewModel
{
public string StateCode { get; set; }
[Display(Name = "State Code")]
public IEnumerable<SelectListItem> BankListStateCode { get; set; }
// ... other properties here
}
In your Controller load the data:
[HttpGet]
public ActionResult Create()
{
var model = new BankListViewModel()
{
// load the values from a datasource of your choice, this one here is manual ...
BankListStateCode = new List<SelectListItem>
{
new SelectListItem
{
Selected = false,
Text ="Oh well...",
Value="1"
}
}
};
return View("Create", model);
}
And then in the View bind it:
#Html.LabelFor(model => model.BankListStateCode)
#Html.DropDownListFor(model => model.StateCode, Model.BankListStateCode)
I hope this helps. Let me know if you nee clarifications.
This error wound up being thrown because the ViewBag element I was using had the same name as one of the list item properties.
The solution was to change ViewBag.StateCode to ViewBag.StateCodeList.

Resources