Grid MVC how to add the details section - asp.net-mvc

I have been using Grid.Mvc to display some records for a while now, however as more and more properties have been added to the model, the grid has become ugly and too busy.
So I revisted the Grid.Mvc documentation pages and realised that in the demo provided:
http://gridmvc.azurewebsites.net/
They are rather neatly displaying full details of the record in a div on the right hand side of the page under the title Order details, meaning the actual grid does not get bogged down with the finer details.
I want to implement this to my project but I cannot work out how this works, there seems to be no documentation on the website that I can find that explains this, even though it seems to be used in every example they show. When I click view source I cannot see any source for how the data is being retrieved asynchronously per row.
How do I add a details section for each selected row as per the example linked?
edit: Object im passing back as JSON
public class MasterEmailViewModel
{
public int EmailId { get; set; }
public int MvpId { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
public string AddressLineOneOld { get; set; }
public string AddressLineTwoOld { get; set; }
public string AddressLineThreeOld { get; set; }
public string AddressLineFourOld { get; set; }
public string AddressLineFiveOld { get; set; }
public string PostcodeOld { get; set; }
public string AddressLineOneNew { get; set; }
public string AddressLineTwoNew { get; set; }
public string AddressLineThreeNew { get; set; }
public string AddressLineFourNew { get; set; }
public string AddressLineFiveNew { get; set; }
public string PostcodeNew { get; set; }
public bool IsChecked { get; set; }
public string Comment { get; set; }
//navigation prop
}

I have done the exact same thing with one of my projects.
The example is just using a jquery $post
View
<div class="row">
<div class="col-md-3 col-md-push-9">
<h4>Order details
</h4>
<div id="emailId">
<p class="muted">
Select order to display detailed infomation
</p>
</div>
<div id="firstNameId">
</div>
</div>
<div class="col-md-9 col-md-pull-3">
//Grid stuff
</div>
</div>
JS
$(function () {
// ordersGrid is what you called your Grid e.g. #Html.Grid(Model).Named("ordersGrid")
pageGrids.ordersGrid.onRowSelect(function (e) {
$.post("/Home/GetOrder?id=" + e.row.OrderID, function (data) {
if (data.Status <= 0) {
alert(data.Message);
return;
}
// Replace the data
$("#emailId").html(data.EmailId);
$('#firstNameId').html(data.FirstName);
});
});
});
Controller
[HttpPost]
public ActionResult GetOrder(int id)
{
// Get the data
return Json(data);
}

Related

How to post a collection of models with its parent model in ASP.Net?

I have a Visit model with properties and a VisitorArivalHistory Collection. I also bind this model to a form which I will show later.
public class Visit
{
public int Id { get; set; }
public int CreatorId { get; set; }
public string Description { get; set; }
public string Location { get; set; }
public string Company { get; set; }
public DateTime ExpectedArrival { get; set; }
public DateTime ExpectedDeparture { get; set; }
public DateTime? Arrival { get; set; }
public DateTime? Departure { get; set; }
public string Status { get; set; }
public bool Announced { get; set; }
public virtual ICollection<VisitorArivalHistory> VisitorArivalHistories { get; set; }
}
The VisitorArivalHistory has a boolean property called HasArrived which I use this as a checkbox. This model basically holds one arrival history of a visitor for each visit. So a visit object knows the arrival time of each visitor.
public class VisitorArivalHistory
{
public int Id { get; set; }
public Visitor Visitor { get; set; }
public DateTime? Arrival { get; set; }
public bool HasArrived => Arrival != null;
public VisitorArivalHistory() { }
}
I am able to bind to VisitorArivalHistory via Visit model:
<div class="form-row">
<div class="col-lg-12">
<h3>Arrival visitors</h3>
</div>
<div class="col-lg-12">
#for (int i = 0; i < Model.Visit.Visitors.Count; i++)
{
var visitor = Model.Visit.Visitors.ElementAt(i);
#if (Model.IsReceptionist)
{
var visitorArivalHistory = Model.VisitorArivalHistories.FirstOrDefault(x => x.VisitorId == visitor.Id);
#if (visitorArivalHistory != null)
{
<div class="form-group col-md-3">
<h5>#visitor.GetFullName()</h5>
<label asp-for="#Model.Visit.VisitorArivalHistories.ElementAt(i).HasArrived">Has arrived?</label>
<input asp-for="#Model.Visit.VisitorArivalHistories.ElementAt(i).HasArrived" />
</div>
}
}
}
</div>
</div>
The VisitorArivalHistory.HasArrived is in the form of checkboxes I want to check or uncheck. It all works.
The problem is that I am not able to get the value from those checkboxes because when I submit the form, the VisitorArivalHistory is null.
How can I fix this? I could not find a tag helper for the checkboxes to store its values.
I am really stuck.
public DateTime? Arrival { get; set; }
public bool HasArrived => Arrival != null;
Your code has set the HasArrived value to be derived from Arrival, it's no need to add a checkbox to the page.
The problem is that I am not able to get the value from those
checkboxes because when I submit the form, the VisitorArivalHistory is
null.
To solve the problem, I tried the following steps to get the value of VisitorArivalHistory.
1.Add List in Visit.cs
public List<VisitorArivalHistory> VisitorArivalHistories { get; set; }
2.Change VisitorArivalHistories.ElementAt(i) into VisitorArivalHistories[i] in your form
Result:

ASP.net MVC - Dynamic Bootstrap Progress Bar in View

I'm trying to use a progress bar in my web application to visually display the number of signatures a cause (petition) has relative to the target number of signatures. Part of the Cause model is a list of Signatures existing in another table. I'm trying to create and add the current variables to the progress bar, but none of my attempts seem to be working and I'm unsure where I'm going wrong. I have tried several permutations of the following code without any success. I just get empty progress bars in the web browser. Any help would be appreciated:
#model IEnumerable<SoWokeWebApp.Models.Cause.Cause>
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#foreach (var item in Model)
{
<div class="jumbotron">
<img src="#item.ImageURL" alt="Alternate Text" class="img-fluid" />
<h1>#Html.DisplayFor(model => item.CauseTitle)</h1>
<p>#Html.DisplayFor(model => item.Description)</p>
<p>#item.Signatures.Count</p>
#foreach (var Signature in item.Signatures)
{
<p>#Signature.UserEmail</p>
<p>#Signature.FirstName</p>
}
#{int target = item.TargetSignatures;
int current = item.Signatures.Count;
int progress = current / target * 100;}
<div class="progress">
<div class="progress-bar" role="progressbar" style="width: #progress%" aria-valuenow="#current" aria-valuemin="0" aria-valuemax="#target"></div>
</div>
<p><a class="btn btn-primary btn-lg" href="#" role="button">#Html.ActionLink("Sign Cause", "Create", "Signatures")</a></p>
</div>
The models are as follows:
public class Cause
{
[Key]
[StringLength(100)]
[DisplayName("Cause Title")]
public string CauseTitle { get; set; }
[Required]
[StringLength(500)]
public string Description { get; set; }
[Required]
public string UserEmail { get; set; }
[Required]
[DisplayName("Date Posted")]
public DateTime DatePosted { get; set; }
public string ImageURL { get; set; }
[DisplayName("Catagory")]
public string CatagoryTitle { get; set; }
[ForeignKey("CatagoryTitle")]
public Catagory Catagory { get; set; }
[DisplayName("Signatures Target")]
public int TargetSignatures { get; set; }
public virtual List<Signature> Signatures { get; set; }
}
and
public class Signature
{
[Key]
public int SignatureId { get; set; }
[Required]
public string UserEmail { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string CauseTitle { get; set; }
[ForeignKey("CauseTitle")]
public virtual Cause Cause { get; set; }
}
I found this neat little trick using SignalR with a Bootstrap Progress Bar in MVC:
SignalR Progress Bar Simple Example - Sending Live Data from Server to Client
I had to tweak ProgressHub.cs and change hubContext.Clients.All to hubContext.Clients.Group(user) or the progress bar messages went to all clients. See: Working with Groups in SignalR
It does seem to generate some traffic.

knockout 3 level mapping, 3rd level doesnt change

I am new to KnockoutJS and I am trying to create a 3-level model binding, for a master-detail ASP.NET MVC view.
here is the screen i am trying to implement this behavior on:
I have the following ViewModel design
public class CreateReservationViewModel
{
public string Title { get; set; }
public String LogoPath { get; set; }
public string StartDate { get; set; }
public string EndDate { get; set; }
public string StartTime { get; set; }
public string EndTime { get; set; }
public int TimeSpan { get; set; }
public int MinPersons { get; set; }
public int MaxPersons { get; set; }
public List<ReservationOptionViewModel> ReservationOptions { get; set; }
public string MessageToClient { get; set; }
public CreateReservationViewModel()
{
ReservationOptions = new List<ReservationOptionViewModel>();
}
}
public class ReservationOptionViewModel
{
public int Id { get; set; }
public string Title { get; set; }
public int TypeId { get; set; }
public string TypeDescription { get; set; }
public string Info { get; set; }
public List<ValuesViewModel> ReservationOptionValues { get; set; }
public ReservationOptionViewModel()
{
ReservationOptionValues = new List<ValuesViewModel>();
}
}
public class ValuesViewModel
{
public int Id { get; set; }
public string ValueTitle { get; set; } // this value i cant seem to get to bind user input always defaults to whatever i set it in knockout
}
and here is my .js and .html https://jsfiddle.net/camLpdty/
Although I am able to successfully bind the first and second levels, 3rd level binding always picks up default values i.e: [({ Id: 0, ValueTitle: "this is read only and cant change"})] }
everything else seems to work...
From your jsfiddle it looks like the binding context for the Values column is a bit off. data-bind="value: Values.ValueTitle" - Here Values is an array but you're trying to bind a single input box to it. It doesn't know which element's ValueTitle to use so it's probably not bound to anything. You'll need to use a "foreach" or change how you're displaying that information
As a quick test you can try changing that last binding from:
<td>
<input class="form-control input-sm"
data-bind="value: ReservationOptionValues.ValueTitle" />
</td>
to
<td data-bind="foreach: ReservationOptionValues">
<input class="form-control input-sm" data-bind="value: ValueTitle" />
</td>
This will create an input box for every element in the Values array so you probably don't want to leave it like this, but it should highlight the knockout-context problem

Login + Register + another model in a single view?

I am trying to find the best solution so that on any given page I always have a login model, a register model and another model (or more). The login and register models are found in the navbar and the footer respectively on each page.
Specifically I have a situation whereby I have a course page which populates from a table depending on which course has been looked up via the url inputted. So the page loads the course page model. On the same page in the header and footer I need to have the login and register forms which both require their own models.
The course page is populated using a foreach loop:
COURSE PAGE VIEW (SHORTENED):
#model IEnumerable<oltinternational_mvc.Models.Course_page>
#{
Layout = "~/Views/Shared/_courselayout.cshtml";
foreach (var cpage in Model) { ViewBag.Title = #cpage.page_title; }
}
#foreach (var cpage in Model)
{
if (cpage.template_type == 2)
{
<div id="main_container">
<article class="content">
<h1>
#cpage.Title
</h1>
<section>
#Html.Raw(cpage.main_image)
<h3>
#cpage.Country
</h3>
<p>#cpage.intro_1</p>
#if (!string.IsNullOrEmpty(cpage.intro_2))
{
<p>#cpage.intro_2</p>
}
#if (!string.IsNullOrEmpty(cpage.intro_3))
{
<p>#cpage.intro_3</p>
}
View sample pages
#if (!string.IsNullOrEmpty(cpage.website_button))
{
#Html.Raw(cpage.website_button)
}
else {
Licensing options
}
#Html.Raw(cpage.popup_script)
<div class="clearfloat"></div>
</section>
</article>
</div>
The controller is as follows:
public ActionResult Course_page(string id)
{
string abbrev = id;
var cpage = from c in db.Course_page
select c;
if (!String.IsNullOrEmpty(abbrev))
{
cpage = cpage.Where(x => x.abbrev.Contains(abbrev));
}
return View(cpage);
}
and the model:
[Table("course_page")]
public class Course_page
{
[Key]
public int CourseID { get; set; }
public string Meta { get; set; }
public string Title { get; set; }
public string Country { get; set; }
public string main_image { get; set; }
public string list_1 { get; set; }
public string list_2 { get; set; }
public string list_3 { get; set; }
public string list_4 { get; set; }
public string list_5 { get; set; }
public string list_6 { get; set; }
public string list_7 { get; set; }
public string list_8 { get; set; }
public string list_9 { get; set; }
public string list_10 { get; set; }
public string testim_1 { get; set; }
public string testim_2 { get; set; }
public string testim_3 { get; set; }
public string course_site { get; set; }
public string popup_script { get; set; }
public string abbrev { get; set; }
public string page_title { get; set; }
public int template_type { get; set; }
public string intro_1 { get; set; }
public string intro_2 { get; set; }
public string intro_3 { get; set; }
public string website_button { get; set; }
}
In the navbar I have the following list item which references my ajax logging in form:
<li class="login">
<span>Login</span>
#using (Ajax.BeginForm("login", "Account", new AjaxOptions
{
HttpMethod = "POST",
UpdateTargetId = "login_box"
}, new { id = "login_box" }))
{
#Html.Partial("_login")
}
</li>
Which loads this form:
#model myproject_mvc.Models.LoginViewModel
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<div id="login_box">
<div class="sign_up_box"> Sign up in seconds </div>
#Html.ValidationSummary(true)
#Html.AntiForgeryToken()
<div class="login_box_lower">
<p class="login_box_or">or</p>
<p class="login_sign_in">Sign in</p>
<div style="position:relative;">
#Html.EditorFor(model => model.Email, new { htmlAttributes = new { placeholder = "Email", maxlength = "18", #class = "login_username clearable" } })
<span class="login_username_icon"></span>
</div>
<div style="position:relative;">
#Html.EditorFor(model => model.Password, new { htmlAttributes = new { placeholder = "Password", maxlength = "18", #class = "login_pw clearable" } })
<span class="login_pw_icon"></span>
</div>
Login
<div class="clearfloat"></div>
Forgot password?
</div>
</div>
With the following model:
public class LoginViewModel
{
[Required]
[Display(Name = "Email")]
[EmailAddress]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
The same situation is such for the Register form in the footer, which uses the RegisterViewModel. Bearing in mind the actual form above for logging in isn't completed yet, it just directs to a /account url for now.
I've tried a few solutions I've found online but can't seem to make any of them work, I think in part because I am using foreach to populate the view from the course page model. I don't see how I can use a viewmodel because I am using a foreach loop on the page.
What I would like to know is what is the best way to deal with this situation?
You should create and use viewmodel(s) instead of directly referencing your objects. Perhaps something like this.
Create a new class, MyViewModel.cs (or whatever name you prefer)
public class MyViewModel
{
public IEnumerable<oltinternational_mvc.Models.Course_page> CoursePages { get; set; }
public myproject_mvc.Models.LoginViewModel { get; set; }
}
Now in your view, instead of referencing the models directly, you would reference the view model. Something like #model myproject_mvc.ViewModels.MyViewModel
In your foreach loop you would so something like this #foreach (var cpage in Model.CoursePages)

using nested class within class doesnt works in mvc 3

Hi I have included nested class within a class to use it in a view but it doesnt show up the properties of that nested class below is the class, I want to use sku in the view :
View:
#model Nop.Web.Models.Catalog.CategoryModel
<div class="product-item">
<h2 class="product-title">
#Model.Name
</h2>
<div class="description">
**#Model.ProductVariantModels.Select(x => x.Sku)//doesnt works** // partial class productvariant
</div>
<div class="add-info">
#Model.Name <br/> #Model.FullDescription //values from class CategoryModel
</div>
</div>
Model:
public class CategoryModel : BaseEntityModel
{
public CategoryModel()
{
ProductVariantModels = new List<ProductVariantModel>();
}
public string Name { get; set; }
public string FullDescription { get; set; }
public string MetaKeywords { get; set; }
public string MetaDescription { get; set; }
public string MetaTitle { get; set; }
public IList<ProductVariantModel> ProductVariantModels { get; set; }
public class ProductVariantModel : BaseNopEntityModel
{
public string Name { get; set; }
public bool ShowSku { get; set; }
public string Sku { get; set; }
public string Description { get; set; }
}
}
ProductVariantModels is a List. You have to enumerate the List.
#foreach (var pvModel in Model.ProductVariantModels) {
#pvModel.Sku
}
If you're using HtmlHelpers, you have to use a for loop with an index rather than a foreach loop.
As explained really well here, the automatic model binding expects field input names to have a 'dot' notation like Property.SubProperty.SubSub... to match the instance properties when assigning -- but if you render them in a foreach loop they won't have the full expression, and thus won't output the full 'dot' notation.
Also see MVC 4 binding nested list of lists result

Resources