I have a Controller with an ActionResult which gets three arrays from the view at HttpPost.
The arrays are the Id(tableId) the position from the top(positionY) and the position from the left(positionX).
When you click save the ActionResult needs to update all the Positions from the corresponding id's in the database.
This is my Controller:
private BonTempsDbContext db = new BonTempsDbContext();
[HttpPost]
public ActionResult Index(int[] tableId, int[] tablePosX, int[] tablePosY)
{
int i = 0;
foreach (var id in tableId)
{
int number = tableId[i];
Tafel tafel = db.Tafel
.Where(x => x.id == number)
.ToList();
db.Entry(tafel).State = EntityState.Modified;
i++;
}
return View(db.Tafel.ToList());
}
I get the error:
Cannot implicitly convert type
'System.Collections.Generic.List' to
'BonTempsMVC.Tafel'
This is my View if you van get something usefull out of it.
#model IEnumerable<BonTempsMVC.Tafel>
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
<div class="VoegToeBtn">
<a href="/tafel/create">
<span class="btn btn-default">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Maak nieuw menu aan
</span>
</a>
</div>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div id="tablewrapper">
#foreach (var item in Model)
{
var positionXId = "posX" + item.id;
var positionYId = "posY" + item.id;
<div class="draggable ui-widget-content" id="#Html.DisplayFor(ModelItem => item.id)">
<input type="text" hidden name="tableId" value="#Html.DisplayFor(ModelItem => item.id)" />
<input type="text" hidden name="tablePosX" id="#positionXId" value="" />
<input type="text" hidden name="tablePosY" id="#positionXId" value="" />
<p>#Html.DisplayFor(ModelItem => item.tafelNaam)</p>
</div>
}
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
}
<script>
$(".draggable").draggable({
snap: ".draggable",
snapMode: "outer",
stop: function (event, ui) {
var finalOffset = $(this).offset();
var finalxPos = finalOffset.left;
var finalyPos = finalOffset.top;
var itemId = $(this).attr('id');
var posXid = "posX" + itemId;
var posYid = "posY" + itemId;
$('input:text[id="' + posXid + '"]').attr("value", finalxPos);
$('input:text[id="' + posYid + '"]').attr("value", finalyPos);
},
});
</script>
And tafel model:
namespace BonTempsMVC
{
public class Tafel
{
public int id { get; set; }
public string tafelNaam { get; set; }
public bool beschikbaar { get; set; }
public float positionY { get; set; }
public float positionX { get; set; }
}
}
Try this:-
List<Tafel> tafel = db.Tafel
.Where(x => x.id == number)
.ToList();
Here, List<Tafel> is returned but you are storing that in Tafel object. If you are pretty sure that in Tafel entity there will be only one item with matching id then do this:-
Tafel tafel = db.Tafel.FirstOrDefault(x => x.id == number);
As, suggested by #DavidG you can also do this:-
Tafel tafel = db.Tafel.Find(number);
Related
I have a .net core 3.1 MVC project where i am displaying a custom made treeview along with 2 textbox.
Here is the Model i used:
public class DeviceGroupAvailabilityModel
{
public string ChapterTitle { get; set; }
public string ChapterDetail { get; set; }
public List<TreeViewModel> TreeViewModels { get; set; }
}
And
public class TreeViewModel
{
public string ParentNode { get; set; }
public bool isChecked { get; set; }
public List<TreeViewModel> ChildNodes { get; set; }
}
Here is the function that i use to display the tree full on the cshtml file.
Here is part of the cshtml file:
here is my controller:
public async Task<IActionResult> Index()
{
List<TreeViewModel> TreeModel = BuildModels(await prtgClient.GetExtendedGroupsAsync(_pRTGApiClient),1);
DeviceGroupAvailabilityModel deviceGroupAvailabilityModel = new DeviceGroupAvailabilityModel
{
ChapterTitle = "Device And Group Availability",
ChapterDetail = "",
TreeViewModels = TreeModel
};
return View("Index",deviceGroupAvailabilityModel);
}
public IActionResult Cancel()
{
return RedirectToAction("Index","Home");
}
[HttpPost]
public async Task<IActionResult> Save([FromForm]DeviceGroupAvailabilityModel deviceGroupAvailabilityModel)
{
int reportID = await _dbConnection.GetReportId(deviceGroupAvailabilityModel.ChapterTitle);
deviceGroupAvailabilityModel.TreeViewModels.Select(async group => await AddGroupToReport(group.ParentNode, reportID));
return View("Index");
}
private async Task AddGroupToReport(string groupName,int reportID)
{
int groupId = await _dbConnection.GetGroupId(groupName);
await _dbConnection.AddGroupToReport(groupId, reportID);
}
private List<TreeViewModel>BuildModels(List<ExtendedGroup>extendedGroups,int rootID)
{
List<TreeViewModel> TreeModel = new List<TreeViewModel>();
extendedGroups.ForEach(group =>
{
if (group.parentId == rootID)
{
TreeModel.Add(new TreeViewModel
{
ParentNode = group.name,
ChildNodes = BuildModels(extendedGroups, group.objid)
});
}
});
return TreeModel;
}
Model Binding from controller to view works great but from view to controller in only get the Two textbox not the list of treeview model.
Is there a way to solve this?
kind regards,
Edit: Full CSHTML Code withoud Title:
#model DeviceGroupAvailabilityModel
#functions{
void DisplayTree(List<TreeViewModel>models)
{
<ul>
#foreach (TreeViewModel treeViewModel in models)
{
<li>
<input type="checkbox" id="#treeViewModel.ParentNode" name="#treeViewModel.ParentNode" checked="#treeViewModel.isChecked" />
#treeViewModel.ParentNode
#if (treeViewModel.ChildNodes.Count > 0)
{
DisplayTree(treeViewModel.ChildNodes);
}
</li>
}
</ul>
}
}
#using (Html.BeginForm("Save", "DeviceGroupAvailability",FormMethod.Post)) {
<div class="container">
<div class="row">
<div class="col-auto">
<div class="card shadow" style="border-left-color:#0087e9;border-left-width:medium">
<h6 class="card-header text-center bg-warning">SELECT GROUP TO REPORT</h6>
<div class="card-body">
<div class="tree well">
#{
DisplayTree(Model.TreeViewModels);
}
</div>
</div>
</div>
</div>
<div class="col">
<div class="row">
<div class="card shadow ml-3 mb-3" style="border-left-color:#0087e9;border-left-width:medium">
<h6 class="card-header text-center bg-warning">CHAPTER TITLE</h6>
<div class="card-body">
#Html.TextBoxFor(m=>m.ChapterTitle)
</div>
</div>
</div>
<div class="row">
<div class="card shadow ml-3 mb-3" style="border-left-color:#0087e9;border-left-width:medium">
<h6 class="card-header text-center bg-warning">CHAPTER Description</h6>
<div class="card-body">
#Html.TextBoxFor(m=>m.ChapterDetail)
</div>
</div>
</div>
<div class="row">
<input type="submit" class="btn ml-5 bg-primary text-white" value="Save" />
<button type="button" class="btn bg-primary ml-5 text-white" onclick="location.href='#Url.Action("Cancel", "DeviceGroupAvailability")'">Cancel</button>
</div>
</div>
</div>
</div>
}
#section Scripts{
<script type="text/javascript">
$(":checkbox").change(function () {
//begin Cheking all child elements
var ulsiblings = $(this).siblings("ul");
var lichilt = ulsiblings.find("li");
var checkchild = lichilt.find(":checkbox");
checkchild.prop("checked", this.checked);
//begin checking Parent element
//if child is checked
var checkLiParent = $(this).parent();
var checkliSiblings = checkLiParent.siblings("li");
var lisibCheck = checkliSiblings.find(":checkbox");
var isAllChecked;
lisibCheck.each(function () {
if (this.checked) {
isAllChecked = true;
}
else {
isAllChecked = false;
}
})
if (isAllChecked) {
var checkParent = checkLiParent.parent();
var parentSib = checkParent.siblings(":checkbox");
parentSib.prop("checked", this.checked);
}
})
</script>
}
I have a problem. I have posts on the wall. And if the user has entered the site, then he can send his response to the post. How do I make it so that when using my TakeAnswer method, I can create a new row in the database? Now I only save the user's response, but not the ID of the post to which he replied.
[HttpGet]
public IActionResult Index()
{
var posts = db.Posts.ToList();
if (posts == null)
return View();
var temp = new IndexModel();
temp.Posts = posts;
temp.PostID = 0;
temp.IndexAnswer = "";
return View(temp);
}
[HttpPost]
public async Task<IActionResult> TakeAnswer(IndexModel model, string PostID)
{
if (ModelState.IsValid)
{
db.Answers.Add(new Answer { UserId = GetUserId, UserAnswer = model.IndexAnswer, PostId = int.Parse(PostID) });
await db.SaveChangesAsync();
}
return View();
}
public class IndexModel
{
[BindProperty]
public string IndexAnswer { get; set; }
[BindProperty]
public int PostID { get; set; }
public IEnumerable<Post> Posts { get; set; }
}
#model CourseProject.ViewModels.IndexModel
<form asp-action="TakeAnswer" asp-controller="Home">
#if (Model != null)
{
#foreach (var post in Model.Posts)
{
<div class="card my-3">
<h5 class="card-header font-weight-bold">
#Html.DisplayFor(model => post.PostName)
</h5>
<div class="card-body">
<p class="card-text">#Html.DisplayFor(model => post.PostContent)</p>
#if (User.Identity.IsAuthenticated)
{
<div class="form-group">
<label asp-for="IndexAnswer">Your answer to this problem</label><br />
<input type="text" asp-for="IndexAnswer" />
</div>
<div>
<button type="submit" class="btn btn-primary" asp-route-PostID="#post.Id">Reply</button>
</div>
}
</div>
<div class="card-footer">
<p>Task created: #Html.DisplayFor(model => post.Date)</p>
</div>
</div>
}
}
</form>
I'm new using MVC Razor and I have this problem.
I have a View like this:
#model MoldCapThickness.Models.MoldCapModels
#using PagedList.Mvc;
#using PagedList;
#{
ViewBag.Title = "Index";
}
#Styles.Render("~/Content/MoldCapStyles.css")
#Scripts.Render("~/bundles/moldCapScript")
#using (Html.BeginForm())
{
<div class="container">
<div class="fields-field">
#Html.LabelFor(m => m.LotNumber)
#Html.TextBoxFor(m => m.LotNumber, new { title = "" })
#Html.LabelFor(m => m.Equipment)
#Html.TextBoxFor(m => m.Equipment, new { title = "" })
<input type="submit" value="Buscar" name="Filter" />
</div>
<h4>Informacion del Lote seleccionado</h4>
<div class="lotInfo">
<p class="lotInfoLabel">
#Html.LabelFor(m => m.PartNumber)
#Html.DisplayTextFor(m => m.PartNumber)
</p>
<p class="lotInfoLabel">
#Html.LabelFor(m => m.Equipment)
#Html.DisplayTextFor(m => m.Equipment)
</p>
<p class="lotInfoLabel">
#Html.LabelFor(m => m.MoldCapThickness)
#Html.DisplayTextFor(m => m.MoldCapThickness)
</p>
<p class="lotInfoLabel">
#Html.LabelFor(m => m.MoldCapThicknessRangeMax)
#Html.DisplayTextFor(m => m.MoldCapThicknessRangeMax)
</p>
<p class="lotInfoLabel">
#Html.LabelFor(m => m.MoldCapThicknessRangeMin)
#Html.DisplayTextFor(m => m.MoldCapThicknessRangeMin)
</p>
<p class="lotInfoLabel">
#Html.DisplayTextFor(m => m.EquipmentType)
</p>
</div>
<div class="press-containers">
#foreach (var press in (Model != null ? Model.TransferConfigurations : new List<MoldCapThickness.Models.TransferConfiguration>()))
{
<div class="press-par">
<div class="press">
<p class="press-name">#press.PressName</p>
<p>LEFT</p>
<p>Molde: #press.Mold</p>
#Html.LabelFor(m => press.NumeroTira)
#Html.TextBoxFor(m => press.NumeroTira)
<div id="press-thickness-list">
#foreach (var thickness in press.Thickness)
{
<div class="moldThicknessListItem">
<label>#thickness.Name: </label>
#Html.TextBoxFor(m => thickness.Value)
<label>mm</label>
</div>
}
</div>
<div class="comments-container">
Comentarios
</div>
</div>
<div class="press">
<p class="press-name">#press.PressName</p>
<p>RIGHT</p>
<p>Molde: #press.Mold</p>
#Html.LabelFor(m => press.NumeroTira)
#Html.TextBoxFor(m => press.NumeroTira)
<div id="press-thickness-list">
#foreach (var thickness in press.Thickness)
{
<div class="moldThicknessListItem">
<label>#thickness.Name: </label>
#Html.TextBoxFor(m => thickness.Value)
<label>mm</label>
</div>
}
</div>
</div>
</div>
}
#foreach (var press in (Model != null ? Model.CompressionConfiguration : new List<MoldCapThickness.Models.CompressionConfiguration>()))
{
<div class="press-par">
<div class="press">
<p class="press-name">#press.PressName</p>
<p>UPPER</p>
<p>Molde: #press.Mold</p>
<div>
#Html.LabelFor(m => press.NumeroTira)
#Html.TextBoxFor(m => press.NumeroTira)
</div>
<div id="press-thickness-list">
#foreach (var thickness in press.Thickness)
{
<div class="moldThicknessListItem">
<label>#thickness.Name: </label>
#Html.TextBoxFor(m => thickness.Value)
<label>mm</label>
</div>
}
</div>
</div>
<div class="press">
<p class="press-name">#press.PressName</p>
<p>LOWER</p>
<p>Molde: #press.Mold</p>
<div>Numero de tira</div>
<div id="press-thickness-list">
#foreach (var thickness in press.Thickness)
{
<div class="moldThicknessListItem">
<label>#thickness.Name: </label>
#Html.TextBoxFor(m => thickness.Value)
<label>mm</label>
</div>
}
</div>
</div>
</div>
}
</div>
<div>
<input type="submit" value="Enviar" name="Send" />
</div>
</div>
}
The View is using the next Model:
public class MoldCapModels {
[Display(Name = "Escanear Lote:")]
public string LotNumber {
get;
set;
}
[Display(Name = "Numero de Parte:")]
public string PartNumber {
get;
set;
}
[Display(Name = "Maquina:")]
public string Equipment {
get;
set;
}
[Display(Name = "Mold Cap Thickness:")]
public float MoldCapThickness {
get;
set;
}
[Display(Name = "MAX:")]
public float MoldCapThicknessRangeMax {
get;
set;
}
[Display(Name = "MIN:")]
public float MoldCapThicknessRangeMin {
get;
set;
}
private string _EquipmentType;
public string EquipmentType {
get {
return _EquipmentType;
}
set {
if (value == "INJECTION") {
_EquipmentType = "TRANSFER";
} else {
_EquipmentType = value;
}
}
}
public int MoldThicknessCap {
get;
set;
}
public List < TransferConfiguration > TransferConfigurations {
get;
set;
} = new List < TransferConfiguration > ();
public List < CompressionConfiguration > CompressionConfiguration {
get;
set;
} = new List < CompressionConfiguration > ();
public string DisplayModal {
get;
set;
}
}
In the view I have two inputs, Filter and Send, each one calls a method in the controller. The controller looks like this:
public class Controller: CommonController {
public ActionResult Index() {
return View();
}
[HttpPost]
[ButtonClick(Name = "Filter")]
public ActionResult Filter(MoldCapModels capModels) {
if (!string.IsNullOrWhiteSpace(capModels.LotNumber) && !string.IsNullOrWhiteSpace(capModels.Equipment)) {
capModels.PartNumber = GetPartNumber(capModels.LotNumber);
capModels.Equipment = GetEquipment(capModels.Equipment);
capModels.MoldCapThickness = GetMoldCapThickness(capModels.PartNumber);
capModels.MoldCapThicknessRangeMin = GetMoldCapThicknessMinRange(capModels.PartNumber);
capModels.MoldCapThicknessRangeMax = GetMoldCapThicknessMaxRange(capModels.PartNumber);
capModels.EquipmentType = GetEquipmentType(capModels.Equipment);
if (capModels.EquipmentType == "TRANSFER") {
capModels.TransferConfigurations = GetTranserPresses(capModels.Equipment, capModels.PartNumber);
} else {
capModels.CompressionConfiguration = GetCompressionPresses(capModels.Equipment, capModels.PartNumber);
}
}
return View(capModels);
}
[HttpPost]
[ButtonClick(Name = "Send")]
public ActionResult Send(MoldCapModels capModel) {
return View(capModel);
}
}
The filter method fills the fields of MoldCapModels and return the model with the fields filled to display them in the view. This is working fine. As you can see in the model I have two list of objects, the list populates depending of certain values on the data base, this list are handled in the view with the foreach cicles and each object of the view has its own values to fill represented as the thickness.Value field.
The problem comes when I want to call the Send method using the Send input. The Send method is waiting the model used by the view but the problem is this model came with empty values and only the LotNumber and Equipment fields has values, the two list of the models and all the other fields are empty.
I need the Model values including the lists and its objects values.
How can I send the model with its current values to the Send method in the controller?
You need to update your View and in your foreach replace all textboxes that look like:
#Html.TextBoxFor(m => press.NumeroTira)
And use this:
<input type="textbox" name="capModel.TransferConfigurations[#index].NumeroTira" value="#press.NumeroTira"/>
or:
<input type="textbox" name="TransferConfigurations[#index].NumeroTira" value="#press.NumeroTira"/>
Depending of how the name attribute of other elements is being generated.
You will need to define #index before the foreach loop
#{var index = 0;}
And increment before the end of each iteration
#index++
Suppose I have a form that contains the following model structure:
TestRecord.cs
class TestRecord {
public string Id { get; set; }
public string RecordName { get; set; }
...other props
public ICollection<RtdbFiles> RtdbFiles { get; set; }
}
Where the corresponding RtdbFile contains the following props:
RtdbFile.cs
class RtdbFile {
public int Id { get; set; }
public string Filename { get; set; }
...other props
}
When I POST this model to my controller to update, I receive the following error:
The instance of entity type 'RtdbFile' cannot be tracked because another instance with the key value '{Id: 2021}' is already being tracked
So it appears that two of the same RtdbFile are being attached to the context. Here's how my controller method is formatted:
[HttpPost("UpdateMilestones")]
public async Task<IActionResult> UpdateMilestones(string testRecordId)
{
db.ChangeTracker.LazyLoadingEnabled = false;
var record = db.TestRecords
.Include(tr => tr.RtdbFiles)
.FirstOrDefault(tr => tr.TestRecordId == testRecordId);
if (await TryUpdateModelAsync(record))
{
await db.SaveChangesAsync();
}
return RedirectToAction("Milestones", new { id = testRecordId });
}
Is TryUpdateModelAsync() not made to handle situations with a One-to-Many relationship? When is the duplicate RtdbFile being added to the context? I've disabled lazy loading and eagerly load the RtdbFiles. This is similar to what is done in the Contoso University example by Microsoft but the difference is their eagerly loaded property is a One-to-One relationship.
How can I fix this? Thanks!
EDIT to show Razor Pages:
UpdateMilestones.cshtml
#model rtdb.Models.TestRecord
#addTagHelper *, rtdb
<input type="hidden" asp-for="#Model.TestRecordId" />
<div class="form-section-text">Milestones & Tracking</div>
<!--unrelated inputs removed -->
<div class="form-group">
<vc:file record="#Model" type="#RtdbFile.AttachmentType.TPR" approvers="true"></vc:file>
</div>
The RtdbFiles are abstracted out a bit in to view components:
File View Component
#model rtdb.Models.ViewModels.FileViewModel
#addTagHelper *, rtdb
#using HtmlHelpers.BeginCollectionItemCore
<div class="form-group attachments">
<div class="link-header">#(Model.AttachmentType.ToString("G"))</div>
<div class="row">
<div class="col-sm-12">
#if (Model.TestRecord.RtdbFiles.Count > 0)
{
foreach (var file in Model.TestRecord.RtdbFiles.Where(f => f.IsDeleted != true && f.Type == Model.AttachmentType && f.TestRecordId == Model.TestRecord.TestRecordId).ToList())
{
<div class="attachment">
#using (Html.BeginCollectionItem("RtdbFiles"))
{
<div class="form-group">
<div class="form-row">
<input asp-for="#file.Id" hidden />
<input asp-for="#file.Type" hidden />
<div class="col-sm-6">
#if (#file.Id < 1)
{
<input class="FileInput" asp-for="#file.UploadedFile" type="file" />
}
else
{
<div><span data-file-id="#file.Id"><a href='#Url.Action("Download", "RtdbFiles", new { id = file.Id })'>#file.Filename (#file.Type.ToString("G"))</a></span></div>
}
</div>
<div class="col-sm-6">
<div>
<label asp-for="#file.FileApproverPersonnel" class="col-form-label col-form-label-sm">Approvers:</label>
<input asp-for="#file.FileApproverPersonnel" class="form-control file-approver-personnel ldap-tags" />
</div>
</div>
</div>
</div>
}
</div>
}
}
<div id="#(Model.AttachmentType.ToString("G"))s"></div>
<button type="button" class="add-file btn btn-primary" data-test-type="Other" data-attachment-type="TPR" data-container="#(Model.AttachmentType.ToString("G"))s">Add #(Model.AttachmentType.ToString("G"))</button>
<small style="display: block; margin-top: 6px">File size limit: 100MB</small>
</div>
</div>
</div>
What is obvious is TryUpdateModelAsync or maybe ChangeTracker has some issues with string ForeignKeys. First of all I highly recommend you to change PrimaryKey to int because EF shows some odd behaviour in such cases.
But if you insist on it, I tried some ways and finally reach this way:
Preventing object from tracking with AsNoTracking and use context.Update after updating record based on controller model
Based on your latest models, It's my sample that works well:
Models:
public class TestRecord
{
public string Id { get; set; }
public string RecordName { get; set; }
public virtual IList<RtdbFile> RtdbFiles { get; set; }
}
public class RtdbFile
{
public int Id { get; set; }
public string TestRecordId { get; set; }
public string Filename { get; set; }
}
Razor Page:
Note: This part has the most important effect on your result. specially RtdbFiles[{i}].Id and RtdbFiles[{i}].Filename
Your View have to send items and values with exactly same name to server object to take effect correctly:
#model Jordan.TestRecord
#using (Html.BeginForm("UpdateMilestones", "Home", FormMethod.Post))
{
#Html.HiddenFor(p => p.Id);
#for (int i = 0; i < Model.RtdbFiles.Count; i++)
{
#Html.Hidden($"RtdbFiles[{i}].Id", Model.RtdbFiles[i].Id);
#Html.TextBox($"RtdbFiles[{i}].Filename", Model.RtdbFiles[i].Filename);
}
<button type="submit">Save</button>
}
Controller:
namespace Jordan
{
[Route("")]
public class HomeController : Controller
{
private readonly AppDbContext context;
public HomeController(AppDbContext context)
{
this.context = context;
context.Database.EnsureCreated();
}
[HttpGet]
public IActionResult Index()
{
var sampleRecord = context.TestRecords
.Include(r => r.RtdbFiles)
.FirstOrDefault();
return View(sampleRecord);
}
[HttpPost]
[Route("UpdateMilestones")]
public async Task<IActionResult> UpdateMilestones(int Id)
{
context.ChangeTracker.LazyLoadingEnabled = false;
var record = context.TestRecords
.Include(tr => tr.RtdbFiles)
.AsNoTracking()
.FirstOrDefault(tr => tr.Id == Id);
if (await TryUpdateModelAsync(record))
{
context.Update(record);
await context.SaveChangesAsync();
}
return RedirectToAction("Index");
}
}
}
I got it, but there seems to be no case of TryUpdateModelAsync on
one-to-many online. (And I tried without success).
Therefore, I suggest that you can use our common method of updating the one-to-many data model.
In the view, you need to bind each field of TestRecord to the corresponding control and pass the latest data of TestRecord to UpdateMilestones action.
Please refer to the following code:
View (which show one record of TestRecord and it related to multiple RtdbFiles datas):
#model WebApplication_core_mvc.Controllers.TestRecord
#{
ViewData["Title"] = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
var i = 0;
}
<h1>Index</h1>
<form asp-action="UpdateMilestones" method="post">
<input id="Text1" type="text" asp-for="Id" hidden />
<label>#Model.Id</label>
<input id="Text2" type="text" asp-for="RecordName" />
<br />
<h4>Rtb:</h4>
<table>
<tr>
<th>Id</th>
<th>FileName</th>
</tr>
#foreach (var item in Model.RtdbFiles)
{
<tr>
<td> <input id="Text1" type="text" value="#item.Id" name="RtdbFiles[#i].Id" hidden/>#item.Id</td>
<td> <input id="Text1" type="text" value="#item.Filename" name="RtdbFiles[#i].Filename" /></td>
</tr>
i++;
}
</table>
<input id="Submit1" type="submit" value="submit" />
</form>
Update
Controller:
[HttpPost("UpdateMilestones")]
public async Task<IActionResult> UpdateMilestones(TestRecord testRecord)
{
db.Entry(testRecord).State = EntityState.Modified;
db.Entry(testRecord).Property(x => x.Id).IsModified = false;
foreach (var item in testRecord.RtdbFiles)
{
db.Entry(item).State = EntityState.Modified;
db.Entry(item).Property(x => x.Id).IsModified = false;
}
await db.SaveChangesAsync();
return RedirectToAction("Milestones", new { id = testRecord.Id });
}
Here is the test result:
I have a list of categories and each category may have subcategories. I am getting the list with correct records in my controller but if I try to use the list of subcategories in my view, it shows nothing.
Classes:
public class DashboardTile
{
public int ID { get; set; }
public int? CategoryID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
public class DashboardTileBO : DashboardTile
{
public bool IsChecked { get; set; }
public List<DashboardTileBO> DashboardTiles { get; set; }
}
public class AddTileVM
{
public List<DashboardTileBO> DashboardTiles { get; set; }
}
Controller:
public ActionResult AddTiles()
{
var allDashBoardTiles = BLL.PublicLayer.GetAllDashBoardTiles(SessionItems.UserId);
var addTileVm = new AddTileVM()
{
DashboardTiles = allDashBoardTiles,
};
return PartialView(addTileVm );
}
View:
#model Hugo.BusinessObjects.AddTileVM
<link href="~/Content/js/nestable/nestable.css" rel="stylesheet"/>
<script src="~/Content/js/nestable/jquery.nestable.js"></script>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Add/Remove Tiles to desktop</h4>
</div>
#using (Ajax.BeginForm("AddTiles", "Default",new AjaxOptions {OnSuccess = "OnSuccess" }))
{
#Html.AntiForgeryToken();
<div class="modal-body">
<div class="row">
<div class="col-lg-12">
#for (var i = 0; i < Model.DashboardTiles.Count; i++)
{
<div class="col-lg-5">
<label class="">
#Html.CheckBoxFor(a=> Model.DashboardTiles[i].IsChecked)
#Model.DashboardTiles[i].Description
</label>
</div>
#*if (#Model.DashboardTiles[i].DashboarTiles.Count > 0)
{
for (var j = 0; j < #Model.DashboardTiles[i].DashboarTiles.Count; j++)
{
<div class="col-lg-5">
<label class="">
#Html.CheckBoxFor(a => #Model.DashboardTiles[i].DashboarTiles[j].IsChecked)
#Model.DashboardTiles[i].DashboarTiles[j].Description
</label>
</div>
}
}*#
#Html.HiddenFor(a=> Model.DashboardTiles[i].ID)
}
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-rounded btn-sm btn-tiles" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-rounded btn-sm btn-tiles" >Update</button>
</div>
}
</div><!-- /.modal-content -->
}
method to fetch list
public static List<BusinessObjects.DashboardTileBO> GetAllDashBoardTiles(long userId)
{
using (var context = new HugoInternalEntities())
{
var distincCategoryIds = context.DashboardTiles.Select(a => a.CategoryID).Distinct().ToList();
var allTiles = (context.DashboardTiles.Where(a => distincCategoryIds.Contains(a.ID)).Select(x=>
new BusinessObjects.DashboardTileBO
{
ID = x.ID,
Name = x.Name,
Description = x.Description,
})).ToList();
var list = context.DashboardUserTiles.Where(a => a.UserID == userId).Select(a => a.DashboardTileID).ToList();
allTiles.ForEach(a => a.IsChecked = list.Contains(a.ID));
allTiles.ForEach(a=>a.DashboardTiles=context.DashboardTiles.Where(b=>a.ID==b.CategoryID && b.ID != a.ID).Select(x=>
new BusinessObjects.DashboardTileBO
{
ID = x.ID,
Name = x.Name,
Description = x.Description,
}).ToList());
allTiles.ForEach(a => a.DashboardTiles.ForEach(b => b.IsChecked = list.Contains(b.ID)));
return allTiles;
}
}
In the above view my list of main categories is shown but if I uncomment my code for subcategories nothing is shown. Please see the screenshot of the list I am getting in addTileVm
List of tiles: