MVC 4 drop down box error - c#

I have had this error for over a day now and I really can't seem to fix it. I know there are a lot of questions on this topic online which I have read over and over and still haven't solved the issue.
I'm just learning MVC 4 so I'm extremely confused.
I get the error message:
The ViewData item that has the key 'cabinCrewId' is of type 'System.Int32' but must be of type 'IEnumerable'.
Any help or direction would be greatly appreciated!
My Controller:
public ActionResult AddCrew()
{
FlightCabinCrew fcc = new FlightCabinCrew();
return View(fcc);
}
Post action:
[HttpPost]
public ActionResult AddCrew(FlightCabinCrew fcc)
{
if (ModelState.IsValid)
{
using (A1Context db = new A1Context())
{
var data = from person in db.person
from flightcrew in db.flightcabincrew
from cabincrew in db.cabincrew
where flightcrew.cabinCrewId == cabincrew.person
where cabincrew.person == person.id
select person.name;
ViewBag.list = new SelectList(data.ToList(), "id", "name");
db.flightcabincrew.Add(fcc);
db.SaveChanges();
return RedirectToAction("Index");
}
}
else
{
using (A1Context db = new A1Context())
{
var data = from person in db.person
from flightcrew in db.flightcabincrew
from cabincrew in db.cabincrew
where flightcrew.cabinCrewId == cabincrew.person
where cabincrew.person == person.id
select person.name;
ViewBag.list = new SelectList(data.ToList(), "name", "name");
return View(fcc);
}
}
}
}
And my view:
<div class="editor-label">
#Html.LabelFor(model => model.cabinCrewId)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.cabinCrewId, (SelectList)ViewBag.list)
#Html.ValidationMessageFor(model => model.cabinCrewId)
</div>
Thanks

You need to assign the SelectList to ViewBag in the GET AddCrew method (as you have done in the POST method). Note also you do not need to assign the SelectList in the POST method if ModelState is valid (your saving and then redirecting so its not necessary since your not returning the view)

Related

How to get Id by interaction of element which is part of List<Element> in .NET Framework

I have a list of elements which are displayed on a page by a for-loop. I want to get data of the selected element into an asp.net view. How can I realize that?
This is the action of the controller which returns a List of elements:
public ActionResult MainContent()
{
SiteContext db = new SiteContext();
db.SaveChanges();
var model = db.Posts.ToList();
return PartialView("MainContent", model);
}
Partial view of MainContent:
#model IEnumerable<LayersDAL.Entity.Post>
#for (int i = 0; i < Model.Count(); i++)
{
<ul>
<li>
//Here is I'm trying to send element Id of current loop iteration to CurrentPost but something goes wrong :(
<a class="post-ref" href="#Url.Action("CurrentPost", "Notes", new { Model.ToList()[i].PostId })">
<h2>#Html.DisplayFor(modelItem => Model.ToList()[i].Title)</h2>
<p>
</p>
</a>
</li>
</ul>
}
CurrentPost action:
public ActionResult CurrentPost(int? id)
{
SiteContext db = new SiteContext();
var post = db.Posts.FirstOrDefault(p => p.PostId == id);
return PartialView(post);
}
Partial view of CurrentPost:
<div class="modal-post">
<div class="modal-post-divs">
<span class="close">×</span>
</div>
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div>
<label>#Model.Title</label>
</div>
</div>
List of elements in Browser
Error picture
So, what can I do? :)
UPD:
Thanks guys, I resolved that problem :)
Change your link code to the following:
<a class="post-ref" href="#Url.Action("CurrentPost", "Notes", new { id = Model[i].PostId })">
<h2>#Html.DisplayFor(modelItem => Model[i].Title)</h2>
<p>
</p>
</a>
You've already converted it to a List in your controller, so you don't need to call ToList() on the model in the View (and especially not inside a for loop!). Also make sure that you name the parameter that you're passing to the CurrentPost() method.
In the CurrentPost() method itself you should double check that the id parameter is valid and not null as follows:
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Post post = db.Posts.Find(id);
if (post == null)
{
return HttpNotFound();
}
FirstOrDefault() returns null if no item found so your model will be null.
public ActionResult CurrentPost(int? id)
{
SiteContext db = new SiteContext();
var post = db.Posts.FirstOrDefault(p => p.PostId == id);
return PartialView(post);
}
you can check it on your view to avoid erros.
#if(Model != null)
{
// your code.
}
else
{
<p>No item found </p>
}

Getting the selected value from a DropDownListFor using a ViewBag list

I'm having trouble getting the data from a DropDownListFor using a ViewBag list with my model. Here is my Controller code:
[HttpGet]
public ActionResult JoinTeam()
{
var TeamList = _db.TeamModels.ToList();
SelectList list = new SelectList(TeamList, "Id", "TeamName");
ViewBag.TeamList = list;
return View();
}
And the Razor view form looks like this:
#using (Html.BeginForm("JoinTeam", "Home", FormMethod.Post))
{
#Html.TextBoxFor(m => m.DisplayName, new { #class = "form-control form-control-lg", placeholder = "Enter your Battle Net ID" })
<br/>
#Html.DropDownListFor(m => m.TeamModel, (SelectList)ViewBag.TeamList, "- Select a Team to Join -", new { #class= "form-control form-control-lg" })
<br />
<button type="submit" class="btn btn-primary" style="width:100%;text-align:center;">Submit</button>
}
The TextBoxFor helper is returning the data correctly, but whatever option I have selected in the drop down does not get passed into my post method. Does anyone have any ideas?
The post action does work as it's getting the data from the model for the TextBoxFor help, but here's what it looks like:
[HttpPost]
public async Task<ActionResult> JoinTeam(GuardianModel model)
{
try
{
string BNETId = model.DisplayName.Replace("#", "%23");
long memberId = 0;
if (ModelState.IsValid)
{
Bungie.Responses.SearchPlayersResponse member = await service.SearchPlayers(MembershipType.Blizzard, BNETId);
memberId = member[0].MembershipId;
}
using (var context = new CoCodbEntities1())
{
var g = new GuardianModel
{
MembershipId = memberId.ToString(),
DisplayName = BNETId,
MembershipType = 4,
TeamID = model.TeamModel.Id
};
TempData["UserMessage"] = ViewBag.TeamList.Id;
return RedirectToAction("Success");
}
}
catch
{
}
return View();
}
These are the values getting passed into the Post action
From the screenshot you shared, it looks like TeamModel property is the virtual navigational property of type TeamModel. You should not bother about loading that. All you need to worry about loading the forign key property value (usually a simple type like an int or so.
Your SELECT element name should be TeamID. When the form is submitted, it will map the selected option value to the TeamID property value of your model which is the foreign key property.
#Html.DropDownListFor(m => m.TeamID, (SelectList)ViewBag.TeamList,
"- Select a Team to Join -", new { #class= "form-control form-control-lg" })
While this might fix the issue, It is a good idea to use a view model instead of using your entity class.
I found the issues I was having. All I needed to get passed into the post action was the Id of the TeamModel. So I changed this line:
#Html.DropDownListFor(m => m.TeamModel.Id, (SelectList)ViewBag.TeamList, "- Select a Team to Join -", new { #class= "form-control form-control-lg" })
I just added the Id and it seemed to work.

How do I resolve ModelState.IsValid = false while binding a List type?

I'm getting an ModelState.IsValid = false from a List that contains a class object that has its own id's.
I've seen some examples of how to exclude class properties from the [HttpPost] method while binding that look like this:
[Bind(Exclude="Id,SomeOtherProperty")]
My Question:
How do you exclude the Id that belongs to a property as it does with List? Or, if there's a better way of handling this, please shed some light on the subject.
Here's my PostController.cs:
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateInput(false)]
public ActionResult Create([Bind(Include = "Title,URL,IntroText,Body,Created,Modified,Author,Tags")] Post post)
{
if (ModelState.IsValid) /*ModelState.IsValid except for its not... */
{
// this is failing so I unwrapped the code below temporarily
}
using (UnitOfWork uwork = new UnitOfWork())
{
var newPost = new Post
{
Title = post.Title,
URL = post.URL,
IntroText = post.IntroText,
Body = replace,
Author = post.Author,
Tags = post.Tags
};
uwork.PostRepository.Insert(newPost);
uwork.Commit();
return RedirectToAction("Index", "Dashboard");
}
return RedirectToAction("Index", "Dashboard");
}
Update: Relevant excerpt from my Create.cshtml (This turned out to be the problem.)
<div class="form-group">
#Html.LabelFor(model => model.Tags, htmlAttributes: new { #class = "control-label col-md-2 col-md-offet-3" })
<div class="col-md-7">
#for (var i = 0; i < 4; i++)
{
#Html.HiddenFor(m => m.Tags[i].Id)
#Html.EditorFor(model => model.Tags[i].Name)
}
</div>
</div>
Gists: Post.cs | Tag.cs
I wanted to include this picture so you could see, visually, what was failing. Each Tag[i].Id tag is causing the invalid state.
To restate my question, how do I omit the List<Tag> Id from my POST method and achieve valid state?
As #StephenMuecke pointed out in the comments of the OP. I just needed to remove the field from the View:
#Html.HiddenFor(m => m.Tags[i].Id)
Now the ModelState.IsValid returns true.

How to properly use SelectListItem for HTML.DropDownList instead of SelectList?

I've been digging through other posts trying to figure out how to go from using a SelectList in my controller to fill an #Html.DropDownList in my View to instead use what seems to be the commonly suggested SelectListItem, but I'm completely lost?
I have a main INV_Assets model that when I go to the Edit View, I include Drop Down Lists for other Model Properties (Location, Manufacturer, Model, Status, Type, Vendor, etc.) My current code below adequately fills the lists and allows me on Edit() to change the chosen entity value to any other value stored in that relevant table.
CURRENT CODE:
Controller:
// GET: INV_Assets/Edit/5
public async Task<ActionResult> Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
INV_Assets iNV_Assets = await db.INV_Assets.FindAsync(id);
if (iNV_Assets == null)
{
return HttpNotFound();
}
ViewBag.Location_Id = new SelectList(db.INV_Locations, "Id", "location_dept", iNV_Assets.Location_Id);
ViewBag.Manufacturer_Id = new SelectList(db.INV_Manufacturers, "Id", "manufacturer_description", iNV_Assets.Manufacturer_Id);
ViewBag.Model_Id = new SelectList(db.INV_Models, "Id", "model_description", iNV_Assets.Model_Id);
ViewBag.Status_Id = new SelectList(db.INV_Statuses, "Id", "status_description", iNV_Assets.Status_Id);
ViewBag.Type_Id = new SelectList(db.INV_Types, "Id", "type_description", iNV_Assets.Type_Id);
ViewBag.Vendor_Id = new SelectList(db.INV_Vendors, "Id", "vendor_name", iNV_Assets.Vendor_Id);
return View(iNV_Assets);
}
// POST: INV_Assets/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include = "Id,Model_Id,Manufacturer_Id,Type_Id,Location_Id,Vendor_Id,Status_Id,ip_address,mac_address,note,owner,cost,po_number,description,invoice_number,serial_number,asset_tag_number,acquired_date,disposed_date,created_date,created_by,modified_date,modified_by")] INV_Assets iNV_Assets)
{
if (ModelState.IsValid)
{
db.Entry(iNV_Assets).State = EntityState.Modified;
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
ViewBag.Location_Id = new SelectList(db.INV_Locations, "Id", "location_dept", iNV_Assets.Location_Id);
ViewBag.Manufacturer_Id = new SelectList(db.INV_Manufacturers, "Id", "manufacturer_description", iNV_Assets.Manufacturer_Id);
ViewBag.Model_Id = new SelectList(db.INV_Models, "Id", "model_description", iNV_Assets.Model_Id);
ViewBag.Status_Id = new SelectList(db.INV_Statuses, "Id", "status_description", iNV_Assets.Status_Id);
ViewBag.Type_Id = new SelectList(db.INV_Types, "Id", "type_description", iNV_Assets.Type_Id);
ViewBag.Vendor_Id = new SelectList(db.INV_Vendors, "Id", "vendor_name", iNV_Assets.Vendor_Id);
return View(iNV_Assets);
}
View - Just [Locations] for example:
<div class="form-group">
#*#Html.LabelFor(model => model.Location_Id, "Location_Id", htmlAttributes: new { #class = "control-label col-md-2" })*#
<span class="control-label col-md-2">Location:</span>
<div class="col-md-10">
#Html.DropDownList("Location_Id", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.Location_Id, "", new { #class = "text-danger" })
</div>
</div>
What I'm trying to do now is add a value to each list stating "Add New", which I want to allow users to click on and have a (partial view?) popup for them to immediately add a new relevant record (Ex. New [Location] of "Warehouse 2") and then be able to choose that from the [Locations] list for the particular Asset being Edited.
Can anyone walk me through this?
A lot of the suggestions are to add a SelectList or IEnumerable<SelectListItem> to my relevant Model properties, but from there I am lost on what to tweak in my controller/view? Currently I am using Code-First Migrations with an InventoryTrackerContext.cs inside my DAL folder for the project.
You're confusing two very different aspects of this. First, Html.DropDownList only requires an IEnumerable<SelectListItem>. Passing a full SelectList object satisfies this parameter merely because a SelectList is an IEnumerable<SelectListItem>. The advice to not use SelectList is simply to save yourself the work of having to construct a full SelectList object (and remembering to do things like set the selectedValue to the right item), when Razor will handle this for you. Whether you use SelectList or IEnumerable<SelectListItem> has no bearing on the remainder of your question.
As far as adding items to an existing drop down list goes, you have to use JavaScript for that. At a basic level, it's as simple as just just selecting the select element in the DOM and appending a new option node.

c# MVC5 RedirectToAction not working - "Sequence contains no matching element"

I have a controller action that does the following:
Takes a list of Product models from a form.
Cycles through and adds selected products (IsSelected == true) to an Account model.
Saves the changes and Redirects to another action, passing the AccountID.
ID is received correctly, finds the correct account and passes it to the View.
The view then parses the account and shows the list of products that were added to the account above.
However, MVC keeps throwing an error "Sequence contains no matching element" and I'm struggling to figure out why.
Account Controller that takes a list of Products and adds it to the user's account:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> AddProduct(List<Product> products)
{
//var acc = FetchCurrentUserAccount();
if (db.Accounts != null)
{
var acc = db.Accounts.Find(UserManager.FindByIdAsync(User.Identity.GetUserId()).Result.Account.AccountID);
foreach (var product in products)
{
if (product.IsSelected)
{
product.PaymentStatus = enPaymentStatus.Pending
acc.ProductsPurchased.Add(product);
}
}
await db.SaveChangesAsync();
return RedirectToAction("Pay", "Products", new {id = acc.AccountID} );
}
return HttpNotFound();
}
Product Controller that takes an AccountID, finds the account and passes it to the View as a model:
[HttpGet]
public ActionResult Pay(int? id)
{
var acc = db.Accounts.Find(id);
return View(acc);
}
The View itself:
#using PPPv2.Models
#model Account
#{ var subtotal = 0.0d;}
<h2>Payment:</h2>
#if (Model != null)
{
<h4>Thank you #Model.Salutation #Model.LastName</h4>
<h4>Please check that the product(s) and amounts below are correct before completing your purchase.</h4>
foreach (var item in Model.ProductsPurchased)
{
<div>
#Html.DisplayFor(modelItem => item.Name)
<div>
#Html.DisplayFor(modelItem => item.Price)
#{ subtotal += item.Price; }
</div>
</div>
}
<div>
Total to pay:
<div>
#Html.Display(String.Format("£{0:#,###0.00}", subtotal))
</div>
</div>
#Html.ActionLink("Pay", "Process", "Products", new {id = Model.ProductsPurchased.First(x => x.PaymentStatus == enPaymentStatus.Sold)}, new {#class = "btn btn-warning"})
}
else
{
<div>
No products were found in your cart... Odd...
<div>
#Html.ActionLink("Select a product", "AddProduct", "Account", null, new {#class = "btn btn-warning"})
</div>
</div>
}
Points to note:
During the foreach(var product in Products) loop in the Account Controller; product is populated correctly. Yet when added to the account, the Count of the list increases correctly and the PaymentStatus is correct, but the remaining fields are null.
Creating a new Product() and pushing each individual property from product into it gives the same result.
Any ideas?
on this line:
#Html.ActionLink("Pay", "Process", "Products",
new {id = Model.ProductsPurchased.First(x => x.PaymentStatus == enPaymentStatus.Sold)},
new {#class = "btn btn-warning"})
You'll need to first add a condition checking whether there are any items in the ProductsPurchased collection that have a status of Sold. You may also want to make sure you wish to display a product with status of Sold, or rather as your controller code suggests, one with a status of Pending.
So, basically
if(Model.ProductsPurchased.Any(x => x.PaymentStatus == enPaymentStatus.Sold/*or Pending?*/)){
#Html.ActionLink("Pay", "Process", "Products",
new {id = Model.ProductsPurchased.First(x => x.PaymentStatus == enPaymentStatus.Sold/*or Pending?*/)},
new {#class = "btn btn-warning"})
}
I'm not certain of the fix, but I believe it is that you are not waiting for the acc to return back, well more accurately for this to return back.
UserManager.FindByIdAsync(User.Identity.GetUserId()).Result.Account.AccountID
Because you are using async, I would have expected you to use await before trying to use the result.
Have you tried mak

Categories

Resources