How to use 2 Models in a View - c#

How can I use Two different models into the same view?
Why I need this?
I need to do a create view where the client fills his registry, and I need to have 1 combo box/select form(This form needs to load the strings from the other model).
Here is what I have tried:
Creating a ViewModel for both of the Models that looks like this:
public class CandidateViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public int Number { get; set; }
public string Profile { get; set; }
public Byte[] CV { get; set; }
public string CVNAME { get; set; }
public List<Profile> ProfileList { get; set; }
public string ProfileText { get; set; }
}
ProfileList and ProfileText are from ProfileModel, the rest is from the CandidateModel.
My controller right now looks like this:
public IActionResult Candidate(Candidate candidate, string searchString)
{
using (var aplicationDbContext = new ApplicationContext())
{
var candidates = from m in aplicationDbContext.Candidates select m;
if (!String.IsNullOrEmpty(searchString))
{
candidates = candidates.Where(s => s.Name.Contains(searchString) || s.Number.ToString().Contains(searchString) || s.ProfileText.Contains(searchString));
}
return View(candidates.ToList());
}
}
public IActionResult CandidateCreate()
{
using (var applicationcontext = new ApplicationContext())
{
var ProfileTextFromProfile = applicationcontext.Candidates.Include(q => q.ProfileList);
return View(ProfileTextFromProfile);
}
return View();
}
[HttpPost, ActionName("CandidateCreate")]
[ValidateAntiForgeryToken]
public IActionResult CandidateCreatePost([Bind("Name,Number,Profile,CV,CVID")] Candidate candidate, IFormFile CV,string profileText, int Id)
{
if (ModelState.IsValid)
{
if (CV != null)
{
if (CV.Length > 0)
{
byte[] p1 = null;
using (var fs1 = CV.OpenReadStream())
using (var ms1 = new MemoryStream())
{
fs1.CopyTo(ms1);
p1 = ms1.ToArray();
}
candidate.CVNAME = CV.FileName;
candidate.CV = p1;
}
}
using (var applicationcontext = new ApplicationContext())
{
var ProfileTextFromProfile = applicationcontext.Profile.Include(q => q.ProfileList);
//var ProfileTextFromProfile = applicationcontext.Profile.Include(q => q.ProfileText).Single(q => q.Id == Id);
//ProfileTextFromProfile.ProfileText.Add(new Candidate() { Profile = profileText });
}
candidateRepository.Add(candidate);
candidateRepository.SaveChanges();
return RedirectToAction("Candidate");
}
return View();
}
But I don't know what acctually I need to do after this, I'm really new to this and I'm doing this work as an intership soo I'm still learning, If have any doubts about my question please ask me and I will try to explain.
Also here is my View where I need to use both of the models.
#*model HCCBPOHR.Data.Candidate*#
#model HCCBPOHR.DomainModel.CandidateModel
#{
ViewData["Title"] = "CandidateCreate";
}
<h2>CandidateCreate</h2>
<h4>Candidate</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post" enctype="multipart/form-data" asp-action="CandidateCreate">
<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="Number" class="control-label"></label>
<input asp-for="Number" class="form-control" maxlength="9" />
<span asp-validation-for="Number" class="text-danger"></span>
</div>
<div class="form-group">
<label>Selects</label>
<select asp-for="Profile" class=" form-control ">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</select>
</div>
<div class="form-group">
<label asp-for="CV" type="file" class="control-label"></label>
<input asp-for="CV" type="file" class="form-control" />
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" onclick="this.disabled=true;this.form.submit();" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>

You can apply MVVM pattern in this scenario.
Here is example:
I will define 3 class in Model folder
public class Post
{
public string Id {get;set;}
public string Content {get;set;}
public string UserId {get;set;}
}
public class Comment
{
public string Id {get;set;}
public string Content {get;set;}
public string PostId {get;set;}
}
// this will hold data for 2 class above
public class PostVM
{
public Post Post {get;set}
public Comment Comment {get;set}
}
Then in my controller I will query to db to get data for post and comment like this
public IActionResult PostDetail(string postId)
{
var post = _postRepository.GetPostById(postId);
var comment = _commentRepository.GetCommentByPostId(postId);
// MVVM model return to the view
var vM = new PostVM
{
Post = post,
Comment = comment
}
}
Finally in my view
#model PostVM
<div> #model.Post.Content </div>
#foreach(var comment in #model.Comment)
{
<div> #comment.Content</div>
}
Please adjust accordingly with your code. If you have any problem please let me know.
Cheers

Related

ASP.NET Tag helpers and Bootstrap Dropdown. How to use together?

Faced such peculiar problem. For a better understanding, I will try to describe in more detail.
I have two metod in ArticleController:
[HttpGet]
public async Task<IActionResult> Create()
{
var sources = await _sourceServices.GetSourceNameAndId();
var listSources = new List<SourceNameAndIdModel>();
foreach (var source in sources)
{
listSources.Add(_mapper.Map<SourceNameAndIdModel>(source));
}
var viewModel = new ArticleCreateViewModel()
{
SourceNameAndIdModels = listSources
};
return View(viewModel);
}
[HttpPost]
public async Task<IActionResult> Create(ArticleCreateViewModel viewModel)
{
await _articleService.CreateArticle(_mapper.Map<ArticleDTO>(viewModel));
return RedirectToAction("Index", "Article");
}
As you can see, in the Get-method, I get the names and ids of all Sources from the database via _sourceService in the form of IEnumerable :
public class SourceNameAndIdDTO
{
public Guid Id { get; set; }
public string Name { get; set; }
}
Next, I enumerate them in a foreach loop and add each SourceNameAndIdDTO object to the List listSources I created before:
public class SourceNameAndIdModel
{
public string Name { get; set; }
public Guid Id { get; set; }
}
Next, I create an instance of the ArticleCreateViewModel model, which I will use further in the View:
public class ArticleCreateViewModel
{
public Guid Id { get; set; } = Guid.NewGuid();
public string Title { get; set; }
public string Description { get; set; }
public string Body { get; set; }
public Guid SourceId { get; set; }
public DateTime CreationDate { get; set; }
public List<SourceNameAndIdModel> SourceNameAndIdModels { get; set; }
}
And I assign to the field public List SourceNameAndIdModels { get; set; } List listSources values:
var viewModel = new ArticleCreateViewModel()
{
SourceNameAndIdModels = listSources
};
You can see this in the controller code I posted above. Next, I send the viewModel to the View.
Code of my View:
#model ArticleCreateViewModel
<div class="container">
<h2>Edit article</h2>
<div class="row gx-5">
<form asp-controller="Article" asp-action="Create" asp-antiforgery="true" method="post">
<div>
<input type="hidden" asp-for="Id" />
<div class="mb-3">
<label class="col-sm-2 col-form-label" asp-for="Title"></label>
<input class="form-control" type="text" asp-for="Title">
</div>
<div class="mb-3">
<label class="col-sm-2 col-form-label" asp-for="Description"></label>
<textarea class="form-control" asp-for="Description"></textarea>
</div>
<div class="mb-3">
<label class="col-sm-2 col-form-label" asp-for="Body"></label>
<textarea class="form-control" asp-for="Body"></textarea>
</div>
<div class="dropdown">
<a class="btn btn-secondary dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Source
</a>
<ul class="dropdown-menu">
#foreach (var item in #Model.SourceNameAndIdModels)
{
<li><select class="dropdown-item" asp-for="SourceId" asp-items="#item.Id">#item.Name</select></li>
}
</ul>
</div>
<div class="mb-3">
<label class="col-sm-2 col-form-label" asp-for="CreationDate"></label>
<input type="datetime-local" class="form-control" asp-for="CreationDate">
</div>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>
And finally, in this place of the code, in which I want to set the Source of the article I am creating, I have an error:
enter image description here
Can you please tell me how in my case to make friends with my code with dropdown on my request? What am I doing wrong?
Using asp-items in this way is incorrect.
The Select Tag Helper asp-items specifies the option elements
Details and example:
https://learn.microsoft.com/...netcore-6.0#the-select-tag-helper

How can I add Blog post with PostId and CategoryId in PostCategory?

i have a question. Im doing Blog on mvc core. I created 3 entities.Post, Category and PostCategory. When i want to create blog post, I wanna add on PostCategory tables with PostId and CategoryId.I done for EditPost its working but I didnt for CreatePost method. I need help. Let me show my codes.
Its my entities.
public class Post : IEntity
{
public int Id { get; set; }
public string Title { get; set; }
public string Text { get; set; }
public DateTime DateTime { get; set; }
public string ImageUrl { get; set; }
public List<PostCategory> PostCategories { get; set; }
}
public class Category : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public List<PostCategory> PostCategories { get; set; }
}
public class PostCategory:IEntity
{
public int PostId { get; set; }
public Post Post { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
}
I done settings in BlogContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<PostCategory>()
.HasKey(c => new { c.CategoryId, c.PostId });
}
public DbSet<Post> Posts { get; set; }
public DbSet<Category> Categories { get; set; }
I wanna show PostService in EfCorePostDal.(I think i should be fix here)
public void Create(Post entity, int [] categoryIds)
{
using (var context = new BlogContext())
{
var post = context.Posts
.Include(x => x.PostCategories)
.ThenInclude(x => x.Category)
.FirstOrDefault();
if (post != null)
{
post.Title = entity.Title;
post.Text = entity.Text;
post.ImageUrl = entity.ImageUrl;
post.DateTime = entity.DateTime;
post.PostCategories = categoryIds.Select(categoryId => new PostCategory()
{
CategoryId = categoryId,
PostId = entity.Id
}).ToList();
}
context.SaveChanges();
}
}
Its my AdminController.I try to take categoryIds.When i check in debug.I can do it.I created in PostModel in WebUI by the way
public ActionResult CreatePost()
{
ViewBag.Categories = _categoryService.GetAll();
return View(new PostModel() { });
}
[HttpPost]
public async Task<ActionResult> CreatePost(PostModel model, IFormFile file,int[] categoryIds)
{
if (ModelState.IsValid)
{
var entity = new Post
{
Title = model.Title,
Text = model.Text,
DateTime = model.DateTime,
ImageUrl = model.ImageUrl,
};
if (file != null)
{
entity.ImageUrl = file.FileName;
var path = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot\\img", file.FileName);
using (var stream = new FileStream(path, FileMode.Create))
{
await file.CopyToAsync(stream);
}
}
if (_postService.Create(entity,categoryIds))
{
return RedirectToAction("ListPosts", "Admin");
}
return View(model);
}
ViewBag.Categories = _categoryService.GetAll();
return View(model);
}
My PostModel
public class PostModel
{
public int Id { get; set; }
public string Title { get; set; }
public string Text { get; set; }
public DateTime DateTime { get; set; }
public string ImageUrl { get; set; }
public List<Category> SelectedCategories { get; set; }
}
and Its my Createpost.cshtml
<form asp-controller="Admin" asp-action="CreatePost" method="post" enctype="multipart/form-data">
<div asp-validation-summary="All" class="text-danger"></div>
<input type="hidden" name="Id" value="#Model.Id" />
<div class="row">
<div class="col-md-8">
<div class="form-group row">
<label asp-for="Title" class="col-md-2 col-form-label"></label>
<div class="col-md-10">
<input asp-for="Title" value="" class="form-control" />
</div>
</div>
<div class="form-group row">
<label asp-for="DateTime" class="col-md-2 col-form-label"></label>
<div class="col-md-10">
<input asp-for="DateTime" value="" class="form-control" />
</div>
</div>
<div class="form-group row">
<label asp-for="ImageUrl" class="col-md-2 col-form-label"></label>
<div class="col-md-10">
<input type="file" name="file" value="" />
</div>
</div>
</div>
<div class="col-md-4">
#foreach (var item in (List<Category>)ViewBag.Categories)
{
<div class="form-check">
<input type="checkbox"
name="categoryIds"
value="#item.Id"
class="form-check-input"
id="category#(item.Id)">
<label class="form-check-label" for="category#(item.Id)">#item.Name</label>
</div>
}
</div>
<div class="col-md-12">
<div class="form-group row">
<div class="col-md-12">
<textarea asp-for="Text" class="form-control"></textarea>
</div>
</div>
</div>
</div>
<div class="form-group row">
<div class="col-md-12">
<button type="submit" class="btn btn-success btn-block">Share</button>
</div>
</div>
Finally what I should ? When i add post its not working. (Im new developer sorry for my basic error)
According to your description and codes, I found you update the post instead of creating a new post record.
I suggest you could try to below codes instead to create the new post record.
Notice: I directly use the dbcontext with DI in the CreatePost method, you could modify the Create method by yourself according to my codes.
[HttpPost]
public async Task<ActionResult> CreatePost(PostModel model, IFormFile file, int[] categoryIds)
{
int[] test = new int[] {1,2,3,4,5 };
if (ModelState.IsValid)
{
var entity = new Post
{
Title = model.Title,
Text = model.Text,
DateTime = model.DateTime,
ImageUrl = model.ImageUrl,
};
var reparePart1 = test.Select(categoryId => new PostCategory()
{
CategoryId = categoryId,
Post = entity
}).ToList();
entity.PostCategories = reparePart1;
_dbContext.Add(entity);
_dbContext.SaveChanges();
if (file != null)
{
entity.ImageUrl = file.FileName;
//var path = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot\\img", file.FileName);
//using (var stream = new FileStream(path, FileMode.Create))
//{
// await file.CopyToAsync(stream);
//}
}
//Create(entity, categoryIds);
return View(model);
}
//ViewBag.Categories = _categoryService.GetAll();
return View(model);
}

Many to Many relationship in ASP.NET CORE 3.1 by repository pattern

I've made many to many relationship in ASP.NET Core and there are two tables Category and Subject
This is Category Model
public class Category
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public List<CategorySubject> CategorySubjects { get; set; } = new List<CategorySubject>();
}
This is subject model
public class Subject
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Exam_Time { get; set; }
public List<CategorySubject> CategorySubjects { get; set; }
}
This is CategorySubject Model
public class CategorySubject
{
public int CategoryId { get; set; }
public int SubjectId { get; set; }
public Category Category { get; set; }
public Subject Subject { get; set; }
}
This is part of DatabaseContext
public DbSet<CategorySubject> CategorySubjects { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<CategorySubject>().HasKey(pt => new { pt.CategoryId, pt.SubjectId });
modelBuilder.Entity<CategorySubject>().HasOne(pt => pt.Category)
.WithMany(pt => pt.CategorySubjects).HasForeignKey(p => p.CategoryId);
modelBuilder.Entity<CategorySubject>().HasOne(pt => pt.Subject)
.WithMany(pt => pt.CategorySubjects).HasForeignKey(p => p.SubjectId);
}
I made one helper class by the name of Helper
public class Helpers:Profile
{
public Helpers()
{
CreateMap<Subject, SubjectViewModel>().ReverseMap();
CreateMap<SubjectViewModel, Subject>();
CreateMap<Category, CategoryViewModel>().ReverseMap();
}
}
this is category service:
public void Insert(Category category)
{
_context.Categories.Add(category);
}
public void Update(Category category)
{
_context.Categories.Update(category);
}
This is CategoryController :
// GET: CategoryController/Create
public IActionResult Create()
{
var subjectFromRepo = _categorySubject.Subject.GetAll();
var selectList = new List<SelectListItem>();
foreach (var item in subjectFromRepo)
{
selectList.Add(new SelectListItem(item.Name, item.Id.ToString()));
}
var vm = new CategoryViewModel()
{
Subjects = selectList
};
return View(vm);
}
// POST: CategoryController/Create
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(CategoryViewModel vm )
{
try
{
Category category = new Category()
{
Name = vm.Name
};
foreach(var item in vm.SelectedSubjects)
{
category.CategorySubjects.Add(new CategorySubject()
{
SubjectId = Int32.Parse(item)
});
}
_categorySubject.Category.Insert(category);
_categorySubject.Save();
return RedirectToAction(nameof(Index));
}
catch
{
return View();
}
}
// GET: CategoryController/Edit/5
public IActionResult Edit(int id)
{
var category = _categorySubject.Category.GetCategoryById(id);
var subjects = _categorySubject.Subject.GetAll();
var selectsubjects = category.CategorySubjects.Select(x => new Subject()
{
Id = x.Subject.Id,
Name = x.Subject.Name
});
var selectlist = new List<SelectListItem>();
subjects.ForEach(i => selectlist.Add(new SelectListItem(i.Name, i.Id.ToString(),
selectsubjects.Select(x => x.Id).Contains(i.Id))));
var vm = new CategoryViewModel()
{
Id= category.Id,
Name = category.Name,
Subjects = selectlist
};
return View(vm);
}
// POST: CategoryController/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(CategoryViewModel vm)
{
try
{
var category = _categorySubject.Category.GetCategoryById(vm.Id);
category.Name = vm.Name;
var selectedSubjects = vm.SelectedSubjects;
var existingSubjects = category.CategorySubjects.Select(x => x.SubjectId.ToString()).ToList();
var toAdd = selectedSubjects.Except(existingSubjects).ToList();
var toRemove = existingSubjects.Except(selectedSubjects).ToList();
var CategorySubjects = category.CategorySubjects.Where(x => !toRemove.Contains(x.SubjectId.ToString())).ToList();
foreach (var item in toAdd)
{
category.CategorySubjects.Add(new CategorySubject()
{
SubjectId = Int32.Parse(item),
CategoryId = Int32.Parse(item)
});
}
_categorySubject.Save();
return RedirectToAction("Index", "Category");
}
catch
{
return View();
}
}
This is Create.cshtml of Category :
<div class="style-form">
<h2 class="text-center mt-3 mb-lg-3">Create New Category</h2>
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="row">
<div class="col-md-3 col-lg-3 col-sm-3"></div>
<div class="col-md-6 col-lg-6 col-sm-6">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><strong>Name:</strong></span>
</div>
<input asp-for="Name" class="form-control input-hover" placeholder="Enter Name.." />
<span asp-validation-for="Name" class="text-danger"></span>
</div><br />
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><strong>Subject:</strong></span>
</div>
<select asp-for="SubjectId" class="form-control input-hover" asp-items="#Model.Subjects ">
<option value="">Please choose a Subject...</option>
</select>
<span asp-validation-for="SubjectId" class="text-danger"></span>
</div><br />
</div>
</div>
<div class="row">
<div class="col-md-3 col-lg-3 col-sm-3"></div>
<div class="col-md-6 col-lg-6 col-sm-6">
<div class="form-group">
<button type="button" class="btn btn-backToList">
<a asp-action="Index">Back to List</a>
</button>
<button type="submit" class="btn btn-create">Create</button>
</div>
</div>
</div>
</form>
There when I click on the create new category button I can get data of subject form drop down list, but when I want to submit it I face this error:
NullReferenceException: Object reference not set to an instance of an object.
AspNetCore.Views_Category_Create.b__20_0() in Create.cshtml, line 27
<select asp-for="SubjectId" class="form-control input-hover" asp-items="#Model.Subjects ">
I think there is an exception thrown in the Create (POST) method, it then goes to the catch, which returns a view without a model
catch
{
return View();
}
The next exception comes while rendering the page trying to bind to #Model.Subjects where Model is null.
Remove try/catch or handle the catch to find if there is any exception.

CRUD with Many to Many relationships in Entity Framework Core

I have two models and one join table.
I would like to use the same view to edit and create new purchase order while adding items in the same form.
PurchaseOrder Model:
public class PurchaseOrder
{
public int ID { get; set; }
[Required]
public string Requester { get; set; }
public int WorkOrder { get; set; }
[Required]
public string WorkSite { get; set; }
public string Equipment { get; set; }
public string Operator { get; set; }
public string Remarks { get; set; }
public ICollection<PurchaseOrderItem> Items { get; set; }
}
Item Model:
public class Item
{
public int ID { get; set; }
public string PartNumber { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
[Required]
public int Quantity { get; set; }
public string Remarks { get; set; }
public ICollection<PurchaseOrderItem> PurchaseOrders { get; set; }
}
Join Table Entity
public class PurchaseOrderItem
{
public int PurchaseOrderID { get; set; }
public int ItemID { get; set; }
public PurchaseOrder PurchaseOrder { get; set; }
public Item Item { get; set; }
}
PurchaseOrdersController Edit:
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var purchaseOrder = await _context.PurchaseOrders
.Include(x => x.Items)
.ThenInclude(x => x.Item)
.Where(x => x.ID == id)
.AsNoTracking()
.SingleOrDefaultAsync();
if (purchaseOrder == null)
{
return NotFound();
}
return View(purchaseOrder);
}
Edit Post Method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, PurchaseOrder order)
{
if (id != order.ID)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
var po = _context.PurchaseOrders.FirstOrDefault(i => i.ID == order.ID);
po.Requester = order.Requester;
po.WorkOrder = order.WorkOrder;
po.WorkSite = order.WorkSite;
po.Equipment = order.Equipment;
po.Operator = order.Operator;
po.Remarks = order.Remarks;
_context.Update(po);
foreach (var i in order.Items)
{
var item = _context.Items.FirstOrDefault(n => n.ID == i.Item.ID);
item.PartNumber = i.Item.PartNumber;
item.Name = i.Item.Name;
item.Description = i.Item.Description;
item.Quantity = i.Item.Quantity;
item.Remarks = i.Item.Remarks;
_context.Update(item);
}
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!PurchaseOrderExists(order.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(order);
}
Edit View
#model OrderTrackingApp.Models.PurchaseOrder
#{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>PurchaseOrder</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="ID" />
<div class="form-group">
<label asp-for="Requester" class="control-label"></label>
<input asp-for="Requester" class="form-control" />
<span asp-validation-for="Requester" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="WorkOrder" class="control-label"></label>
<input asp-for="WorkOrder" class="form-control" />
<span asp-validation-for="WorkOrder" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="WorkSite" class="control-label"></label>
<input asp-for="WorkSite" class="form-control" />
<span asp-validation-for="WorkSite" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Equipment" class="control-label"></label>
<input asp-for="Equipment" class="form-control" />
<span asp-validation-for="Equipment" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Operator" class="control-label"></label>
<input asp-for="Operator" class="form-control" />
<span asp-validation-for="Operator" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Remarks" class="control-label"></label>
<input asp-for="Remarks" class="form-control" />
<span asp-validation-for="Remarks" class="text-danger"></span>
</div>
<div class="form-group">
<label class="control-label">Order Items</label>
<table>
<tbody>
#{ int i = 0;}
<tr>
#foreach (var OrderItem in Model.Items)
{
<td>
<input type="hidden" value="OrderItem[#i].ItemID" asp-for="#OrderItem.ItemID"
class="form-control" />
<input type="text" value="OrderItem[#i].Item.PartNumber" asp-for="#OrderItem.Item.PartNumber" class="form-control" />
<input type="text" value="OrderItem[#i].Item.Name" asp-for="#OrderItem.Item.Name" />
</td>
i++;
}
</tr>
</tbody>
</table>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
I have two problems currently with the Edit:
The label for the item populates as follows: OrderItem[0].Item.Name instead of Value
When the code reaches the foreachloop to iterate over items, it is throwing a null exception.
You are getting name only as for newly added child items, you don't have ids. and getting null values as in returned data for relational table ids of child data are not initialized.
you can solve this issue in tow ways as per suitability:
Add child rows from view directly and update ids in model, so you
will always have ids in data and you will not need face null ids
issue.
Alternatively, You may need to add new values of child table first
then use new ids in relational data manually before saving changes.
The problem is model binding, it is based on the name attribute. Change the code like below:
#{ int i = 0;}
<tr>
#foreach (var OrderItem in Model.Items)
{
<td>
<input type="hidden" name="Items[#i].Item.ID" asp-for="#OrderItem.Item.ID"
class="form-control" />
<input type="text" name="Items[#i].Item.PartNumber" asp-for="#OrderItem.Item.PartNumber" class="form-control" />
<input type="text" name="Items[#i].Item.Name" asp-for="#OrderItem.Item.Name" class="form-control" />
</td>
i++;
}
</tr>

ASP.NET MVC View needs to display data from a certain model and at the same time take input data for another model

I have a View which uses a dynamic object to pull data from multiple models declared in the ViewModel. However, within the same model I am trying to take user input via a form. The data is correctly displayed for the 2 models which are also part of the dynamic object. But I am UNSUCCESSFUL getting the form input, because I keep getting the error that the dynamic object is not accessible.[And this is for the form only.]
Here is how the View looks like
#model dynamic
#using ActionAugerMVC.ViewModels
#addTagHelper "*,Microsoft.AspNetCore.Mvc.TagHelpers"
<div class="course__title">
#Model.item.PageTitle
</div>
<p class="course__desc">
#Html.Raw(Model.item.PageText)
</p>
<div class="event__price">
<h3>#Model.item.NoticeTitle</h3>
<p>#Model.item.NoticeNeedItem</p>
<button type="submit" class="btn btn-accent">
Get A Quote Now
</button>
</div>
<h3 class="course_desc__title">Other Services</h3>
<ul class="course_requirements__list multi-column">
#foreach (var link in Model.data)
{
<li><i class="ion-android-arrow-forward"></i> #Html.ActionLink((string)link.PageTitle, "Page", "Plumbing", new { id = link.ID, url = link.PageURL }) </li>
}
</ul>
<div class="sidebar__item">
<p class="subheading">Instant Quote Request</p>
<form class="register__form" role="form" asp-controller="Plumbing" asp-action="Page" method="post">
<div class="text-danger" asp-validation-summary="All"></div>
<div class="form-group">
<label class="sr-only">Full Name </label>
<input asp-for="#Model.quote.FullName" type="text" class="form-control" placeholder="Full name">
</div>
<div class="form-group">
<label class="sr-only">Your phone</label>
<input asp-for="#Model.quote.Phone" type="tel" class="form-control" placeholder="Your phone">
<span asp-validation-for="#Model.quote.Phone" class="text-danger"></span>
</div>
<div class="form-group">
<label class="sr-only">E-mail</label>
<input asp-for="#Model.quote.Email" type="email" class="form-control" placeholder="E-mail">
<span asp-validation-for="#Model.quote.Email" class="text-danger"></span>
</div>
<div class="form-group">
<label class="sr-only">Your Message</label>
<input asp-for="#Model.quote.Message" type="text" class="form-control" placeholder="Your Message">
</div>
<input type="submit" value="Get a Quote Now" class="btn btn-accent btn-block">
</form>
</div> <!-- .sidebar__item -->
And here is how the controller looks like :-
public class PlumbingController : Controller
{
private readonly ActionAugerDataContext actionAugerDbContext;
private readonly UnitOfWork unitOfWork;
private readonly PlumbingPageViewModel plumbingViewModel;
public PlumbingController(ActionAugerDataContext context)
{
actionAugerDbContext = context;
unitOfWork = new UnitOfWork(actionAugerDbContext);
plumbingViewModel = new PlumbingPageViewModel(unitOfWork);
}
// GET: /<controller>/
public IActionResult Index()
{
var data = plumbingViewModel.PlumbingContent;
return View(data);
}
[HttpGet]
[Route("plumbing/calgary-{url}")]
public IActionResult Page(int ID, string url)
{
dynamic Page = new ExpandoObject();
Page.item = unitOfWork.ContentRepository.GetById(ID);
Page.data = plumbingViewModel.PlumbingContent;
Page.cities = plumbingViewModel.Cities;
// Page.quote = plumbingViewModel.Quote;
return View(Page);
}
[HttpPost]
public IActionResult Page(Quote quote)
{
return View();
}
}
Here is the View Model :-
public class PlumbingPageViewModel
{
public IEnumerable<Content> PlumbingContent { get; set; }
public IEnumerable<Cities> Cities { get; set; }
public Quote Quote { get; set; }
public PlumbingPageViewModel(UnitOfWork unitOfWork)
{
PlumbingContent = unitOfWork.ContentRepository
.GetAll()
.Where(d => d.Section == "Plumbing")
.Where(c => c.City == "Calgary");
Cities = unitOfWork.CitiesRepository
.GetAll()
.Where(c => c.HomeCity == "Calgary");
}
}
And here is the model class for the form.
public class Quote
{
public int ID { get; set; }
public string FullName { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public string City { get; set; }
public string Message { get; set; }
}
you can't you use dynamic model ( #model dynamic) for building your form with HtmlHelper
If you want Post form you should specific model.
Hope you this will you.

Categories