MVC : Routing route value acting strange? - asp.net-mvc

I have a viewmodel which looks like
public class BusinessUnitDetail
{
public Guid Id { get; set; }
public string Name { get; set; }
public LicensesDetails LicensesDetails { get; set; }
public RepresentativeDetails RepresentativeDetails { get; set; }
}
public class LicensesDetails
{
public string BusinessUnitName { get; set; }
public List<LicensesVM> Licenses { get; set; }
}
public class RepresentativeDetails
{
public string BusinessUnitName { get; set; }
public List<RepresentativeVM> Representatives { get; set; }
}
Currently my view contains two tables which shows List of Representative and List of Licenses.
Here is the view, quite long but anyways
#{
ViewBag.Title = "BusinessUnitDetails";
Layout = "~/Views/shared/_BootstrapLayout.basic.cshtml";
int snor = 1;
int snol = 1;
}
<fieldset>
<legend>#Model.Name <small>Representatives</small></legend>
</fieldset>
<table class="table table-striped table-hover table-bordered">
<caption></caption>
<thead>
<tr>
<th>S.No</th>
<th>Name</th>
</tr>
</thead>
#foreach (var entry in Model.RepresentativeDetails.Representatives)
{
<tr>
#*#Html.HiddenFor(m=>entry.BusinessUnitId)*#
<td>#(snor++)</td>
<td>#entry.UserName</td>
<td>
<div class="btn-group" >
<a class="btn btn-primary dropdown-toggle" data-toggle="dropdown" href="#">
Action<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li>#Html.ActionLink("Details", "RepresentativeDetails",routeValues:new{entry.BusinessUnitId})</li>
</ul>
</div>
</td>
</tr>
}
</table>
<fieldset>
<legend>#Model.Name <small>Licenses</small></legend>
</fieldset>
<table class="table table-striped table-hover table-bordered">
<caption></caption>
<thead>
<tr>
<th>S.No</th>
<th>Kind</th>
</tr>
</thead>
#foreach (var entry in Model.LicensesDetails.Licenses)
{
<tr>
#* #Html.HiddenFor(m=>entry.BusinessUnitId)*#
<td>#(snol++)</td>
<td>#entry.LicenseName</td>
<td>
<div class="btn-group" >
<a class="btn btn-primary dropdown-toggle" data-toggle="dropdown" href="#">
Action<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li>#Html.ActionLink("Details", "LicenseDetails",routeValues:new{entry.BusinessUnitId})</li>
</ul>
</div>
</td>
</tr>
}
</table>
When i click the Details action of some row in License table, I expected it to redirect me to LicenseDetails action method as stated in Html.ActionLink, but it doesn't.
The route when i click the details action is
Customer/businessunit/LicenseDetails?BusinessUnitId=25c70a1d-0ed5-406f-8cae-1c6c1bf7d0da
I don't know how the LicenseDetails?BusinessUnitId= part is being generated, and why the action doesn't forward to the LicenseDetails method in controller.
What can I do so that, when i click the details action in second table, I get forwarded to proper controller action which proper Guid id
Any idea?
Edit 1: License Detail Action Methode
public ActionResult LicenseDetails(Guid licenseId)
{
var licenseDetails = _businessUnitRepository.GetLicenseDetail(licenseId);
return View(licenseDetails);
}

Your ActionLink seems fit to some routes. Type it more strongly with controllername:
#Html.ActionLink("Details", "LicenseDetails", "Home", new { licenseId = entry.BusinessUnitId }, null)

Try with this syntax:
#Html.ActionLink("Details", "LicenseDetails", new { licenseId = entry.BusinessUnitId })

Related

Models not collecting values from View to HttpPost Controller Method

I am currently trying to pass a list of models from my view to my controller. Currently, I am able to grab each model from the view and place them inside of a list and pass the list within the HttpPost controller ActionResult method. However when doing this, none of the models have their data within them as all of the models have their property values set to either 0 or null.
My code is as follows:
View:
#using (Html.BeginForm("SaveCarouselImageData", "Admin", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="card shadow">
<div class="card-header border-0">
<div class="row align-items-center">
<div class="col">
<h3 class="mb-0">Homepage Carousel</h3>
</div>
</div>
</div>
<div class="table-responsive">
<table class="table align-items-center table-flush">
<thead class="thead-light">
<tr>
<th></th>
<th scope="col">Image</th>
<th scope="col">Order Num</th>
<th></th>
</tr>
</thead>
<tbody id="carousel-content">
#for (int i = 0; i < Model.Count(); i++)
{
<tr>
<th><input type="number" value="#Model[i].getId()" name="id" hidden readonly /></th>
<th scope="row">
<img src="#Url.Content(Model[i].getImgLoc())" name="imgLoc" class="carousel-img-thumbnail" alt="Image" />
</th>
<td>
#Html.TextBoxFor(model => model[i].orderNum, Model[i].getOrderNum().ToString(), new { type = "number", name = "orderNum" })
</td>
<td>
<a class="btn btn-danger btn-lg btn-block openDeleteModal" data-toggle="modal" href="#deleteImageModal" data-id="#Model[i].getId()">
DELETE
</a>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
<div class="row form-button-group">
<div class="col-md-6 col-sm-12 form-button-padding">
<button type="button" class="btn btn-success btn-lg btn-block" data-toggle="modal" data-target="#addImageModal">
+ Add New Image
</button>
</div>
<div class="col-md-6 col-sm-12 form-button-padding">
<button type="submit" class="btn btn-primary btn-lg btn-block">
Save Changes
</button>
</div>
</div>
}
Controller:
// POST: Saves Carousel Image Data
[HttpPost]
public ActionResult SaveCarouselImageData(List<CarouselModel> images)
{
if (!checkLoginCredentials())
{
return RedirectToAction("Login", "Home");
}
else
{
List<CarouselModel> updatedModels = new List<CarouselModel>();
foreach (CarouselModel img in images)
{
CarouselModel dbModal = siteServices.getCarouselImageById(img.getId());
dbModal.setOrderNum(img.getOrderNum());
}
int result = siteServices.updateCarouselTable(updatedModels);
return RedirectToAction("HomepageCarousel", "Admin");
}
}
Model:
public class CarouselModel
{
[Display(Name="id")]
private int id;
[Display(Name = "imgLoc")]
private string imgLoc;
[Display(Name = "orderNum")]
public int orderNum;
public int getId()
{
return this.id;
}
public string getImgLoc()
{
return this.imgLoc;
}
public int getOrderNum()
{
return this.orderNum;
}
public void setId(int id)
{
this.id = id;
}
public void setImgLoc(string imgLoc)
{
this.imgLoc = imgLoc;
}
public void setOrderNum(int orderNum)
{
this.orderNum = orderNum;
}
}
Again, the models themselves are currently getting passed from the View and into the List for the SaveCarouselImageData method, but all of their property values are null or 0.
Please assist.
all of their property values are null or 0.
I think that's because your CarouselModel's properties lack get;set; which the ASP.NET MVC Binding makes use of by default.
During POST, the binder will try to bind the form values to that of your model, but since it lacks accessors, it can't set any of the values that comes from the form.
Besides that, they're both specified to be Private which can only be accessed within the class, you should use Public if you want them to be set externally.
The easiest solution is to make them Public and add the accessors get;set;:
[Display(Name="id")]
public int id {get;set;}
[Display(Name = "imgLoc")]
public string imgLoc {get;set;}
[Display(Name = "orderNum")]
public int orderNum {get;set;}
If you still want ID and ImgLoc to remain Private then, you could do something like this;
private int _id {get;set;}
private string _imgLoc {get;set;}
[Display(Name = "orderNum")]
public int orderNum;
[Display(Name="id")]
public int id{
get{
return this._id;
}
set{
this._id = value;
}
}
[Display(Name = "imgLoc")]
public string imgLoc{
get{
return this._imgLoc;
}
set{
this._id = value;
}
}
Then change your HTML input fields to use the public properties.

Create table based on two nested foreach loops in MVC Razor

I tried to create a week view table by using two nested foreach loop,
Model:
public class EmployeeViewModel
{
public ServiceProvider Employee { get; set; }
public IEnumerable<DayViewModel> Days { get; set; }
}
public class DayViewModel
{
public DateTime TheDate { get; set; }
public TimePeriodCollection FreeTimes { get; set; }
public IEnumerable<DateTime> Blocks { get; set; }
}
So I have a list of dayes and each days has a list of times
In the view:
#model BookingSystem.ViewModels.EmployeeViewModel
:
<div class="table-responsive">
<table class="table table-bordered table-striped table-hover">
#foreach (var item in Model.Days)
{
<th class="text-center">
#item.TheDate.ToString("dddd") <br /> #item.TheDate.ToString("MM-dd")
</th>
}
<tr>
#foreach (var item in Model.Days)
{
<td class="text-center col-sm-15">
#if (item.FreeTimes != null)
{
foreach (var iu in item.FreeTimes)
{
<span>
#Html.ActionLink(iu.Start.ToString("HH:mm") + " - " + iu.End.ToString("HH:mm"), "Index", "ClientForm", new { ChoosedStartTime = iu.Start.ToString("HH:mm"), ChoosedEndTime = iu.End.ToString("HH:mm"), ChoosedDate = item.TheDate }, new { #class = "btn btn-primary" })
</span>
<br />
}
}
</td>
}
</tr>
</table>
</div>
I had an acceptable result but what I want is: each time block to be in a separate row so I can use table-striped class and put borders.
This is what I did:
Image of the result
Any Ideas.
A Quick Solution would be to control by a variable.
Let's say you have Treatments and Appointments for a given Patient. It would be something like
public class Patient: Person
{
public virtual ICollection<Treatment>? Treatments { get; set; }
}
And a Patient as multiple Treatments
public class Treatment
{
public int Id { get; set; }
[DisplayName("Treatment Type")]
public TreatmentType TreatmentType { get; set; }
[DisplayName("Upfront Cost (INR)")]
public decimal? UpFrontCost { get; set; } = 0;
[DisplayName("Total Cost (INR)")]
public decimal? TotalCost { get; set; } = 0;
public virtual ICollection<Appointment>? Appointments { get; set; }
public int PatientId { get; set; }
public virtual Patient? Patient { get; set; }
}
And each treatment would have multiple Appointments.
public class Appointment
{
public int Id { get; set; }
[DisplayName("Appointment Time")]
public DateTime AppointmentTime { get; set; }
[DisplayName("Appointment Status")]
public AppointmentStatus AppointmentStatus { get; set; }
[DisplayName("Charge (INR)")]
public decimal? Cost { get; set; } = 0;
[ForeignKey(nameof(Treatment))]
[DisplayName(nameof(Treatment))]
public virtual int TreatmentId { get; set; }
public virtual Treatment? Treatment { get; set; }
}
Use this approach to have nested models rendered using #foreach in razor views.
<div class="row flex-row">
<h4> #Html.DisplayNameFor(model => model.Treatments)</h4>
<hr />
#if (Model.Treatments.Count == 0)
{
<p>
Treatment not started. <a asp-controller="Treatments" asp-action="Create" asp-route-patientId="#Model.Id">Start treatment</a> for this paitent!
</p>
}
else
{
<table class="table table-striped">
#{
var trHead = 0;
var apHead = 0;
}
#foreach (var tmnt in Model.Treatments)
{
if (trHead == 0)
{
<thead>
<tr>
<th>
#Html.DisplayNameFor(a => tmnt.TreatmentType)
</th>
<th>
#Html.DisplayNameFor(a => tmnt.TotalCost)
</th>
<th></th>
</tr>
</thead>
trHead += 1;
}
<tbody>
#{
<tr>
<td>
#Html.DisplayFor(a => tmnt.TreatmentType)
</td>
<td>
#Html.DisplayFor(a => tmnt.TotalCost)
</td>
<td>
<a class="float-end" asp-controller="Treatments" asp-action="Details" asp-route-id="#tmnt.Id">View treatment</a>
</td>
</tr>
apHead = 0;
}
<tr>
<td colspan="4">
#if (tmnt.Appointments.Count == 0)
{
<p>
No Appointments Scheduled. <a asp-controller="Appointments" asp-action="Create" asp-route-treatmentId="#tmnt.Id">Schedule an appointment</a> for this paitent!
</p>
}
else
{
<table class="table mb-0">
#foreach (var apt in tmnt.Appointments)
{
if (apHead == 0)
{
<thead>
<tr>
<th>
#Html.DisplayNameFor(a => apt.AppointmentTime)
</th>
<th>
#Html.DisplayNameFor(a => apt.AppointmentStatus)
</th>
<th>
#Html.DisplayNameFor(a => apt.Cost)
</th>
<th></th>
</tr>
</thead>
apHead += 1;
}
<tbody>
<tr>
<td>
#Html.DisplayFor(a => apt.AppointmentTime)
</td>
<td>
#Html.DisplayFor(a => apt.AppointmentStatus)
</td>
<td>
#Html.DisplayFor(a => apt.Cost)
</td>
<td>
<a class="float-end" asp-controller="Appointments" asp-action="Details" asp-route-id="#apt.Id">View Appointment</a>
</td>
</tr>
</tbody>
}
</table>
}
</td>
</tr>
</tbody>
}
</table>
}
</div>
Then, obtain the following output
Responsive mobile view (scope for some improvement though! :p
PS: using Bootstrap 5.1
References: https://getbootstrap.com/docs/5.0/content/tables/#nesting

Passing back child entity from MVC view to controller

I'm trying to delete entries which are marked (checked) in view, but not sure how to pass back the collection back to the controller
my mode is:
Group which has ICollection<SubGroup> SubGroups and SubGroup has ICollection<Event> Events
I pass Group to the view and iterate and display Event details including a checkbox so if it's checked the event entry should be deleted.
When I get the postback to the controller, Group.SubGroups is null
How do I make sure the child entities are passed back to the controller?
Can I use #Html.CheckBox instead Of <input type="checkbox"... ?
Update: Model
public class Group
{
[Key]
public int GroupId { get; set; }
public virtual IList<SubGroup> SubGroups { get; set; }
....
}
public class SubGroup
{
[Key]
public int SubGroupId { get; set; }
public virtual IList<Event> Events { get; set; }
....
}
public class Events
{
[Key]
public int EventId { get; set; }
public string EventName { get; set; }
public bool IsDeleted { get; set; }
....
}
I am passing Group to the view (see below) as the Model and want to delete events which are checked by the user
View:
#using System.Globalization
#model NS.Models.Group
#{
ViewBag.Title = "Edit";
}
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Booking Details</legend>
<div class="display-label">
Group Name
</div>
<div class="display-field">
#Html.DisplayFor(model => model.GroupName)
</div>
<div class="display-field">
#foreach (var b in Model.SubGroup)
{
groupNo += 1;
<table class="main" style="width: 80%; margin-top: 10px">
<tr>
<th>
#Html.DisplayName("Sub Group ")
#Html.DisplayName(b.SubGroupName)
</th>
</tr>
<table class="main" style="width: 80%;">
<tr>
<th>Event</th>
<th>Delete</th>
</tr>
#foreach (var ev in b.Events)
{
<tr>
<td>
#Html.DisplayFor(modelItem => ev.EventName)
</td>
<td>
<input type="checkbox" id="eventToDelete" name="eventToDelete" value="#ev.EventId" />
</td>
</tr>
}
</table>
</table>
}
</div>
<p>
<input type="submit" name="xc" value="Delete" class="button" />
</p>
</fieldset>
}
Thank You
Try this...
public ActionResult ViewName(FormCollection collection)
{
if(collection['eventToDelete']!=null && collection['eventToDelete'].ToString()!="")
{
//delete....
}
return....
}
Try this
public ActionResult ViewName(Group model)
{
if(model != null && ModelState.IsValid)
{
//delete....
}
return....
}

MVC: Displaying table data not working as expected

I have this table in database:
I'm trying to display the the values (ModuleID and DateEntered) in the browser as table.
I'm using viewbag to pass the value to my view, which is not quite the right way as I get just one row right now. What I'm doing right now is
public ActionResult Index()
{
var modules = (from entries in _db.Modules
orderby entries.DateEntered
select entries);
ViewBag.entries = modules.ToList();
return View();
}
How can I get all rows from the table in above picture and pass it to view?
In my view I currently have:
#using BootstrapSupport
#model Sorama.DataModel.SIS.ModuleStock.Module
#{
ViewBag.Title = "Index";
Layout = "~/Views/shared/_BootstrapLayout.basic.cshtml";
}
<table class="table table-striped">
<caption></caption>
<thead>
<tr>
<th>
Module ID
</th>
<th>
Date Entered
</th>
</tr>
</thead>
<tr>
#foreach (var entry in ViewBag.entries)
{
<td>#entry.ModuleId</td>
<td>#entry.DateEntered</td>
}
<td>
<div class="btn-group">
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
Action
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li>#Html.ActionLink("Details", "Details")</li>
#if (Request.IsAuthenticated && HttpContext.Current.User.IsInRole("Admin"))
{
<li class="divider"></li>
<li>#Html.ActionLink("Edit", "Edit")</li>
<li>#Html.ActionLink("Delete", "Delete")</li>
}
</ul>
</div>
</td>
</tr>
</table>
This shows the values of entire row and not just (ModuleID and DateEntered)
This is what I get in browser.
To sum up, I want to get all the rows from table but only specific columns.
Which is not happening at current situation.
Suggestions?
You misreading your results. There are 3 records with 2 fields each displayed in one row. You have single <tr> in your view and you insert values from ViewBag in <td> tags. You should put new <tr> for each of your record in ViewBag.
It should look more like this:
#foreach (var entry in ViewBag.entries)
{
<tr>
<td>#entry.ModuleId</td>
<td>#entry.DateEntered</td>
<td>
<div class="btn-group">
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
Action<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li>#Html.ActionLink("Details", "Details")</li>
#if (Request.IsAuthenticated && HttpContext.Current.User.IsInRole("Admin"))
{
<li class="divider"></li>
<li>#Html.ActionLink("Edit", "Edit")</li>
<li>#Html.ActionLink("Delete", "Delete")</li>
}
</ul>
</div>
</td>
</tr>
}
Try this
public ActionResult Index()
{
ABCList abc=new ABCList();
var modules = (from entries in _db.Modules
orderby entries.DateEntered
select new ABC {
id=entries.id,
ModuleTypeId=entries.ModuleTypeId,
ModuleId=entries.ModuleId,
DataEntered=entries.DataEntered
});
abc.settings = modules.ToList();
return View();
}
public class ABC
{
public long Id{ get; set; }
public long ModuleTypeId{ get; set; }
public string ModuleId{get;set;}
public DateTime DataEntered{ get; set; }
}
public class ABCList{
public List<ABC> Settings { get; set; }
}
View
#model ABCList
#foreach (var entry in Model.Settings)
{
<tr>
<td>#entry.ModuleId</td>
<td>#entry.DateEntered</td>
<td>
<div class="btn-group">
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
Action<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li>#Html.ActionLink("Details", "Details")</li>
#if (Request.IsAuthenticated && HttpContext.Current.User.IsInRole("Admin"))
{
<li class="divider"></li>
<li>#Html.ActionLink("Edit", "Edit")</li>
<li>#Html.ActionLink("Delete", "Delete")</li>
}
</ul>
</div>
</td>
</tr>
}

How to get data from partial in form

I have
ASP.NET MVC Form in popup with some controls and partial (data grid) with his own Model.
here is popup:
<div id="AddEditDialog" class="none">
#using (Ajax.BeginForm("Save", "Templates", new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "AddEditPlaceHolder",
OnSuccess = "OnSaveSuccess",
HttpMethod = "Post"
}))
{
<div>
<div id="AddEditPlaceHolder"></div>
<div id="PopupButtons" class="btn-holder-centered">
<input type="submit" value="Save" name="SaveButton" />
<input type="button" value="Cancel" name="SaveCancelButton" id="CancelEditHandler" />
</div>
</div>
}
</div>
here is form which I render in AddEditPlaceHolder via js:
#model TemplatesViewModel
<div class="form-field-plain overflow">
<div class="forRow narRow float-left">
#Html.LabelFor(x => x.Revocable)
#Html.CheckBoxFor(x => x.Revocable)
</div>
</div>
<div class="form-field-plain overflow">
<div class="forRow narRow float-left">
#Html.LabelFor(x => x.HtmlTemplate)
#Html.TextAreaFor(x => x.HtmlTemplate)
</div>
</div>
#Html.Partial("_VariablesGridView", Model.Variables)
_VariablesGridView.cshtml:
#model List<TemplateVariableViewModel>
<table id="TemplateVariablesGrid">
<thead>
<tr>
<td>Tag/Code</td>
<td>Prompt</td>
<td>Action</td>
</tr>
</thead>
<tbody>
#foreach (var i in Model)
{
<tr>
<td>
#Html.TextBox("txtTag", #i.Tag, new {})
</td>
<td>
#Html.TextBox("txtPrompt", #i.Prompt, new { })
</td>
<td>
#Html.HiddenFor(x => x.First(s => s.Id == #i.Id).Id)
<label class="delete-variable">delete</label>
</td>
</tr>
}
</tbody>
</table>
<br />
<input type="button" name="btnAddTemplateVariable" value="add new variable"/>
<br />
My problem is :
in Controller 'save form' method public ActionResult Save(TemplateViewModel model)
my model contains all data from form but TemplateViewModel.Variables is empty
Is there any way to fill it in there?
Models:
public class TemplateViewModel
{
public int Id { get; set; }
public string HtmlTemplate { get; set; }
public List<TemplateVariableViewModel> Variables { get; set; }
}
public class TemplateVariableViewModel
{
public int Id { get; set; }
public string Tag { get; set; }
public string Prompt { get; set; }
}
I believe it is because the ASP.Net MVC binding is not putting these fields in context, have a look at your field names delivered to the browser, what is txtTag prefixed by when it gets to the browser and what is is after you do the following:
#Html.Partial("_VariablesGridView", Model)
_VariablesGridView.cshtml:
#model TemplatesViewModel
...
#for (int i = 0; i < Model.Variables.Count; i++)
#Html.TextBox("txtTag", #Model.Variables[i].Tag, new {})
Forgive me if this fails miserably (again), I'm shooting from the hip.

Resources