I am using ASP.NET CORE with SQL server.
I have 2 tables: Review and WorkSchedule. The WorkSchedule table contains ManagerID and StaffID. The Review table contains ReviewerID and RevieweeID. The ManagerID,StaffID,ReviewerIDandRevieweeID are foreign keys to StaffID in the Staff table.
For example, if I create a new review in the CreateReview page, I will manually set the ReviewerID and RevieweeID. I want to make the WorkScheduleID dropdown to automatically change so that WorkScheduleID will appear where ManagerID = ReviewerID and StaffID = RevieweeID
Model:
namespace Starbucks.Models
{
public class ReviewModel
{
[Key]
[Display(Name = "Review ID")]
public Guid ReviewID { get; set; }
[Display(Name = "Reviewer")]
public Guid ReviewerID { get; set; }
[ForeignKey("ReviewerID")]
public StaffModel Staff { get; set; }
[Display(Name = "Reviewee")]
public Nullable<Guid> RevieweeID { get; set; }
[ForeignKey("RevieweeID")]
public StaffModel Staffs { get; set; }
[Display(Name = "WorkSchedule ID")]
public Nullable<Guid> WorkScheduleID { get; set; }
[ForeignKey("WorkScheduleID")]
public WorkScheduleModel WorkSchedules { get; set; }
}
public class WorkScheduleModel
{
[Key]
[Display(Name = "Work Schedule ID")]
public Guid WorkScheduleID { get; set; }
[Display(Name = "Staff ID")]
public Nullable<Guid> StaffID { get; set; }
[ForeignKey("StaffID")]
public StaffModel Staffs { get; set; }
[Display(Name = "Manager ID")]
public Guid ManagerID { get; set; }
[ForeignKey("ManagerID")]
public StaffModel Staff { get; set; }
}
}
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> CreateReview([Bind("ReviewID,ReviewCategoryID,ReviewRating,ReviewComment,ReviewDateTime,ReviewerID,RevieweeID,WorkScheduleID")] ReviewModel reviewModel)
{
if (ModelState.IsValid)
{
reviewModel.ReviewID = Guid.NewGuid();
reviewModel.ReviewDateTime = DateTime.Now;
_context.Add(reviewModel);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(ProfilePage));
}
ViewData["RevieweeID"] = new SelectList(_context.Staff, "StaffID", "StaffName", reviewModel.RevieweeID);
ViewData["ReviewerID"] = new SelectList(_context.Staff, "StaffID", "StaffName", reviewModel.ReviewerID);
ViewData["WorkScheduleID"] = new SelectList(_context.WorkSchedule, "WorkScheduleID", "WorkScheduleID");
return View(reviewModel);
}
View Page:
<body>
<div class="row">
<div class="col-md-8">
<form asp-action="CreateReview">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="ReviewerID" class="control-label"></label>
<select asp-for="ReviewerID" class="form-control" asp-items="ViewBag.ReviewerID"></select>
</div>
<div class="form-group">
<label asp-for="RevieweeID" class="control-label"></label>
<select asp-for="RevieweeID" class="form-control" asp-items="ViewBag.RevieweeID"></select>
</div>
<div class="form-group">
<label asp-for="WorkScheduleID" class="control-label"></label>
<select asp-for="WorkScheduleID" class="form-control" asp-items="ViewBag.WorkScheduleID"></select>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<input type="submit" value="Save" class="btn btn-primary btn-block" />
</div>
</div>
</form>
</div>
</div>
</body>
You can create a method to filter the data, and use ajax to call it, then populate the select list with the returned data.
Controller:
public IActionResult Index()
{
ViewData["RevieweeID"] = new SelectList(_context.Staff, "StaffID", "StaffName");
ViewData["ReviewerID"] = new SelectList(_context.Staff, "StaffID", "StaffName");
ViewData["WorkScheduleID"] = new SelectList(_context.WorkSchedule, "WorkScheduleID", "WorkScheduleID");
return View();
}
[HttpGet]
public IActionResult FilterWorkScheduleID(Guid reviewerid, Guid revieweeid)
{
var WorkScheduleIDs = _context.WorkSchedule.Where(x => x.StaffID == revieweeid && x.ManagerID == reviewerid).ToList();
return Json(new { items = WorkScheduleIDs });
}
View:
#model ReviewModel
#{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<div class="row">
<div class="col-md-8">
<form asp-action="CreateReview">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="ReviewerID" class="control-label"></label>
<select asp-for="ReviewerID" class="form-control" asp-items="ViewBag.ReviewerID"></select>
</div>
<div class="form-group">
<label asp-for="RevieweeID" class="control-label"></label>
<select asp-for="RevieweeID" class="form-control" asp-items="ViewBag.RevieweeID"></select>
</div>
<div class="form-group">
<label asp-for="WorkScheduleID" class="control-label"></label>
<select asp-for="WorkScheduleID" class="form-control" asp-items="ViewBag.WorkScheduleID"></select>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<input type="submit" value="Save" class="btn btn-primary btn-block" />
</div>
</div>
</form>
</div>
</div>
#section scripts{
<script>
$("select").on("change", function () {
var reviewerID = $("#ReviewerID").find(":selected").val();
var revieweeID = $("#RevieweeID").find(":selected").val();
$.ajax({
type: 'get',
url: '/Staff/FilterWorkScheduleID',
data: {
reviewerid: reviewerID,
revieweeid:revieweeID
},
success: function (result) {
$("#WorkScheduleID").empty();
$.each(result.items, function (i, obj) {
var option = "<option value=" + obj.workScheduleID + ">" + obj.workScheduleID + "</option>";
$(option).appendTo("#WorkScheduleID");
})
}
})
})
</script>
}
Result:
Related
I'm quite new to ASP.NET Core MVC and I'm having trouble retrieving a DateTime value from the database into the 'Edit' razor view.
I can use the scaffolded views to create a new Activity Item and this displays correctly in the 'Index' list, and in the 'Details' view, but when I attempt to 'Edit' the entry the DateTime value doesn't pull through from the database.
I've done plenty of reading but the main thing I seem to get in search results is information about JQuery Datepickers.
Any advice on where to look, how to resolve would be very much appreciated.
Here is my model:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MISDataRepo.Models
{
[Table("Activity",Schema = "coir")]
public partial class ActivityItem
{
public ActivityItem()
{
ActivityIdentifier = new HashSet<ActivityIdentifier>();
}
[Key]
public int ActivityItemId { get; set; }
[Required(ErrorMessage = "A valid Activity Name is required.")]
[Display(Name = "Activity Name")]
[StringLength(100)]
public string ActivityName { get; set; }
[Required]
[Display(Name = "Activity Type")]
public int ActivityTypeId { get; set; }
[Required]
[Display(Name = "Date Activity Created")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime DateCreated { get; set; }
[Display(Name = "Date Activity Modified")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime? DatetModified { get; set; }
[Required]
[Display(Name = "Created By (Employee ID)")]
[RegularExpression("^[1-9][0-9]{6}$", ErrorMessage = "A valid Employee ID is required!")]
public int? CreatedBy { get; set; }
[Display(Name = "Project Co-Ordinator (Employee ID)")]
[RegularExpression("^[1-9][0-9]{6}$", ErrorMessage = "A valid Employee ID is required!")]
public int? PC { get; set; }
[DefaultValue(true)]
public bool Live { get; set; }
public virtual ActivityType ActivityType { get; set; }
public virtual ICollection<ActivityIdentifier> ActivityIdentifier { get; set; }
}
}
Here is the view:
#model MISDataRepo.Models.ActivityItem
#{
ViewData["Title"] = "Edit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>Edit</h1>
<h4>ActivityItem</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="ActivityItemId" />
<div class="form-group">
<label asp-for="ActivityName" class="control-label"></label>
<input asp-for="ActivityName" class="form-control" />
<span asp-validation-for="ActivityName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ActivityTypeId" class="control-label"></label>
<select asp-for="ActivityTypeId" class="form-control" asp-items="ViewBag.ActivityTypeId"></select>
<span asp-validation-for="ActivityTypeId" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="DateCreated" class="control-label"></label>
<input asp-for="#Html.DisplayFor(a => a.DateCreated)" class="form-control" />
<span asp-validation-for="DateCreated" class="text-danger"></span>
#*<input type="hidden" asp-for="DateCreated" type="date" placeholder="Enter Date Created" value="#Model.DateCreated" />*#
</div>
<div class="form-group">
<label asp-for="DatetModified" class="control-label"></label>
<input asp-for="#Html.DisplayFor(a => a.DatetModified)" class="form-control" />
<span asp-validation-for="DatetModified" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CreatedBy" class="control-label"></label>
<input asp-for="CreatedBy" class="form-control" />
<span asp-validation-for="CreatedBy" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PC" class="control-label"></label>
<input asp-for="PC" class="form-control" />
<span asp-validation-for="PC" class="text-danger"></span>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="Live" /> #Html.DisplayNameFor(model => model.Live)
</label>
</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");}
}
Here are the 'Edit' methods of the controller
// GET: ActivityItems/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var activityItem = await _context.ActivityItem.FindAsync(id);
if (activityItem == null)
{
return NotFound();
}
ViewData["ActivityTypeId"] = new SelectList(_context.ActivityType, "ActivityTypeId", "ActivityTypeName", activityItem.ActivityTypeId);
return View(activityItem);
}
// POST: ActivityItems/Edit/5
// To protect from overposting attacks, 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> Edit(int id, [Bind("ActivityItemId,ActivityName,ActivityTypeId,DateCreated,DatetModified,CreatedBy,PC,Live")] ActivityItem activityItem)
public async Task<IActionResult> Edit(int id, [Bind("ActivityItemId,ActivityName,ActivityTypeId,DatetModified,CreatedBy,PC,Live")] ActivityItem activityItem)
{
if (id != activityItem.ActivityItemId)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(activityItem);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ActivityItemExists(activityItem.ActivityItemId))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
ViewData["ActivityTypeId"] = new SelectList(_context.ActivityType, "ActivityTypeId", "ActivityTypeName", activityItem.ActivityTypeId);
return View(activityItem);
}
But when I attempt to 'Edit' the entry the DateTime value doesn't pull
through from the database.
Yes, the issue you are having with the your View is pretty obvious due to your HTML Helper atrribute that is #Html.DisplayFor and the Property you have defined within your Model ActivityItem. You are probably getting following issue.
Problem:
How To Resolve:
Either you could use ViewModel or you can redefine your property public DateTime DateCreated { get; set; } by get rid of your annotations. However, I would prefer to use ViewModel. On the other hands, use the property like asp-for="DateCreated" within your edit view and get rid of your additional HTML helper class #Html.DisplayFor. Follow the below steps.
View Model:
public class ActivityItemViewModel
{
public int ActivityItemId { get; set; }
public string ActivityName { get; set; }
public DateTime DateCreated { get; set; }
public DateTime? DatetModified { get; set; }
}
Note: While loading your Edit view you certainly doesn't require annotations so you can ommit that.
View :
In view you are using additional HTML helper class #Html.DisplayFor which is not required in this scenario. You could try as following:
#model DotNet6MVCWebApp.Models.ActivityItemViewModel
#{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>ActivityItem</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="ActivityItemId" />
<div class="form-group">
<label asp-for="ActivityName" class="control-label"></label>
<input asp-for="ActivityName" class="form-control" />
<span asp-validation-for="ActivityName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="DateCreated" class="control-label"></label>
<input asp-for="DateCreated" class="form-control" />
<span asp-validation-for="DateCreated" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="DatetModified" class="control-label"></label>
<input asp-for="DateCreated" class="form-control" />
<span asp-validation-for="DatetModified" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="ActivityList">Back to List</a>
</div>
Output:
Having started studying asp net core 6, I ran into the problem of invalidity of my model.
What is the hidden problem?
Model of Product
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace TestWebApplication.Models
{
public class Product
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
[Range(0.1, int.MaxValue)]
public double Price { get; set; }
public string Image { get; set; }
[Display(Name = "Category Type")]
public int CategoryId { get; set; }
[ForeignKey("CategoryId")]
public virtual Category Category { get; set; }
}
}
Model of ProductVM
using Microsoft.AspNetCore.Mvc.Rendering;
namespace TestWebApplication.Models.ViewModels
{
public class ProductVM
{
public Product Product { get; set; }
public IEnumerable<SelectListItem> CategorySelectList { get; set; }
}
}
View
#model TestWebApplication.Models.ViewModels.ProductVM
#{
var title = "Create Product";
}
<form method="post" enctype="multipart/form-data">
#if(Model.Product.Id != 0)
{
title = "Edit Product";
<input asp-for="Product.Id" hidden />
}
<div class="border p-3">
<div class="form-group row">
<h2 class="text-info pl-3">#title</h2>
</div>
<br />
<div class="row">
<div class="col-8">
<div class="form-group row">
<div class="col-4">
<label asp-for="Product.Name"></label>
</div>
<div class="col-8">
<input asp-for="Product.Name" class="form-control"/>
<span asp-validation-for="Product.Name" class="text-danger"></span>
</div>
</div>
<br />
<div class="form-group row">
<div class="col-4">
<label asp-for="Product.Price"></label>
</div>
<div class="col-8">
<input asp-for="Product.Price" class="form-control"/>
<span asp-validation-for="Product.Price" class="text-danger"></span>
</div>
</div>
<br />
<div class="form-group row">
<div class="col-4">
<label asp-for="Product.Description"></label>
</div>
<div class="col-8">
<textarea asp-for="Product.Description" class="form-control summernote"> </textarea>
<span asp-validation-for="Product.Description" class="text-danger"></span>
</div>
</div>
<br />
<div class="form-group row">
<div class="col-4">
Image
</div>
<div class="col-8">
<input type = "file" name="files" id="uploadBox" multiple class="form-control"/>
</div>
</div>
<br />
<div class="form-group row">
<div class="col-4">
<label asp-for="Product.CategoryId"></label>
</div>
<div class="col-8">
<select asp-for="Product.CategoryId" asp-items="#Model.CategorySelectList" class="form-control">
<option disabled selected>---Select Category---</option>
</select>
<span asp-validation-for="Product.CategoryId" class="text-danger"></span>
</div>
</div>
<br />
<div class="form-group row">
<div class="col-8 offset-4 row">
<div class="col">
#if (Model.Product.Id != 0)
{
//update
<input type="submit" class="btn btn-info w-100" value="Update"/>
}
else
{
//create
<input type="submit" onclick="return validateInput()" class="btn btn-primary w-100" value="Create"/>
}
</div>
<div class="col">
<a asp-action="Index" class="btn btn-success w-100">
Back
</a>
</div>
</div>
</div>
</div>
<div class="col-4">
#* Keep this empty *#
</div>
</div>
</div>
</form>
#section Scripts
{
#{
<partial name= "_ValidationScriptsPartial.cshtml"/>
}
<script>
$(document).ready(function() {
$('.summernote').summernote(
{
height:250
});
});
function validateInput()
{
if (document.getElementById("uploadBox").value == "")
{
Swal.fire
(
'Error!',
'Please, upload an image',
'error'
)
return false;
}
return true;
}
</script>
}
Controller
using TestWebApplication.Data;
using TestWebApplication.Models;
using TestWebApplication.Models.ViewModels;
namespace TestWebApplication.Controllers
{
public class ProductController : Controller
{
private readonly ApplicationDbContext _db;
private readonly IWebHostEnvironment _webHostEnvironment;
public ProductController(ApplicationDbContext db, IWebHostEnvironment webHostEnvironment)
{
_db = db;
_webHostEnvironment = webHostEnvironment;
}
public IActionResult Index()
{
IEnumerable<Product> objList = _db.Product;
foreach (var item in objList)
{
item.Category = _db.Category.FirstOrDefault(category => category.Id == item.Id);
}
return View(objList);
}
// get - upsert
public IActionResult Upsert(int? id)
{
//IEnumerable<SelectListItem> CategoryDropDown = _db.Category.Select(i => new SelectListItem
//{
// Text = i.Name,
// Value = i.Id.ToString()
//});
//ViewBag.CategoryDropDown = CategoryDropDown;
//Product product = new Product();
ProductVM productVM = new ProductVM()
{
Product = new Product(),
CategorySelectList = _db.Category.Select(i => new SelectListItem
{
Text = i.Name,
Value = i.Id.ToString()
})
};
if(id == null)
{
// for create
return View(productVM);
}
else
{
productVM.Product = _db.Product.Find(id);
if (productVM.Product == null)
NotFound();
return View(productVM);
}
}
// post - upsert
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Upsert(ProductVM obj)
{
if (ModelState.IsValid)
{
var files = HttpContext.Request.Form.Files;
string webRootPath = _webHostEnvironment.WebRootPath;
if(obj.Product.Id == 0)
{
//Creating
string upload = webRootPath + WC.ImagePath;
string fileName = Guid.NewGuid().ToString();
string extention = Path.GetExtension(files[0].FileName);
using(var fileStream = new FileStream(Path.Combine(upload, fileName + extention), FileMode.Create))
{
files[0].CopyTo(fileStream);
}
obj.Product.Image = fileName + extention;
_db.Product.Add(obj.Product);
}
else
{
//Updating
}
return RedirectToAction("Index");
}
return View();
}
}
List of fields that participated in validation
From one answer, I realized that the Category field will remain null, and ef core will substitute the values. But I don't quite understand why so many fields are validated without being marked required in the model.
By receives a POST action, ModelState takes all of the name-value pairs and adds them as individual instances of ModelStateEntry to an instance of ModelStateDictionary.
ModelState checks all properties regardless of whether they have a Data annotations or not, such as you get a number, text, file etc. But for those data annotations you put on the properties the ModelState makes a special check based on it.
Just the properties of the model you passed to controller would be validated,you could try custom model validation follow this document:
https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-6.0#custom-attributes
And you could try with ModelState.AddModelError(string.Empty, "errormessage");add model level error in backend.
I'm quite new to C#, MVC and EF and I've hit a problem I don't seem o be able to resolve.
I'm trying to update the Create.cshtml view so that it shows/lists the itemName rather than itemID where the Item Name is in a different table.
Heres parts of my code so far:
Models:
using System;
using System.Collections.Generic;
namespace CIMSTest.Models
{
public class DirectActivityItem
{
public int ID { get; set; }
public int DirectTypeID { get; set; }
public string ActivityName { get; set; }
public DateTime DateActivityCreated { get; set; }
public bool ActivityLive { get; set; }
public ICollection<DirectActivityGroup> DirectActivityGroups { get; set; }
public DirectType DirectType { get; set; }
}
}
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace CIMSTest.Models
{
public class DirectType
{
public int DirectTypeID { get; set; }
public string DirectTypeName { get; set; }
public bool DirectTypeLive { get; set; }
public ICollection<DirectActivityItem> DirectActivityItems { get; set; }
}
}
Controller (Create):
public IActionResult Create()
{
ViewData["DirectTypeID"] = new SelectList(_context.DirectTypes, "DirectTypeID", "DirectTypeID");
return View();
}
// POST: DirectActivityItems/Create
// To protect from overposting attacks, 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,DirectTypeID,ActivityName,DateActivityCreated,ActivityLive")] DirectActivityItem directActivityItem)
{
if (ModelState.IsValid)
{
_context.Add(directActivityItem);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["DirectTypeID"] = new SelectList(_context.DirectTypes, "DirectTypeID", "DirectTypeID", directActivityItem.DirectTypeID);
return View(directActivityItem);
}
Create.cshtml
#model CIMSTest.Models.DirectActivityItem
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>DirectActivityItem</h4>
<hr />
<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="DirectTypeID" class="control-label"></label>
<select asp-for="DirectTypeID" class ="form-control" asp-items="ViewBag.DirectTypeID"></select>
</div>
<div class="form-group">
<label asp-for="ActivityName" class="control-label"></label>
<input asp-for="ActivityName" class="form-control" />
<span asp-validation-for="ActivityName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="DateActivityCreated" class="control-label"></label>
<input asp-for="DateActivityCreated" class="form-control" />
<span asp-validation-for="DateActivityCreated" class="text-danger"></span>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="ActivityLive" /> #Html.DisplayNameFor(model => model.ActivityLive)
</label>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
As you can see the Direct ActivityItem table contains the DirectTypeID, but on the Create page for this I want to list the DirectTypeNames from the DirectType table and not the ID as users won't know what the ID's are.
If anyone can provide any information it would be gratefully received.
Change this:
ViewData["DirectTypeID"] = new SelectList(_context.DirectTypes, "DirectTypeID", "DirectTypeID");
to this:
ViewData["DirectTypeID"] = new SelectList(_context.DirectTypes, "DirectTypeID", "DirectTypeName");
You'll want your view model returning the DirectTypeID since that will be how you best resolve the relationship, but the third parameter tells the SelectList what to display for each selection.
I am trying a create a page where I have a form to create the product. here I trying to creating category and subcategory dependent list box.but not understand how I will do that.
Here is my code:
public class Category
{
public int Id { get; set; }
[Required]
[Display(Name = "Category Name")]
public string CategoryName { get; set; }
}
//my SubCategory model
public class SubCategory
{
public int Id { get; set; }
[Required]
[Display(Name = "SubCategory Name")]
public string SubCategoryName { get; set; }
}
//my product model
public class Product
{
public int Id { get; set; }
[Required]
public String Name { get; set; }
[Required]
[Display(Name = "Category Type")]
public int CategoryTypeId { get; set; }
[ForeignKey("CategoryTypeId")]
public Category Category { get; set; }
[Required]
[Display(Name = "SubCategory Type")]
public int SubCategoryTypeId { get; set; }
[ForeignKey("SubCategoryTypeId")]
public SubCategory SubCategory { get; set; }
}
Product Controller
[HttpGet]
public IActionResult Create()
{
ViewData["CategoryId"] = new SelectList(_db.Category.ToList(), "Id", "CategoryName");
ViewData["SubCategoryId"] = new SelectList(_db.SubCategory.ToList(), "Id", "SubCategoryName");
return View();
}
[HttpPost]
public async Task<IActionResult> Create(Product product)
{
if (ModelState.IsValid)
{
_db.Product.Add(product);
await _db.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(product);
}
Create.cshtml
#model Amazon.Models.Product
#{
ViewData["Title"] = "Create";
}
<br />
<h2 class="text-info">Add New Product</h2>
<form asp-action="Create" method="post" enctype="multipart/form-data">
<div class="p-4 rounded border">
<div asp-validation-summary="ModelOnly" class="text-danger">
</div>
<h3>#ViewBag.message</h3>
<div class="form-group row">
<div class="col-2">
<label asp-for="Name"></label>
</div>
<div class="col-5">
<input asp-for="Name" class="form-control" />
</div>
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group row">
<div class="col-2">
<label asp-for="CategoryTypeId"></label>
</div>
<div class="col-5">
<select asp-for="CategoryTypeId" asp-items="ViewBag.CategoryId" class="form-control"></select>
#*<input asp-for="ProductTypeId" class="form-control" />*#
</div>
<span asp-validation-for="CategoryTypeId" class="text-danger"></span>
</div>
<div class="form-group row">
<div class="col-2">
<label asp-for="SubCategoryTypeId"></label>
</div>
<div class="col-5">
<select asp-for="SubCategoryTypeId" asp-items="ViewBag.SubCategoryId" class="form-control"></select>
#*<input asp-for="SpecialTagId" class="form-control" />*#
</div>
<span asp-validation-for="SubCategoryTypeId" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary" value="Save" />
<a asp-action="Index" class="btn btn-success">Back To List</a>
</div>
</div>
</form>
#section Scripts{
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");
}
}
above code, I did successfully data-bind of my product controller and view.but I trying to creating category and subcategory dependent list box.but not understand how I will do that.
here my expectation output :
I am an absolute beginner. please help anyone.
I trying to creating category and subcategory dependent list box.
To implement cascading dropdown for Category and Subcategory, as I mentioned in comment, you can populate SubCategory dropdown based on the previous selection of Category in Category dropdown change event, like below.
You can try to modify your model class and implement one-to-many relationship between Category and Subcategory entities, so that you can store data like below in database and retrieve all sub-categories based on the provided CategoryId.
Populate only Category dropdown when page loads
<div class="form-group">
<div class="col-2">
<label asp-for="CategoryTypeId" class="control-label"></label>
</div>
<div class="col-5">
<select asp-for="CategoryTypeId" asp-items="ViewBag.CategoryId" class="form-control">
<option value="">Select Category</option>
</select>
</div>
</div>
<div class="form-group">
<div class="col-2">
<label asp-for="SubCategoryTypeId" class="control-label"></label>
</div>
<div class="col-5">
<select asp-for="SubCategoryTypeId"></select>
</div>
</div>
Dynamically populate SubCategory dropdown in Category dropdown change event
$(function () {
$("select#CategoryTypeId").change(function () {
var cid = $(this).val();
$("select#SubCategoryTypeId").empty();
$.getJSON(`/Home/GetSubCategory?cid=${cid}`, function (data) {
//console.log(data);
$.each(data, function (i, item) {
$("select#SubCategoryTypeId").append(`<option value="${item.id}">${item.name}</option>`);
});
});
})
});
Action method GetSubCategory
public IActionResult GetSubCategory(int cid)
{
var SubCategory_List=_db.SubCategory.Where(s => s.CategoryId == cid).Select(c => new { Id = c.Id, Name = c.SubCategoryName }).ToList();
return Json(SubCategory_List);
}
Test Result
Update:
Model classes
public class Category
{
public int Id { get; set; }
[Required]
[Display(Name = "Category Name")]
public string CategoryName { get; set; }
public ICollection<SubCategory> SubCategories { get; set; }
}
public class SubCategory
{
public int Id { get; set; }
[Required]
[Display(Name = "SubCategory Name")]
public string SubCategoryName { get; set; }
public int CategoryID { get; set; }
public Category Category { get; set; }
}
I have a model Event with a ViewModel, Controller and View.
In this case it is about the CreateView
Model
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Verbonding.Models
{
public class Event
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
[Required]
public DateTime EventStart { get; set; }
public DateTime EventEnd { get; set; }
public int OrganizationId { get; set; }
public int AddressId { get; set; }
[ForeignKey("Category")]
public int CategoryId { get; set; }
public virtual Organization Organization { get; set; }
public virtual Address Address { get; set; }
public virtual Category Category { get; set; }
}
}
ViewModel
using System;
using System.Collections.Generic;
namespace Verbonding.Models.EventViewModels
{
public class CreateViewModel
{
public string Name { get; set; }
public int? CategoryId { get; set; }
public List<Category> Categories { get; set; }
public string Description { get; set; }
public string LocationName { get; set; }
public string Street { get; set; }
public string HouseNr { get; set; }
public string PostalCode { get; set; }
public string City { get; set; }
public DateTime EventStartDate { get; set; }
public DateTime EventEndDate { get; set; }
}
}
Controller
I removed the actions that are not required for this question.
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using Verbonding.Data;
using Verbonding.Models;
using Verbonding.Services;
using Verbonding.Models.EventViewModels;
using Microsoft.AspNetCore.Authorization;
namespace Verbonding.Controllers
{
public class EventsController : Controller
{
private readonly ApplicationDbContext _context;
private IEventData _eventData;
private IOrganizationData _organizationData;
private IAddressData _addressData;
private ICategoryData _categoryData;
private ICountryData _countryData;
public EventsController(IEventData eventData,
IOrganizationData organizationData,
IAddressData addressData,
ICategoryData categoryData,
ICountryData countryData)
{
_eventData = eventData;
_organizationData = organizationData;
_addressData = addressData;
_categoryData = categoryData;
_countryData = countryData;
}
[Authorize]
public IActionResult Create()
{
CreateViewModel model = new CreateViewModel();
var categories = _categoryData.GetChildCategories();
model.Categories = new List<Category>(categories);
return View(model);
}
[Authorize]
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(CreateViewModel model)
{
if (ModelState.IsValid)
{
Category category = _categoryData.Get(model.CategoryId.GetValueOrDefault());
Country country = _countryData.Get(1);
_eventData.Add(model.Name, category, model.Description, model.LocationName, model.Street,
model.HouseNr, model.PostalCode, model.City, country, model.EventStartDate, model.EventEndDate);
_eventData.Commit();
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
else
{
var categories = _categoryData.GetChildCategories();
model.Categories = new List<Category>(categories);
}
return View(model);
}
}
}
View
#model Verbonding.Models.EventViewModels.CreateViewModel
#{
ViewData["Title"] = "Create";
}
<h2>Create</h2>
<form asp-action="Create">
<div class="form-horizontal">
<hr />
<h4>Algemene gegevens</h4>
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="CategoryId" class="col-md-2 control-label"></label>
<div class="col-md-10">
<select asp-for="CategoryId" asp-items="#(new SelectList(Model.Categories, "Id", "Name"))" class="form-control">
<option>Please select a Category</option>
</select>
</div>
</div>
<div class="form-group">
<label asp-for="Description" class="col-md-2 control-label"></label>
<div class="col-md-10">
<textarea asp-for="Description" class="form-control" rows="5" cols="100"></textarea>
<span asp-validation-for="Description" class="text-danger" />
</div>
</div>
<hr />
<h4>Adresgegevens Evenement Evenement</h4>
<div class="form-group">
<label asp-for="LocationName" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="LocationName" class="form-control" />
<span asp-validation-for="LocationName" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="Street" class="col-md-2 control-label"></label>
<div class="col-md-10">
<div><input asp-for="Street" class="form-control" style="width: 200px; float: left"/></div>
<div><input asp-for="HouseNr" class="form-control" style="width: 60px; float: left; margin-left: 20px" /></div>
<span asp-validation-for="Street" class="text-danger" />
<span asp-validation-for="HouseNr" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="PostalCode" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="PostalCode" class="form-control" />
<span asp-validation-for="PostalCode" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="City" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="City" class="form-control" />
<span asp-validation-for="City" class="text-danger" />
</div>
</div>
<hr />
<h4>Tijden Evenement</h4>
<div class="form-group">
<label asp-for="EventStartDate" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="EventStartDate" class="form-control" />
<span asp-validation-for="EventStartDate" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="EventEndDate" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="EventEndDate" class="form-control" />
<span asp-validation-for="EventEndDate" class="text-danger" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Opslaan" class="btn btn-default" />
</div>
</div>
</div>
</form>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Now when I test this, the page does reload, and the fields that have been entered stay there, but there are no validation errors.
What can I do to get the validation errors on the page?
Update
I found out that the properties of the DateTime type are required by default,
so I have changed these.
In the model:
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd-MM-yyyy HH:mm}")]
public DateTime EventStart { get; set; }
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd-MM-yyyy HH:mm}")]
public DateTime? EventEnd { get; set; }
In the ViewModel:
public DateTime EventStartDate { get; set; }
public DateTime? EventEndDate { get; set; }
This seems to have the desired effect for these fields.
Update 2
Based on the comments from Dai I have updated the Model and ViewModel.
To test it out I started with the Name property and left the rest as it is.
In the Model:
public string Name { get; set; }
In the ViewModel:
[required]
public string Name { get; set; }
At first this did not have the desired effect. (But also no errors)
Then I changed required to Required with a capital R, and that did the trick.
I also needed to add using System.ComponentModel.DataAnnotations;, but that was nicely handled by Visual Studio.
try to create file _ViewImports.chtml into your Views folder. Then add following lines to the file:
#using Microsoft.AspNetCore.Identity
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers