MVC create using liked table values in dropdown, entity framework - asp.net-mvc

I have a patient model, which has a foreign key to a gender table. I would like to create a 'Create' view which allows user to select the value of male or female from a dropdown.
firstly I was not sure how to get the Gender model into the dropdown. I have managed to get it to work by putting into the viewbag, like so
// GET: Patient
public ActionResult Create()
{
using (var context = new WaysToWellnessDB())
{
// prepopulat roles for the view dropdown
var gender = context.Genders.Select(rr => new SelectListItem { Value = rr.GenderId.ToString(), Text = rr.GenderDesc }).ToList();
ViewBag.Gender = gender;
return View();
}
}
In my view I have the following, which gives me the dropdown I desire.
<div class="form-group">
#Html.LabelFor(model => model.Gender, "Gender", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("Gender", (IEnumerable<SelectListItem>)ViewBag.Gender, "Select ...")
#Html.ValidationMessageFor(model => model.Gender.GenderId, "", new { #class = "text-danger" })
</div>
</div>
However when I do my post to add to the database, the gender column is null.
// POST: /Roles/Create
[HttpPost]
public ActionResult Create(Patient patient)
{
try
{
using (var context = new WaysToWellnessDB())
{
context.Patients.Add(patient);
context.SaveChanges();
return RedirectToAction("Index");
}
}
catch
{
return View();
}
}
Please can someone advise how I get the gender Id to be stored in the database?

You should use #Html.DropDownListFor
#Html.DropDownListFor(model => model.GenderId, (IEnumerable<SelectListItem>)ViewBag.Gender, "Select ...")

Related

DropDown Population Error in ASP.NET MVC 5

I have the following in my controller:
public ActionResult Create()
{
ViewBag.PlayerId = new SelectList(db.Players, "Id", "Name");
return View();
}
This is in the view:
<div class="form-group">
#Html.LabelFor(model => model.PlayerId, "PlayerId", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("PlayerId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.PlayerId, "", new { #class = "text-danger" })
</div>
</div>
But when I submit the form, it gives me the below error:
System.InvalidOperationException: 'The ViewData item that has the key 'PlayerId' is of type 'System.Int32' but must be of type 'IEnumerable'.'
I googled a lot but were not able to find the solution. Your help is highly appreciated.
Write your #Html.DropDownList as follows:
#Html.DropDownList("PlayerId", ViewBag.PlayerId as SelectList,"Select Player", htmlAttributes: new { #class = "form-control" })
Now it will work!
You have to pass the SelectList into dropdown but actually the model binder is confused between PlayerId as viewmodel property and PlayerId as ViewBag property, hence causing the error.
Better to create a viewmodel property which will store option list with different name:
public class ViewModel
{
public int PlayerId { get; set; }
// other properties
// option list here
public List<SelectListItem> PlayerList { get; set; }
}
Then add the option lists from database into controller action:
public ActionResult Create()
{
var model = new ViewModel();
model.PlayerList = db.Players.Select(x => new SelectListItem { Text = x.Name, Value = x.Id }).ToList();
return View(model);
}
And use strongly-typed helper to bind it afterwards:
#Html.DropDownListFor(model => model.PlayerId, Model.PlayerList, "Select", new { #class = "form-control" })
Related issue:
The ViewData item that has the key is of type 'System.Int32' but must be of type 'IEnumerable<SelectListItem>'

Remote Validation DropDownList MVC5

I want to Check whether the master type already exists or not using remote validation.
In my case the remote validation method is not firing.Can anyone help me?
Model
[Key, Column(Order = 1)]
[StringLength(200)]
[Display(Name = "MasterType")]
[Remote("IsNameAvailable", "MasterSetUps", ErrorMessage = "Master type already exists ")]
public string MasterType { get; set; }
Validation Method
[AllowAnonymous]
public JsonResult IsNameAvailable(string MasterType)
{
bool result = true;
if (s_mode == "ADD")
{
return Json(!db.MasterSetUps.Any(a => a.MasterType == MasterType), JsonRequestBehavior.AllowGet);
}
else if (s_mode == "EDIT" & MasterType != s_Master_Type_name)
{
return Json(!db.MasterSetUps.Any(a => a.MasterType == MasterType), JsonRequestBehavior.AllowGet);
}
return Json(result, JsonRequestBehavior.AllowGet);
}
View
<div class="form-group">
#Html.LabelFor(model => model.MasterType, htmlAttributes: new { #class = "control-label col-sm-2" })
<div class="col-sm-10">
#Html.DropDownList("MasterType", null, htmlAttributes: new { #class = "form-controls" })
#Html.ValidationMessageFor(model => model.MasterType, "", new { #class = "text-danger" })
</div>
</div>
GetMethod
public ActionResult Create()
{
s_mode = "ADD";
ViewBag.MasterType = new SelectList(db.Masters, "MasterType", "MasterType");
return View();
}
Your use of DropDownList(...) means that your not generating the necessary data-val-* attributes for validation. The method is using your ViewBag property for binding (not your model property) and their are no validation attributes associated with ViewBag.
Change the code in the GET method to
ViewBag.MasterTypeList = new SelectList (.....
and the view code to
#Html.DropDownListFor (m => m.MasterType, (SelectList)ViewBag.MasterTypeList, new { ... })
Note that the name of the property your binding to cannot be the same as the SelectList.

Mixing model binding with form posting in MVC 5?

Is it possible to enable model binding along with a posted data from a form?
I have a collection property that I want to iterate in a foreach loop to save each selected item in the collection:
<div class="form-group">
#Html.LabelFor(m => m.Users, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#{
List<ApplicationUser> allUsers = ViewBag.AllUsers;
bool assigned;
}
<div>
#foreach (var user in allUsers)
{
//here I want to render all users, and only the users who are in the task, have a checked checkbox
assigned = Model.Users.Select(u => u.Id).Contains(user.Id);
<input type="checkbox" name="asndUsers" value="#user.Id" id="#user.Id" #Html.Raw(assigned ? "checked" : "") /> <label style="font-weight: normal;" for="#user.Id">#user.UserName</label><br />
}
</div>
</div>
</div>
//fields updated with model binding:
<div class="form-group">
#Html.LabelFor(m => m.Status, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(m => m.Status, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(m => m.Status)
</div>
</div>
this is the Edit action post method:
[HttpPost, ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "Id,Title,Description,DueDate,Status")] UserTask task, string[] asndUsers)
{
if (ModelState.IsValid)
{
task.Users = new List<ApplicationUser>();
foreach (var item in asndUsers)
{
var user = context.Users.Find(item);
task.Users.Add(user);
}
context.Entry(task).State = EntityState.Modified;
context.SaveChanges();
return RedirectToAction("Index");
}
return View(task);
}
it works when I debug, I see the new posted data has merged with the bound data.
but when the request redirected to the Index view, there is no change after editing an item, this is the Index action method:
public ActionResult Index(int? taskId)
{
var viewModel = new TasksUsers();
viewModel.Tasks = context.Tasks.Include(x => x.Users);
if (taskId != null)
{
viewModel.Users = viewModel.Tasks.Where(t => t.Id == taskId).Single().Users;
ViewBag.Row = taskId;
}
return View(viewModel);
}
Original answer
The proper way of updating the related entities is loading them first,
so I used eager loading to redefine the incoming task parameter as
follows:
task = context.Tasks.Include(t => t.Users).Single(s => s.Id == task.Id); Note that `Find` can't be used with `Include` so I used
Single.
That resolved the problem of updating the Users entity
this was wrong,
the proper way is to use explicit binding instead of implicit binding (TryUpdateModel())
The task that is posted back is no longer tracked by the DbContext. Try to Attach the task to the DbSet in the Edit action:
context.Tasks.Attach(task);
if (task.Users == null) {
task.Users = new List<ApplicationUser>();
}
foreach (var item in asndUsers) {
var user = context.Users.Find(item);
task.Users.Add(user);
}
// may be important because Attach() sets the State to 'Unchanged'?
context.Entry(task).State = EntityState.Modified;
context.SaveChanges();
As a side note, you can pass parameters when you call RedirectToAction. (Only do this if you want to pass the id of the edited task to the Index action):
return RedirectToAction("Index", new { taskId = existingTask.Id });
// ^^^^^^
// must match parameter name of Index action

CSHTML, Changing a value with EditorFor, do not want old value displayed?

I'm sorry if this question is unclear or I'm not giving enough information, I'm new to CSHTML, and cannot tell if I'm missing something incredibly obvious.
I currently have an EditorFor() in my view that changes the password of a specific userCard field in a database. When this password is being changed, I would like the textbox to be empty, but every time it displays the old value. Deleting the value prior to edit is not an option, for my page has too many options for the user to go to a different page and leave the account passwordless. Any help?
The offending EditorFor is below:
<div class="form-group">
<div class="col-md-10">
<h5>Enter a New Passcode</h5>
</div>
<div class="col-md-10">
#Html.EditorFor(model => model.userCard.password, new { htmlAttributes = new { #class = "form-control"} })
#Html.ValidationMessageFor(model => model.userCard.password, "", new { #class = "text-danger" })
</div>
</div>
The controller methods for this viewpage are below:
public ActionResult EditPasscode(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
UserCardOBJ userCardOBJ = new UserCardOBJ();
userCardOBJ.userCard = db.UserCards.Find(id);
if (userCardOBJ.userCard == null)
{
return HttpNotFound();
}
return View(userCardOBJ);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult EditPasscode([Bind(Include = "ID,password")] UserCard userCard)
{
if (ModelState.IsValid)
{
//db.Entry(userCard).State = EntityState.Modified;
db.UserCards.Attach(userCard);
db.Entry(userCard).Property(model => model.password).IsModified = true;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(userCard);
}
You could add a value property to the htmlAttribute anonymous type passed in.
#Html.EditorFor(model => model.userCard.password, new { #class = "form-control", #value = "" } })

how to insert fixed value into table in MVC

I want to insert a fixed date into table. How can I do this ?
<div class="editor-label">
Description :
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.Description, new { #class = "textboxes" })
#Html.ValidationMessageFor(model => model.Description)
</div>
<div class="editor-label">
Date :
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.Date, new { #class = "textboxes" }) /// I wanna be here a fixed date .
#Html.ValidationMessageFor(model => model.Date)
</div>
My controller code is:
public ActionResult Index(tblOrder tblorder)
{
if (ModelState.IsValid)
{
db.tblOrders.AddObject(tblorder);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.fxBudjet = new SelectList(db.tblBudjets, "ID", "Budjet", tblorder.fxBudjet);
ViewBag.fxServiceType = new SelectList(db.tblServiceTypes, "ID", "Service", tblorder.fxServiceType);
ViewBag.fxStartTime = new SelectList(db.tblStartDates, "ID", "StartDate", tblorder.fxStartTime);
return View(tblorder);
}
Do I need to change my controller code?
#Html.DisplayTextFor(model => model.Date, new { #class = "textboxes" })
if the date is needed in a post you can do
#Html.DisplayTextFor(model => model.Date, new { #class = "textboxes" })
#Html.HiddenFor(model => model.Date)
or in the post method you can use TryUpdateModel and exclude the date field
Edit from your update i would set the value for the model in the controller e.g. say you had a class year that you wanted to prepopulate the year value
public ActionResult Create()
{
return View(new Year() { Value = DateTime.Now.Year });
}
again if you want it for the post you can use a hiddenfor or else regenerate it in the post
if this field is only required in the post method a better option might be to create a viewmodel without that field and then have some mapping logic in the post method
Edit 2: As with MVC music store you should really have 2 methods a get and a post. I will assume it is for a create.
[HttpGet]
public ActionResult Create()
{
ViewBag.fxBudjet = new SelectList(db.tblBudjets, "ID", "Budjet");
ViewBag.fxServiceType = new SelectList(db.tblServiceTypes, "ID", "Service");
ViewBag.fxStartTime = new SelectList(db.tblStartDates, "ID", "StartDate");
return View(new tblOrder() { Date = DateTime.Now });
}
[HttpPost]
public ActionResult Create(tblOrder tblorder)
{
if (ModelState.IsValid)
{
db.tblOrders.AddObject(tblorder);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.fxBudjet = new SelectList(db.tblBudjets, "ID", "Budjet", tblorder.fxBudjet);
ViewBag.fxServiceType = new SelectList(db.tblServiceTypes, "ID", "Service", tblorder.fxServiceType);
ViewBag.fxStartTime = new SelectList(db.tblStartDates, "ID", "StartDate", tblorder.fxStartTime);
return View(tblorder);
}
then in your view something like
#model tblOrder
#Html.BeginForm()
{
#Html.DisplayTextFor(model => model.Date, new { #class = "textboxes" })
#Html.HiddenFor(model => model.Date)
...other form stuff
<input type="submit" value="Delete" />
}
Hopefully this should give you some idea of how to fix your code

Resources