ASP.net MVC 3 - DropDownList not updating during edit? - c#

forgive me as I am fairly new to using ASP.net MVC 3...
I have two tables - one called Contract, one called Sow. Contract has a foreign key that points to SOW. What I'd like to do is to be able to edit the Contract details and provide a drop down list of different SOW records to choose from. The current code:
In my Contract Controller:
public ActionResult Edit(int id)
{
Contract contract = contractRepository.GetContract(id);
var db = new ManagementDataContext();
IEnumerable<SelectListItem> items = db.Sows
.Select(s => new SelectListItem()
{
Value = s.ID.ToString(),
Text = s.Title
});
ViewData["Sow"] = items;
return View(contract);
}
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
Contract contract = contractRepository.GetContract(id);
try
{
UpdateModel(contract);
contractRepository.Save();
return RedirectToAction("Details", new {id = contract.contractID});
}
catch
{
ModelState.AddRuleViolations(contract.GetRuleViolations());
var db = new ManagementDataContext();
IEnumerable<SelectListItem> items = db.Sows
.Select(s => new SelectListItem()
{
Value = s.ID.ToString(),
Text = s.Title
});
ViewData["Sow"] = items;
return View();
}
}
In my Edit.aspx:
<%: Html.DropDownList("Sow") %>
The list populates with values, but when I change them and update, the foreign key does not change. Any advice or help? Also, if you want to throw in a way I could improve my current code?
Again, I apologize for my lack of ASP.net knowledge, but you gotta get your hands dirty and make mistakes in order to learn.

This is going to generate a select with name="Sow".
What you want is the input to have the same name as the FK property you want to bind it too.
Probably something like <%: Html.DropDownList("SowId") %>

Firstly, I highly recommend you to use ViewModel pattern with AutoMapper.
Whatever, in you scenario, should work if you do something like
public ActionResult Edit(int id)
{
Contract contract = contractRepository.GetContract(id);
var db = new ManagementDataContext();
IEnumerable<SelectListItem> items = db.Sows
.Select(s => new SelectListItem()
{
Value = s.ID.ToString(),
Text = s.Title
});
ViewData["SowItems"] = items;
return View(contract);
}
Your view:
<%: Html.DropDownList("SowId", ViewData["SowItems"]) %>
the possible problem can be with ModelState and I can't right now give to you a clear explanation.
hope it help you

A few approaches for you to try mixed with some code help.
1st: change your [HttpPost] Edit method to accept a Contract parameter instead of FormCollection, its much cleaner
[HttpPost]
public ActionResult Edit(Contract contract)
As long as the fields of Contract are the names of your input fields, then you are set as the MVC toolkit will map them to a Contract object that is then passed to your Action method.
This will also allow you to take advantage of Unobtrusive Validation, meaning MVC framework will validate the data inputs to your model object before it gets to your Action method. You will still have to perform Business Rule validations or Data Model Relational validations, but it helps.
2nd: I prefer to create SelectLists in the View (probably get killed over this), but I think SelectList is definitely a View abstraction of your data which has nothing to do with Control or Model, here's a way to do it, and here's where a ViewModel comes in handy
[Side note here for ViewModel, it helps get away from using Strings to pull things out as Objects from ViewData, which in some cases (like SelectLists) need to then be cast in order to compile]
Controller code
var model = new ContractViewModel()
{
Contract = contractRepository.GetContract(id),
Sows = db.Sows.ToList() // ToList() is important here to ensure the data is pulled into the Model
}
// Do any other initializations here
ViewData.Model = model;
return View();
View code
<%= Html.DropDownListFor(model => model.Contract.Sow, new SelectList(Model.Sows, "ID", "Title")) %>
An alternative, if either this won't work for you or you need different validations is:
Controller code
[HttpPost]
public ActionResult Edit(Contract contract, int sowID)
View code
<%= Html.DropDownList("sowID", new SelectList(Model.Sows, "ID", "Title")) %>
Hope this helps.

Related

Can't get DropDownList working in .NET (C#)

I'm still pretty new to .NET, but I think I've read everything there is to read on this subject (including similar questions on SO, which is where I got some of the things I've tried). I feel like I've tried everything possible and I still can't get it to work.
I have a Note class and a Category class. Pretty straightforward, each note has a Category property, so I want to have a dropdown list in my Create view that displays categories. I can get a list to display the category names correctly, but that's it. It keeps telling me there's no IEnumerable in my ViewData called "Categories" when there definitely, 1000% for sure is...
The Create action in my NoteController looks like this:
// GET: Create
public ActionResult Create()
{
SelectList items = (new CategoryService()).GetCategories().Select(c => new SelectListItem
{
Value = c.CategoryId.ToString(),
Text = c.Name
}) as SelectList;
ViewData["Categories"] = items;
return View();
}
And I've tried a few variations in the view:
#Html.DropDownListFor(e=>e.CategoryId , (IEnumerable<SelectListItem>) ViewData["Categories"])
#Html.DropDownList("Categories", "Select a Category")
My Create view uses a NoteCreate model, which has this:
public class NoteCreate {
...
[Display(Name = "Category")]
[Required]
public string CategoryId { get; set; }
And my NoteService has a CreateNote method like so:
public bool CreateNote(NoteCreate model)
{
using (var ctx = new ApplicationDbContext())
{
bool isValid = int.TryParse(model.CategoryId, out int id);
if (!isValid)
{
id = 0;
}
var entity =
new Note()
{
OwnerId = _userId,
Title = model.Title,
Content = model.Content,
CreatedUtc = DateTimeOffset.Now,
Status = model.Status,
CategoryId = id
};
ctx.Notes.Add(entity);
return ctx.SaveChanges() == 1;
}
}
I figured I have to turn the ID into a string for the sake of the dropdown list (because SelectListItem's Value and Text are strings), which is why I parse it back into an int here
I tried attaching the list to the ViewBag instead, and I've tried variations of both DropDownListFor and DropDownList
One of those combinations resulted in a dropdown list actually showing, and I don't remember what it was, but selecting an item resulted in a null being passed to the NoteCreate method (model.CategoryId)
Can anyone help me, and potentially many others who will struggle with this in the future because the documentation is so terrible?
UPDATE:
My controller has been refactored to:
// GET: Create
public ActionResult Create()
{
List<SelectListItem> li = new List<SelectListItem>();
List<Category> Categories = (new CategoryService()).GetCategories().ToList();
var query = from c in Categories
select new SelectListItem()
{
Value = c.CategoryId.ToString(),
Text = c.Name
};
li = query.ToList();
ViewBag.Categories = li;
return View();
}
and my view has been refactored to:
#Html.DropDownList("Categories", ViewBag.Categories as SelectList, new { #class = "form-control" })
This is closer, as I can now load the view and see the Category names in the dropdown. However, when I save, model.CategoryId in my CreateNote method is null, so the CategoryId value isn't actually being passed from the dropdown into the model.
If ViewModel is used in the view then its better to paa the data through model properties to the view. No need to put the collection for Dropdownlist in ViewData or ViewBag.
For the detail way of using Dropdownlist through SelectList and pass to the view through, I would refer an answer I had posted:
MVC C# Dropdown list Showing System.Web.SelectListItem on the model and can not blind to controller
The model passed to your view needs a property for CategoryId.
Your Html Helper is looking for CategoryId here:
#Html.DropDownListFor(e=>e.CategoryId
Ok... I figured it out.
It's so stupid.
The key you use to store the SelectList in your ViewData HAS to be the same as the name of the property on the model, even though you can explicitly tell it to use the list using a different key....
So even if you wanted to use the same SelectList for a few different properties (but process them differently in your service, say), you'd have to pass it to the ViewData redundantly for each property
So instead of passing my SelectList through as ViewBag.Categories, I passed it in as ViewBag.CategoryId, and that worked.
I'm going to go drink a lot of alcohol now.
In Controller
List<SelectListItem> li = new List<SelectListItem>();
var query = from of in your_context.Categories
select new SelectListItem()
{
Value = of.CategoryId.ToString(),
Text = of.Name
};
li = query.ToList();
ViewBag.Category_ = li;
View
<div class="col-md-10">
#Html.DropDownList("Categories", ViewBag.Category_ as List<SelectListItem>, new { #class = "form-control" })
</div>

ASP Entity Framework Show data from another model

I have a table called TestAnswer and one called question
This is the Create Script created by Entity Framework. I have added some viewbags
ViewBag.TestAnswerQuestion gives me values from the Question table as a select list. I want to show them as normal text e.g. #Html.DisplayFor(model => model.QuestionText) in my razor view for TestAnswer
How can i get the questions to show in my TestAnswer create view
// GET: /TestAnswer/Create
public ActionResult Create()
{
ViewBag.CurrentTestUser = User.Identity.Name;
ViewBag.CurrentTestUserId = User.Identity.GetUserId();
**ViewBag.TestAnswerQuestion = new SelectList(db.Questions.Where(t => t.QuestionID == 2), "QuestionID", "QuestionText");**
ViewBag.TestAnswerTestID = new SelectList(db.Tests, "TestID", "TestUser");
return View();
}
Looks like you need to pass a Model back to your View when returning from your Controller action method.
If I understand you correctly, you want to return a Question entity to your TestAnswer/Create view, perhaps something like:
var model = db.Questions.Find(t.QuestionID);
...
return View(model);
Then you can reference the model from the View template:
#Html.DisplayFor(model => model.QuestionText)
If you want to show a select list of Answers to the Question, your Question entity could have a collection of Answer entities as a public property, then you can simply navigate from a Question to its Answers.
The best advise I can give you is to consider the Entities and their relationships carefully, and model them as POCO classes. Depending on what you are trying to achieve, you may want tot return a View Model, when is a transformation of you entity model to another form that is optimized for the View.

Select List returns System.Data.Entity.DynamixProxies instead of values in MVC asp.net C#

I have got two different tables. User and ProjectDetails. There are two different controllers as well to do CRUD operations on these tables. Now, I have a case where, in the User CREATE operation, I have to select the Project from the List of Projects in ProjectDetails. I tried the following:
In the user model, I created this line:
public IEnumerable<ProjectDetail> ProjectDetail { get; set; }
And in the controller, in the create Action, I have added the following code:
public ActionResult Create()
{
var model = new UserDetail
{
ProjectDetail = db1.ProjectDetails
};
return View(model);
}
And in the create view, I am trying to get the list of Project IDs as follows:
#Html.DropDownListFor( x => x.ProjectDetail, new SelectList(Model.ProjectDetail, "Project ID"))
However, wen i run, i get the number of lines (as equal to the number of projects) as
System.Data.Entity.DynamicProxies.ProjectDetails_F########### (Some numbers)..
Please can someone help me?
Regards,
Hari
[EDIT] - I checked in the debug mode and found the following.. Tried attaching the image..
I drilled down that Proxy things and found ProjectID there. How can I get that?
You are using a wrong overload, use this instead:
#Html.DropDownListFor( x => x.ProjectDetail,
new SelectList(Model.ProjectDetail, "ProjectId","ProjectName"))
// where ProjectId is the unique identifier field of `ProjectDetail`
// and `ProjectName` is the text you want to show in the dropdown
In your code you are not telling the html helper what properties to use for the datavalue and the datatext. The overload you use is the one where you tell the htmlhelper which value is selected.
You can do something like
var projection = db1.ProjectDetails.Select(t => new ProjectDetailsViewModel
{
Prop1 = t.Prop1,
Prop2 = t.Prop2
});
Can you try
public ActionResult Create()
{
var model = new UserDetail
{
ProjectDetail = db1.ProjectDetails.ToList()
};
return View(model);
}

How to make the DropDownList selected value persist after a POST action in ASP.NET MVC 3?

I'm trying to use this DropDownList stuff for the first time, and so far, I didn't achieve my goal. My goal is to have a DDList, and after the desired value is selected, we use the onchange event to do the post action, but after the POST action is done, the DDList resets to the first value, making impossible to select the first item.
Guys, I know there are better ways around it (like not using this onchange, or instead of a DDList using a ul or a table, but I'm trying to understand POST actions and DDList, in case I have to use this again.
Can you gentle guys help me out?
when loading data to the DD, you must validate first
if (!isPostBack)
fillDD();
I'm in MVC4. In case anybody else has trouble understanding here is my solution to the problem.
//Razor View
#Html.DropDownList("StateID", (SelectList)ViewBag.stateList, "--Select--")
//Code
public ActionResult Create()
{
MyOtherData d = new MyOtherData();
//Load the Dropdown here
ViewBag.stateList = new SelectList(db.State, "StateID", "Name");
return View(d);
}
[HttpPost]
//Notice the 2nd parameter StateID in Action Result must match the 4th parameter in the ViewBag SelectList Below and the first parameter in the DropDownList called StateID.
//The 4th parameter will act like the SelectedItem we were all familiar with in ASP.Net
public ActionResult Create(ModelData data, String StateID)
{
if(ModelState.IsValid)
{
//...
}
//Return SelectList to the dropdownlist here
ViewBag.stateList = new SelectList(db.State, "StateID", "Name", StateID);
return View(data);
}

Beginner MVC question - Correct approach to render out a List and details?

I'm trying to set up a page where I display a list of items and the details of the selected item. I have it working but wonder whether I have followed the correct approach. I'll use customers as an example
I have set the aspx page to inherit from an IEnumerable of Customers. This seems to be the standard approach to display the list of items. For the Details I have added a Customer user control which inherits from customer.
I think i'm on the right track so far but I was a bit confused as to where I should store the id of the customer whose details I intend to display. I wanted to make the id optional in the controller action so that the page could be hit using "/customers" or "customers/1" so I made the arg optional and stored the id in the ViewData like this:
public ActionResult Customers(string id = "0")
{
Models.DBContext db = new Models.DBContext();
var cList = db.Customers.OrderByDescending(c => c.CustomerNumber);
if (id == "0")
{
ViewData["CustomerNumber"] = cList.First().CustomerNumber.ToString();
}
else
{
ViewData["CustomerNumber"] = id;
}
return View("Customers", cList);
}
I then rendered the User control using RenderPartial in the front end:
<%var CustomerList = from x in Model
where x.CustomerNumber == Convert.ToInt32(ViewData["CustomerNumber"])
select x;
Customer c = (Customer)CustomerList.First(); %>
<% Html.RenderPartial("Customer",c); %>
Then I just have an actionLink on each listed item:
<%: Html.ActionLink("Select", "Customers", new { id = item.CustomerNumber })%>
It all seems to work but as MVC is new to me I would just be interested in others thoughts on whether this is a good approach?
In regards to proper MVC and separations of concerns, you shouldn't be calling LINQ queries in your view. To get around that, change your controller action code from what you have to this:
if (id == "0")
{
ViewData["CustomerDetails"] = cList.First();
}
else
{
ViewData["CustomerDetails"] = From x in db.customers where x.id = cInt(id);
}
then your partial
<% Html.RenderPartial("Customer",ViewData["CustomerDetails"]); %>
Are you showing the customer information on the same screen that you have your list of customers and not a separate view?
In this case I would take the following approach.
Display a list of customer's, be it a listbox or drop down list.
Let's assume it's a drop down list, probably not very user friendly
You would have the text as the customer name and then the value as the customer id
<select title="Customers">
<option label="Pieter" value="001"></option>
</select>
and using JQuery's $.getJSON you could load the new data via a call to an asp.net MVC action.
Note that your Action should return JsonResult

Categories