BeginCollectionItem can not bind partial values - c#

I´m doing Editing a variable length list, ASP.NET MVC 2-style tutorial, but I can´t bind values of my ViewModel to my Create View
Model:
public class WebPages
{
[Key]
public int WebPagesId { get; set; }
public String DomainName { get; set; }
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
public DateTime DomainStart { get; set; }
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
public DateTime DomainExp { get; set; }
public String DomainEmails { get; set; }
public String DomainUsers { get; set; }
public String DomainPasswords { get; set; }
}
}
ViewModel:
public class WebPagesViewModel
{
[Key]
public int Id { get; set; }
public String DomainName { get; set; }
public class Domain
{
public String DomainEmails { get; set; }
public String DomainUsers { get; set; }
public String DomainPasswords { get; set; }
}
public List<Domain> Domains { get; set; }
}
}
Create GetController
public ActionResult Create(WebPages model)
{
var ejemdata = new List<WebPagesViewModel.Domain>
{
new WebPagesViewModel.Domain
{
DomainEmails = "email#gmail.com",
DomainUsers = "Testuser",
DomainPasswords = "testpassword"
}
};
var vm = new WebPagesViewModel
{
Clients = new SelectList(db.ClientsList, "ClientsId", "ClientsName"),
Domains = ejemdata
};
return View(vm);
}
Create View:
#foreach (var item in Model.Domains)
{
{ Html.RenderPartial("_WebPagesPartial", item);}
}
Partial:
#using xxx.Models.ViewModels
#model WebPagesViewModel.Domain
#using HtmlHelpers.BeginCollectionItem
<h2>Partial View</h2>
<div>
#using (Html.BeginCollectionItem("Domains"))
{
Html.EditorFor(x => x.DomainPasswords);
Html.EditorFor(x => x.DomainUsers);
Html.EditorFor(x => x.DomainEmails);
}
I use breakpoint debug, and controller gets values from my ejemdata and it pass to my create view into foreach
Receiving data in View photo
So when the page loads it only loads label from partial but no display anything that is in BeginCollectionItem method and I get this on HTML
HTML:
<input type="hidden" name="Domains.index" autocomplete="off" value="28f4e8e7-cde3-44fe-a739-b4c8acb15ec2">

You use of
Html.EditorFor(x => x.DomainPasswords);
just calls the method but does not output its result. You need to change it to
#Html.EditorFor(x => x.DomainPasswords)
(and ditto for the other 2 HtmlHelpers)

Related

Altering the output value of a SelectList in DropDownListFor in MVC View

I have this code inside my form:
<div class="form-group">
#Html.LabelFor(c => c.ClosingHourId)
#Html.DropDownListFor(c => c.ClosingHourId, new SelectList(Model.ClosingHours, "Id", "Time"), "Select time", new { #class = "form-control" })
#Html.ValidationMessageFor(c => c.ClosingHourId)
</div>
The "Time" property is of type DateTime, so when I click the dropdown list on my page I see full dates and times, but the thing is I want to display only the hours, without the dates, so I want to use something like Time.ToString("H:mm") but I don't know where can I write this so it will work. Maybe the right approach would be to add something like [Display(Name = Time.ToString("H:mm"))] annotation in my ClosingHour Model? I'm not sure if it's possible.
My View Model:
public class CinemaFormViewModel
{
public int? Id { get; set; }
[Required]
[StringLength(255)]
public string Name { get; set; }
[Required]
public string Address { get; set; }
[Required]
[Display(Name = "Total Seats")]
public int TotalSeats { get; set; }
public IEnumerable<OpeningHour> OpeningHours { get; set; }
[Required]
[Display(Name = "Opens At")]
public byte? OpeningHourId { get; set; }
public IEnumerable<ClosingHour> ClosingHours { get; set; }
[Required]
[Display(Name = "Closes At")]
public byte? ClosingHourId { get; set; }
}
My ClosingHour Model:
public class ClosingHour
{
public byte Id { get; set; }
[Required]
public DateTime Time { get; set; }
}
The Action inside the controller which calls the view:
public ActionResult New()
{
var openingHours = _context.OpeningHours.ToList();
var closingHours= _context.ClosingHours.ToList();
var viewModel = new CinemaFormViewModel
{
OpeningHours = openingHours,
ClosingHours = closingHours
};
return View("CinemaForm", viewModel);
}
change you by adding string time property
public class ClosingHour
{
public byte Id { get; set; }
[Required]
public DateTime Time { get; set; }
[NotMapped]
public string ShortTime { get; set; }
}
action
public ActionResult New()
{
var closingHours= _context.ClosingHours.ToList();
var openingHours = _context.OpeningHours.ToList();
closingHours.ForEach(i=> i.ShortTime=i.Time.ToShortTimeString());
openingHours.ForEach(i=> i.ShortTime=i.Time.ToShortTimeString());
// or you can try
closingHours.ForEach(i=> {
i.ShortTime=i.Time.ToShortTimeString();
i.Time=null;
});
openingHours.ForEach(i=> {
i.ShortTime=i.Time.ToShortTimeString();
i.Time=null;
});
var viewModel = new CinemaFormViewModel
{
OpeningHours = openingHours,
ClosingHours = closingHours
};
return View("CinemaForm", viewModel);
}
view
....
#Html.DropDownListFor(c => c.ClosingHourId,
new SelectList(Model.ClosingHours, "Id", "ShortTime"), "Select time", new { #class = "form-control" })
.....

Passing a view-model to view, saving data from text-boxes into the view-model, then transferring that data from the view-model to my models

I don't know if I am going about this the correct way but basically I'm creating an MVC application for a club where I have a Members class, and an Events class. I have a view that displays all the events below
#model IEnumerable<Application.Models.Event>
#{
Layout = null;
}
<h1>Events For this Month!</h1>
<table>
#foreach (var events in Model)
{
<tr>
<td><h2>#events.Name</h2></td>
<td>
#Html.ActionLink("Sign-Up", "SignUpEvent", "User", new { id = events.Id }, null)
</td>
</tr>
}
</table>
when they select, signup next to the event they wish to attend, it takes them to this Action. I created a viewmodel called "MemberEventViewModel that has a member, and an event in it.
public ActionResult SignUpEvent(int id)
{
var Vmodel = new MemberEventViewModel()
{
};
return View(Vmodel);
}
the view returned is where the member enters their pin
#model Application.ViewModels.MemberEventViewModel
#{
Layout = null;
}
<h2>Sign-Up</h2>
#using (Html.BeginForm("SignUp", "User"))
{
#Html.LabelFor(c => c.Member.PIN)
#Html.TextBoxFor(c => c.Member.PIN)
#Html.HiddenFor(c => c.Events.Id)
<button type="submit">Save</button>
}
then finally goes to this controller so I can save and see the event that a member has signed up for
[HttpPost]
public ActionResult SignUp(MemberEventViewModel Vmodel)
{
var EventinDB = _context.Events.SingleOrDefault(c => c.Id == Vmodel.Events.Id);
var MemberinDB = _context.Members.SingleOrDefault(c => c.PIN == Vmodel.Member.PIN);
MemberinDB.Events.Add(EventinDB);
EventinDB.Members.Add(MemberinDB);
return View("ViewEvents", "User");
I'm confused on how to get this working. I want to view what events a member is signed up for, and vice versa. A bridge table called MemberEvents was created when I created the many to many relationship between Events and Members.
also here is my View model class
public class MemberEventViewModel
{
public Member Member { get; set; }
public Event Events { get; set; }
}
here is the events class
public class Event
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
[Display(Name = "Date")]
public DateTime? EventDate { get; set; }
[Required]
[Display(Name = "Start Time")]
public TimeSpan EventStartTime { get; set; }
[Required]
[Display(Name = "End Time")]
public TimeSpan EventEndTime { get; set; }
public int EventTotalTime{ get; set; }
public virtual ICollection<Member> Members { get; set; }
}
and members class
public class Member
{
public int Id { get; set; }
[Required]
[MaxLength(4, ErrorMessage = "PIN must be 4 numbers long"), MinLength(4, ErrorMessage = "PIN must be 4 numbers long")]
public string PIN { get; set; }
[Required]
[Display(Name ="First Name")]
public string FirstName { get; set; }
[Required]
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Display(Name = "Date of Birth")]
public DateTime? Birthdate { get; set; }
public virtual ICollection<Event> Events { get; set; }
}
Looks like there's nothing wrong with how you're adding it but I think you missed context.SaveChanges() after you're adding the MemberEvents object.
[HttpPost]
public ActionResult SignUp(MemberEventViewModel Vmodel)
{
var EventinDB = _context.Events.SingleOrDefault(c => c.Id == Vmodel.Events.Id);
var MemberinDB = _context.Members.SingleOrDefault(c => c.PIN == Vmodel.Member.PIN);
// try using one only
MemberinDB.Events.Add(EventinDB);
// EventinDB.Members.Add(MemberinDB);
// you should save here
_context.SaveChanges();
return View("ViewEvents", "User");
}
Also be sure that you're assigning the properties here;
public ActionResult SignUpEvent(int id)
{
var Vmodel = new MemberEventViewModel()
{
Member = // find the member _context.Members.Where(...)
Events = _context.Events.FirstOrDefault(e=>e.Id == id)
};
return View(Vmodel);
}

How can I bind CountryNames in DropDownListFor

I'm using mvc4. How can I Bind CuntryName and its values in DropdownList
Country?
public class Country
{
public int Cnt_Id { get; set; }
public string Cnt_Name { get; set; }
}
This is my private class
public class HybridEmployee
{
public IEnumerable<Country> GetCount { get; set; }
}
Controller
public ActionResult GetCountry()
{
var x = ObjRepo.GetCountry();
hybrid.GetCount = x;
return View(hybrid);
}
Index.cshtml
#model Mvc_Application.Models.HybridEmployee
#using Mvc_Application.Models
#using (Html.BeginForm("SaveEmp", "Home", FormMethod.Post))
{
#Html.DropDownListFor(x=>x.GetCount.FirstOrDefault().Cnt_Id),new SelectList(Model.GetCount,"","");
}
We can have two approaches as shown below:
Using a ViewBag containing the data for dropdown list.
Model file:
public class State
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public int CountryID { get; set; }
}
In .cs file:
ViewBag.Countries = countryService.All().Select(x => new SelectListItem() { Text = x.Name, Value = x.Id.ToString() }).ToList();
In .cshtml file:
#Html.DropDownListFor(x => x.CountryID, ViewBag.Countries as IEnumerable<SelectListItem>, "Select Country", new { #class = "form-control" })
Using a Model's property containing the data for dropdown list.
Model file:
public class State
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public int CountryID { get; set; }
public List<SelectListItem> Countries { get; set; }
}
In .cs file:
model.Countries = countryService.All().Select(x => new SelectListItem() { Text = x.Name, Value = x.Id.ToString() }).ToList();
In .cshtml file:
#Html.DropDownListFor(x => x.CountryID, Model.Countries, "Select Country", new { #class = "form-control" })
This expression totally doesn't makes sense:
#Html.DropDownListFor(x=>x.GetCount.FirstOrDefault().Cnt_Id),new SelectList(Model.GetCount,"","");
The first argument of DropDownListFor helper (Expression<Func<TModel, TProperty>> expression) doesn't use LINQ expression, it is model binding expression - you must use a property to hold selected value instead. The drop down list binding should be used like this:
Model
public class Country
{
public int Cnt_Id { get; set; }
public string Cnt_Name { get; set; }
public int SelectedCountry { get; set; } // additional property to get selected value
}
View
#Html.DropDownListFor(x => x.SelectedCountry, new SelectList(Model.GetCount, "Cnt_Id", "Cnt_Value"), null)

ViewModel Error : Object reference not set to an instance of an object [duplicate]

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 8 years ago.
I cannot understand why I am getting this error, could you please help me find what I am doing wrong ?
What i am trying to achieve with this ViewModel is because i have many records with the same CartId to use it as a parent and booking.Item.ItemName as a child.
Can you please advise if the way i am trying to do it is correct?
Controller:
public ActionResult MyBookings()
{
var myBookingList = SystemDB.Carts.Where(s => s.Booking.Login == strLoginName).OrderByDescending(s => s.RecordId).Take(10);
List<MyBookingsViewModel> result = new List<MyBookingsViewModel>();
foreach (var item in myBookingList)
{
MyBookingsViewModel model = new MyBookingsViewModel();
model.Carts.RecordId = item.RecordId;
model.Carts.CartId = item.CartId;
model.Carts.Booking.Item.ItemName = item.Booking.Item.ItemName;
model.Carts.Booking.UserFullName = item.Booking.UserFullName;
model.Carts.Booking.RequestDate = item.Booking.RequestDate;
model.Carts.StatusCode.StatusCodeName = item.StatusCode.StatusCodeName;
result.Add(model);
}
return View(result);
}
Model:
public class MyBookingsViewModel
{
public Cart Carts { get; set; }
public List<Booking> Bookings { get; set; }
}
public class Cart
{
[Key]
public int RecordId { get; set; }
public string CartId { get; set; }
public int Count { get; set; }
[ForeignKey("Booking")]
public int BookingId { get; set; }
public virtual Booking Booking { get; set; }
[ForeignKey("StatusCode")]
public int Stage { get; set; }
public virtual StatusCode StatusCode { get; set; }
public Cart()
{
this.Booking = new List<Booking>();
}
}
public class Booking
{
[DisplayName("BookingId")]
public int BookingId { get; set; }
[ForeignKey("Item")]
public int ItemId { get; set; }
public virtual Item Item { get; set; }
[DisplayName("Name")]
public string UserFullName { get; set; }
[DateStart]
[DisplayFormat(DataFormatString = "{0:dd/MM/yy hh:mm tt}", ApplyFormatInEditMode = true)]
public DateTime StartDate { get; set; }
[DateEnd(DateStartProperty = "StartDate")]
[DisplayFormat(DataFormatString = "{0:dd/MM/yy hh:mm tt}", ApplyFormatInEditMode = true)]
public DateTime EndDate { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:d MMM yyyy}", ApplyFormatInEditMode = true)]
public System.DateTime RequestDate { get; set; }
[DisplayName("Login")]
public string Login { get; set; }
[DisplayName("NetworkID")]
public string NetworkID { get; set; }
[DisplayName("Comments")]
public string Comments { get; set; }
}
View:
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Carts.RecordId)
</td>
<td>
<a href="#Url.Action("ItemDetails", new {id=item.Carts.BookingId })" >
#Html.DisplayFor(modelItem => item.Carts.CartId)
</a>
#foreach(var booking in item.Bookings)
{
<li>#booking.Item.ItemName</li>
}
</td>
</tr>
}
Have a look at your model class definition
public class MyBookingsViewModel
{
public Cart Carts { get; set; }
public List<Booking> Bookings { get; set; }
}
and the following lines in your controller code
foreach (var item in myBookingList)
{
MyBookingsViewModel model = new MyBookingsViewModel();
model.Carts.RecordId = item.RecordId;
model.Carts.CartId = item.CartId;
model.Carts.Booking.Item.ItemName = item.Booking.Item.ItemName;
model.Carts.Booking.UserFullName = item.Booking.UserFullName;
model.Carts.Booking.RequestDate = item.Booking.RequestDate;
model.Carts.StatusCode.StatusCodeName = item.StatusCode.StatusCodeName;
result.Add(model);
}
You were trying to set model.Carts.RecordId while model.Carts is null. You need to initialize Carts property before doing so. The initialization should be done in the constructor to avoid code repetition. Change your model class definition to the following
public class MyBookingsViewModel
{
public MyBookingsViewModel()
{
this.Carts = new Cart();
}
public Cart Carts { get; set; }
public List<Booking> Bookings { get; set; }
}
UPDATE:
As per your comment regarding the error in this line:
model.Carts.Booking.Item.ItemName = item.Booking.Item.ItemName;
The problem is model.Carts.Booking is null when you set the value of model.Carts.Booking.Item. This line also has a similar problem:
model.Carts.StatusCode.StatusCodeName = item.StatusCode.StatusCodeName;
model.Carts.StatusCode is null when you set the value of model.Carts.StatusCode.StatusCodeName, so you need to add the similar thing to the Cart class definition in the constructor like below:
public class Cart()
{
public Cart()
{
this.Booking = new Booking();
this.StatusCode = new StatusCode();
}
}
and also in the Booking class definition
public class Booking()
{
public Booking()
{
this.Item = new Item();
}
}

Conditional view of fields in a view model in razor

I have the following view model to query my table:
QuestionViewModel.cs
public enum TypeQuestion {
Long = 1,
Short = 2,
Small = 3,
}
public class QuestionViewModel
{
public string Name { get; set; }
public string LastName { get; set; }
public string Address { get; set; }
public string MaxAge { get; set; }
public string Category { get; set; }
public string Account { get; set; }
public TypeQuestion CurrentTypeQuestion { get; set; }
}
if the query I'm doing is of type:
Long: displays all fields.
Short: displays Name, LastName, Address, MaxAge.
Small: displays Name, LastName.
Is there any way to put some kind of DataAnnotation to determine which fields to display in the view or some other way?, To avoid putting a "what if?" for each field.
Thank you.
This may be overkill, and i'd in fact lean towards #Mystere Man's answer, but this is another option.
Instead of regular primitive types in your ViewModel, set them up to cater for the logic. Looks like Name and LastName are always displayed, whilst Address and MaxAge are conditional.
So, setup your ViewModel like this:
public class QuestionViewModel
{
public string Name { get; set; }
public string LastName { get; set; }
public IEnumerable<ConditionalField> ConditionalFields { get; set; }
public string Category { get; set; }
public string Account { get; set; }
}
public class ConditionalField
{
public string Field { get; set; }
public bool Display { get; set; }
}
In your controller, setup the nested viewmodel and the boolean values for Address and MaxAge ccording to the value of CurrentTypeQuestion.
Then, have your View like this:
/Views/Questions.cshtml
#model QuestionViewModel
#Html.DisplayForModel()
Then create a custom display template (or editor template, if this is a form) for QuestionViewModel:
/Views/DisplayTemplates/QuestionViewModel.cshtml
#model QuestionViewModel
#Html.DisplayFor(model => model.Name)
#Html.DisplayFor(model => model.LastName )
#Html.DisplayFor(model => model.Category)
#Html.DisplayFor(model => model.Account)
#Html.DisplayFor(model => model.ConditionalFields)
Then create another custom display template for ConditionalField:
Views/DisplayTemplates/ConditionalField.cshtml
#model ConditionalField
#if (Model.Display) {
#Html.DisplayForModel()
}
As i said, may be overkill, but in the end, you only have a single if statement in the custom template, no loops, and your main view and first-level template stays clean.
To keep it simple, and avoid complex if logic in your view, just create three different views, with only the data you need in each view. Then select the view in your controller based on the question type.
Based from this link and this link
Controller:
public ActionResult Consulta()
{
return View(new QuestionViewModel());
}
ViewModel:
public enum TypeQuestion {
Long = 1,
Short = 2,
Small = 3,
}
public class QuestionViewModel
{
public string Name { get; set; }
public string LastName { get; set; }
public string Address { get; set; }
public int MaxAge { get; set; }
public string Category { get; set; }
public string Account { get; set; }
public TypeQuestion CurrentTypeQuestion { get; set; }
public bool EnabledField(ModelMetadata field)
{
//check pending implementation
return true;
}
}
View:
#model MySite.QuestionViewModel
#using System.Linq;
#using System.Collections;
#{
ViewBag.Title = "Question";
Layout = "~/Views/Shared/Layout.cshtml";
}
<h2>Question</h2>
#using (Html.BeginForm(new { id = "FormQuestion" }))
{
foreach (var prop in ViewData.ModelMetadata.Properties
.Where(pm => pm.ShowForDisplay && !ViewData.TemplateInfo.Visited(pm) && ViewData.Model.EnabledField(pm)))
{
if (prop.HideSurroundingHtml)
{
Html.Editor(prop.PropertyName);
}
else
{
<div class="editor-label">
#(prop.IsRequired ? "*" : "")
#Html.Label(prop.PropertyName)
</div>
<div class="editor-field">
#Html.Editor(prop.PropertyName, prop.Model)
#Html.ValidationMessage(prop.PropertyName, "*")
</div>
}
}
}

Categories

Resources