I'm trying to update my model from an [HttpPost] The user enters their job title and I want to update the model and render it on a results page. However, its not updating the model based on the scope. Default is "Student" and I want it to display whatever the user inputs.
Model
public class InternshipModel {
public string Employer { get; set; }
public string Major { get; set; }
public string Title { get; set; }
}
Controller
Models.InternshipModel mod = new Models.InternshipModel() { Major = "Computer Science", Employer = "Random", Title = "Student" };
//After Post method
[HttpPost]
public ActionResult Index(string major, string employer, string title) {
mod.Title = title;
UpdateModel(mod.Title);
return RedirectToAction("Results", "Home");
}
public ActionResult Results() {
ViewBag.Message = "This is the results page.";
ViewBag.Changes = mod.Title; //Should expect user input, not default 'Title'
return View();
}
Another way is to declare your model as static like
Public static Models.InternshipModel mod = new Models.InternshipModel() { Major = "Computer Science", Employer = "Random", Title = "Student" };
In static only one copy of your model will be created when the application starts and will reserve memory until the application stops thats why its not suggested .You can try the solution of Stephen since its a better way to achieve this.
The mod variable that you have created (scope for the controller), resets each time you move out of the controller, or as #stephen in his comment mentions, it creates a new instance each time you make a request.
Here, you move out of the controller to the routing table when you return RedirectToAction("Results","home");
So, the best way to deal with this issue is to save the model in a database and retrieve wherever you need it.
Since you have mentioned, you would query the database with this details, It would be better to send this object as a whole or only the title to the Action you are passing it to. Like
return RedirectToAction("Results", "Home", new { InternshipModel = mod });
or
return RedirectToAction("Results", "Home", new { title = mod.Title });
and you would recieve them in the Action as parameters.
like :
public ActionResult Results(Models.InternshipModel mod)
{
ViewBag.Message = "This is the results page.";
ViewBag.Changes = mod.Title; //User input, not default 'Title'
return View();
}
Hope this helps.
Another simple change you can do is adding static keyword before the object declaration. This will help you to initialise only one instance and your latest values will be reflected when object is invoked
Public static Models.InternshipModel mod = new Models.InternshipModel() { Major = "Computer Science", Employer = "Random", Title = "Student" };
Related
This question already has answers here:
Passing object in RedirectToAction
(4 answers)
Closed 5 years ago.
I've got a situation that I don't understand. I'm developing a small web application, which simulates poolbillard game process. I've got two actions, first is an action responsible for gathering input from user, second calculates necessary data:
[HttpPost]
public ActionResult UserInput(UserInputViewModel inputParameters)
{
if (!ModelState.IsValid)
{
return View();
}
return RedirectToAction("Play", new { inputParameters });
}
public ActionResult Play(UserInputViewModel playParameters)
{
PoolTableConfig config = CreatePoolTableConfig(playParameters);
PoolTable poolTable = new PoolTable(config);
PocketName resultPocketName = poolTable.Play();
IEnumerable<Point> crossPoints = poolTable.CrossPoints;
ViewBag.ResultPocketName = resultPocketName;
ViewBag.CrossPoints = crossPoints;
return View();
}
private PoolTableConfig CreateConfig(UserInputViewModel input)
{
return new PoolTableConfig()
{
Width = input.Width,
Height = input.Height,
BallPointX = input.BallPointX,
BallPointY = input.BallPointY,
VectorX = input.VectorX,
VectorY = input.VectorY
};
}
And UserInputViewModel looks like this:
public class UserInputViewModel
{
[Required(ErrorMessage = "Please specify width.")]
[ProperWidth(ErrorMessage = "Width must be an even number.")]
[Range(300, 700)]
public uint Width { get; set; }
[Required(ErrorMessage = "Please specify height.")]
[Range(150, 500)]
public uint Height { get; set; }
[Required(ErrorMessage = "Please specify ball position X.")]
[Display(Name = "Ball position X")]
[ProperBallPosition("Width", ErrorMessage = "Ball position X cannot be equal or higher than pool table width.")]
public uint BallPointX { get; set; }
[Required(ErrorMessage = "Please specify ball position Y.")]
[Display(Name = "Ball position Y")]
[ProperBallPosition("Height", ErrorMessage = "Ball position Y cannot be equal or higher than pool table width.")]
public uint BallPointY { get; set; }
[Required(ErrorMessage = "Please specify vector X.")]
[Display(Name = "Vector X value")]
[Range(-1000, 1000)]
public int VectorX { get; set; }
[Required(ErrorMessage = "Please specify vector Y.")]
[Display(Name = "Vector Y value")]
[Range(-1000, 1000)]
public int VectorY { get; set; }
}
As you see I'm passing custom type (viewmodel) from UserInput() action to Play() action. The inputParameter variable in UserInput() action has proper values, but when program goes to the Play() action it is null or empty (with default values of types contained in object).
As I understand default ASP.NET model binding verifies what properties does custom object need and searches for them in http header sent from the client. I stick to the standard ASP.NET validation schema, so I don't understand why does my app have a problem with translating http header parametrs to a .NET object. When I replace custom type with predefined type (i.e. string) everything is as it should be.
My question is: Why ASP can't generate proper object from http header in this situation?
Maybe try something like this
return RedirectToAction("Play", "ControllerName", inputParameters);
Also could you not just do the calculations that are done in Play inside the UserInput ActionResult and then just return what ever view you were going to return in the Play ActionResult?
[HttpPost]
public ActionResult UserInput(UserInputViewModel inputParameters)
{
if (!ModelState.IsValid)return View();
PocketName resultPocketName;
IEnumerable<Point> crossPoints;
PoolTable poolTable = new PoolTable((int)inputParameters.Width, (int)inputParameters.Height, (int)inputParameters.BallPointX, (int)inputParameters.BallPointY, inputParameters.VectorX, inputParameters.VectorY);
resultPocketName = poolTable.Play();
crossPoints = poolTable.CrossPoints;
ViewBag.ResultPocketName = resultPocketName;
ViewBag.CrossPoints = crossPoints;
return View("ViewName", whatEverModelYouNeed);
}
Thank you guys for your help, but I've just found the most satisfying answer myself :). I used RouteValueDictionary(). UserInput() action should look like this:
[HttpPost]
public ActionResult UserInput(UserInputViewModel inputParameters)
{
if (!ModelState.IsValid)
{
return View();
}
return RedirectToAction("Play", "Home" , new RouteValueDictionary(inputParameters));
}
Use:
return Play(inputParameters);
instead of
return RedirectToAction("Play", new { inputParameters });
-- Edit
Good to know that you can't pass object to GET method, and it's not possible to use POST either because RedirectToAction returns 302 to the browser which means the browser will make GET request!
The solution I wrote above 'll do the trick but it's hacky and not recommended :)
If you want to issue the redirect command and pass data you will use TempData. This post breaks it down better than I could without plagiarism Passing object in RedirectToAction
I have a simple form which saves the following entity
public class TravelInfo{
public int ID {get;set;}
public string CentreCode {get;set;}
public DateTime TravelDate {get;set;}
}
I have the standard 2 create methods in my controller - 1 get 1 post and am using this viewmodel to get stuff into the view.
public class TravelInfoVM{
public TravelInfo TravelInfo{get;set;}
public IEnumerable<SelectListItem> Centres {get;set;}
}
Controller methods...
public ActionResult Create(){
var CentresList = db.Centres.Select(c=> new SelectListItem {Text = c.Name, Value = c.Code}).ToList();
TravelInfoVM = new TravelInfoVM(){Centres = CentresList};
return View(TravelInfoVM);
}
[HttpPost]
public ActionResult Create(TravelInfoVM model){
//the Centres part of the model at this point is empty
if(ModelState.IsValid){
//save
//redirect
}
//do i **REALLY** have to get it again as below, or can I hold on to it somehow?
model.Centres = db.Centres.Select(c=> new SelectListItem {Text = c.Name, Value = c.Code}).ToList();
return View(model);
}
the question is, do I really need to do a second round trip to the DB to get the list of Centres if the ModelState comes back as invalid? or is there a better/different way to persist this list across posts, until the user correctly enters the details required to save..? or do i have completely the wrong end of the stick..
Not without adding it to the session, which is an inappropriate use of the session. Otherwise, each request is a unique snowflake, even if it seems like it's the same because you're returning the same form with errors.
I wouldn't worry about this. It's just one query, and since it's the same query already issued previously, it will very likely (or at least could be) cached, so it may not actually hit the database, anyways.
One thing I would recommend though is abstracting the query so that your GET and POST actions will simply call a function that won't change, and if you need to make a change to how the select list is created, you just do it in one place:
internal void PopulateCentreChoices(TravelInfoVM model)
{
model.Centres = db.Centres.Select(c=> new SelectListItem {Text = c.Name, Value = c.Code}).ToList();
}
...
public ActionResult Create(){
var model = new TravelInfoVM();
PopulateCentreChoices(model);
return View(model);
}
[HttpPost]
public ActionResult Create(TravelInfoVM model){
if(ModelState.IsValid){
//save
//redirect
}
PopulateCentreChoices(model);
return View(model);
}
Ok, so I'm new to this whole MVC-world, but it seems to be a pretty good way of getting things done and I'm trying to make it work here.
The problem is:
I can't get data from my table in my SQL-database to a simple drop-down form on my registration page.
I have just no idea where to put the stuff, where to code to open the table, select the ids, where to put the response.write and how do I send it to the view?
My Model is this:
public class users
{
public string name {get; set;}
public int user_id {get; set;}
}
My Controller is this:
[HttpGet]
public ActionResult ListUser()
{
return View();
}
And my View is this:
#model Community.Models.users
I have googled for 2 days now and watched several videos on youtube but of no use, I can't find it. Please, anyone with some knowledge here? And please point me to some good tutorials and/or forums where I can browse for more questions I might have
Still no luck on this project..
I'm creating a form and within that form, i want a db-loop (IEnumerable).. But the current model is not a IEnumerable. I'm pretty much stuck, watched a bunch of tutorials and they all just list ONE connection, what if I want two models?
Here is my Controller, I get that you must pass a list to the view, right?
public ActionResult Registration()
{
return View(db.users.ToList());
}
How do i get hold of that list in my view witout an IEnumerable model?
#neoistheone, your example didnt help me much, my DB opens like this:
private DataBaseContext db = new DataBaseContext();
and i don't know how, but it opens the connection.
I've tried for so many hours now, its just silly, haven't slept for soo long!
I'm used to programming ASP-Classic fyi, and this is my first serious try to upgrade my knowledge about programing an up-to-date language and OOP.
Add the SelectList to your model:
public SelectList DropDownList { get; set; }
build the class for that collection:
public class MyListTable
{
public string Key { get; set; }
public string Display { get; set; }
}
and then in your controller, load the data for the MyListTable class from the database:
var list = new List<MyListTable>();
using (SqlConnection c = new SqlConnection(cString))
using (SqlCommand cmd = new SqlCommand("SELECT KeyField, DisplayField FROM Table", c))
{
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
list.Add(new MyListTable
{
Key = rdr.GetString(0),
Display = rdr.GetString(1)
});
}
}
}
var model = new users();
model.DropDownList = new SelectList(list, "Key", "Display");
and then finally, you need to send your model to the view:
return View(model);
Now in the Razor you can display this:
#Html.DropDownListFor(m => Model.DropDownList);
You of course can name these things better names, but you get the idea.
There a great answers already but Here is another approach.
You will use user as a model, ListUserViewModel as view-model and UserController as the contoller. The work of view-model is to carry all info needed to be displayed on the page from the controller without adding unwanted properties into the model class. In your case list of users from database into the drop down list.
Model:
public class User //By the way use singular when naming a class
{
public string name {get; set;}
public int user_id {get; set;}
}
View-model
public class ListUserViewModel
{
public list<User> Users{get; set;}
}
Controller
public class UserController : Controller
{
private DataBaseContext db = new DataBaseContext();
[HttpGet]
public ActionResult ListUser()
{
var users = db.Users.ToList();
var viewModel = new ListUserViewModel { Users = users };
return View(viewModel);
}
}
Now use ListUserViewModel instead of User in your view as a model
#model Community.Models.ListUserViewModel
and the drop down
#Html.DropDownListFor(m => m.Users, new SelectList(Model.Users, "user_id", "name"), " ")
Explanation:
You are creating drop down list for Users with Model.Users as select list data source. "user_id" as a value of the selected user and "name" as display label. the last argument( i put empty string " ") is a default value that the drop down will display before selection.
I hope this will help you or someone else.
Try this,
model
public string CoutryID { get; set; }
public List<SelectListItem> CountryList { get; set; }
Controller method which fill the list
public List<Country> getCountryList()
{
using (QRMG_VendorPortalDataContext _context = new QRMG_VendorPortalDataContext())
{
return (from c in _context.Countries
where c.IsDeleted == false
select c).ToList();
}
}
Drop down list in View
#Html.DropDownListFor(m => m.CoutryID,
new SelectList(Model.CountryList,
"CoutryID", "Value"))
I find this system works (and avoids using ViewBag):
View Model:
public class YourViewModel
{
// This could be string, int or Guid depending on what you need as the value
public int YourDropdownSelectedValue { get; set; }
public IEnumerable<SelectListItem> YourDropdownList { get; set; }
}
Controller:
// Get database values (by whatever selection method is appropriate)
var dbValues = db.YourEntity.ToList();
// Make Selectlist, which is IEnumerable<SelectListItem>
var yourDropdownList = new SelectList(dbValues.Select(item => new SelectListItem
{
Text = item.YourSelectedDbText,
Value = item.YourSelectedDbValue
}).ToList(), "Value", "Text");
// Assign the Selectlist to the View Model
var viewModel = new YourViewModel(){
// Optional: if you want a pre-selected value - remove this for no pre-selected value
YourDropdownSelectedValue = dbValues.FirstOrDefault(),
// The Dropdownlist values
YourDropdownList = yourDropdownList
};
// return View with View Model
return View(viewModel);
and in the View:
#Html.DropDownListFor(a => a.YourDropdownSelectedValue, Model.YourDropdownList, "select this text - change this to null to exclude", new { #class = "your-class" })
If you are really new to ASP.Net MVC, this is a quite good Tutorial that shows you how the MVC-Pattern works.
MVC3: http://www.asp.net/mvc/tutorials/getting-started-with-aspnet-mvc3/cs/intro-to-aspnet-mvc-3
MVC4: http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/intro-to-aspnet-mvc-4
Here is the sample-code to download: http://code.msdn.microsoft.com/Introduction-to-MVC-3-10d1b098
this is an helpful video: http://www.asp.net/mvc/videos/mvc-1/conference-presentations/creating-nerddinnercom-with-microsoft-aspnet-model-view-controller-mvc
this is my table in the database
take look it my Action controller
// GET: Letters
public ActionResult Index()
{
ViewBag.LetterStatus = new SelectList(LetterStatusService.GetAllLettersStatus(), "Id", (CultureHelper.GetCurrentCulture() == "ar") ? "NameArabic" : "Name", Request.QueryString["LetterStatus"]);
return View();
}
and in the view
#Html.DropDownList("LetterStatus")
the constructor I used is
new SelectList(
list<Objlect> myListFromDatabase,
string PropertyNameOfValueInHtml,
string PropertyNameOfDesplayInHtml,
string SelectedItemValue
);
this line Request.QueryString["LetterStatus"] because I send the Selected Items within QuerySrting
and based on CurrentCulture I chose what column to display
and the result are
but I think the best way to do this,,,, is to get or create the Items then Iterate throw them to generate the select tag manually. I described this approach well in this answer
hope this helps you
I had to put Everything together from about 5 different Stack Overflow entries. I'm a newbie that's not in love with EF. I prefer doing things in SQL. Mike Perrenoud got me started, but I had trouble getting his solution to compile properly in the view.
First, I declared my dropdown Id/Name inside my model and then declared a numeric selector plus a SelectList
public class BusinessType
{
public int Id { get; set; }
public string Name { get; set; }
}
public int SelectedBusinessId { get; set; }
public SelectList BusinessTypeddList { get; set; }
In my [HttpGet] (the one that does not pass in a model), I ALWAYS populate my dropdowns. I'm doing things in SQL Server because I find it easier than the EF abstraction and syntax (which baffles me). This code declares a List of business types and populates it directly from the database. Feel free to use a sproc or whatever. IMPORTANT: Don't forget to return the View(model) at the end or your View will get an object missing reference error.
var list = new List<MerchantDetail.BusinessType>();
using (var con = new SqlConnection(Common.DB_CONNECTION_STRING_BOARDING))
{
con.Open();
using (var command = new SqlCommand("SELECT Id, BusinessTypeDesc as Name FROM BusinessType order by Id", con))
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
list.Add(new MerchantDetail.BusinessType
{
Id = reader.GetInt32(0),
Name = reader.GetString(1)
});
}
}
}
}
var model = new MerchantDetail();
model.BusinessTypeddList = new SelectList(list, "Id", "Name");
model.SelectedBusinessId = 0;
return View(model);
The view is simple. Mine looks like this.
#Html.DropDownListFor(m => m.SelectedBusinessId, Model.BusinessTypeddList, "Select One:", new { #class = "custom-select" });
NOTE: Setting the SelectedBusinessId to 0 only works if there's nothing in the model. It will be set to one of the dropdown values otherwise. That will happen in a different HttpGet that passes the model in.
The big picture is this: when a user logs in, they can select an "organization" to log into from a drop down list, which has a unique ID # for each organization. When the user logs in, I want to grab their ID # from my people table. The problem is, the person could be in the table twice, like this:
ID Name OrgID
1 Brandon 1
2 Brandon 2
So, to grab the appropriate person ID, I need to check against the OrgID also. Here is what I've got in my GET method:
public ActionResult Index()
{
ViewBag.Organization = new SelectList(db.Organizations, "OrgID", "OrgName");
return View();
}
Here is my POST method:
[HttpPost, ActionName("Submit")]
public ActionResult SubmitPost(string LoginNID, string LoginPassword, LoginViewModel model)
{
//Create new instance of ActiveDirectoryHelper to access its methods
ActiveDirectoryHelper login = new ActiveDirectoryHelper();
//Get Username and Password from forms
String Username = LoginNID;
String Password = LoginPassword;
int OrgID = model.Organizations.OrgID;
//ViewBag initialization for error message
ViewBag.NumTimes = 0;
ViewBag.Message = "Error logging in, please try again.";
//LDAP Authentication
bool LoginPassed = login.IsAuthenticated(Username, Password);
int Organization = organizations.OrgID;
//if Authentication Success enter site, otherwise Return error message
if (LoginPassed == true)
{
//grabs the one person from the People model whose NID is == to Username
var peopleModel = db.People.Single(g => g.NID == Username);
//grabs the peopleID for use in making a session variable
var peopleID = peopleModel.PeopleID;
//sets a session variable to be used that is the logged in person's ID
Session["peopleID"] = peopleID;
return View("LoginSuccess");
}
else
{
ViewBag.NumTimes = 1;
ViewBag.Organization = new SelectList(db.Organizations, "OrgID", "OrgName", organizations.OrgID);
return View("Index");
}
}
Here is my ViewModel:
public class LoginViewModel
{
public Music.Models.Organizations Organizations { get; set; }
}
Any ideas as to how I can get the organization's ID # and then select the unique person ID # so I can make that a session variable? Thanks much!
Edit: updated code to reflect changes I've made from answers provided.
You can create a class of something like session information with the id to capture that immediately after login. Have your login perform login verification, then route to the session info page where you select the organization based off users available organization. Pass that to your helper class that stores it in session information.
Edit
After re-reading your question you can provide a list of organizations at the login page and do a verification against whatever you are doing currently and ensuring it matches organization too so it is only one screen.
public class LoginCredential
{
public string UserName { get; set; }
public string Password { get; set; }
public int OrganizationId { get; set; }
}
public ActionResult Index()
{
ViewBag.Organizations = new SelectList(db.Organizations, "OrgID", "OrgName");
return View();
}
Make the model for your login page a login credential
#model MyProject.Models.LoginCredential
Then when you do a submit from your form it will pass in the selected options.
[HttpPost, ActionName("Submit")]
public ActionResult Login(LoginCredential credentials)
{
String Username = credentials.UserName ;
String Password = credentials.Password ;
Int OrganizationId = credentials.OrganizationId;
/// Rest of your code goes here
}
After creating a ViewModel based on kadumel's answer, I was still getting the same error as before. Since the username and password were both getting posted back from the view, I only needed 1 model to be used by the view to get the OrgID. What I did was change the #model directive in my view to this:
#model Music.Models.Organizations
and then changed the parameters in my POST to:
public ActionResult SubmitPost(Organizations model, string LoginNID, string LoginPassword)
From there, I was able to get the OrgID by doing this:
int OrgID = model.OrgID;
From debugging, OrgID is finally getting the correct value, and it says that the value of "model" is no longer null. While this works, I'm not completely sure why, since I would think that using a ViewModel would do just the same thing because my ViewModel contained the Organizations model.
EDIT: I figured out why this was working and using a ViewModel didn't seem to: in my view I had
#Html.DropDownList("Organization", ViewData["Organizations"] as SelectList, "--Select Organization--")
The first parameter, "Organization" is nothing in my model, so of course the model was going to return null and not give me the ID. When I changed it to:
#Html.DropDownList("OrgID", ViewData["Organizations"] as SelectList, "--Select Organization--")
It passed back the ID and I was good to go.
I have a ViewModel like so:
public class ProductEditModel
{
public string Name { get; set; }
public int CategoryId { get; set; }
public SelectList Categories { get; set; }
public ProductEditModel()
{
var categories = Database.GetCategories(); // made-up method
Categories = new SelectList(categories, "Key", "Value");
}
}
Then I have two controller methods that uses this model:
public ActionResult Create()
{
var model = new ProductEditModel();
return View(model);
}
[HttpPost]
public ActionResult Create(ProductEditModel model)
{
if (ModelState.IsValid)
{
// convert the model to the actual entity
var product = Mapper.Map(model, new Product());
Database.Save(product);
return View("Success");
}
else
{
return View(model); // this is where it fails
}
}
The first time the user goes to the Create view, they are presented with a list of categories. However, if they fail validation, the View is sent back to them, except this time the Categories property is null. This is understandable because the ModelBinder does not persist Categories if it wasn't in the POST request. My question is, what's the best way of keeping Categories persisted? I can do something like this:
[HttpPost]
public ActionResult Create(ProductEditModel model)
{
if (ModelState.IsValid)
{
// convert the model to the actual entity
var product = Mapper.Map(model, new Product());
Database.Save(product);
return View("Success");
}
else
{
// manually populate Categories again if validation failed
model.Categories = new SelectList(categories, "Key", "Value");
return View(model); // this is where it fails
}
}
But this is an ugly solution. How else can I persist it? I can't use a hidden field because it's a collection.
I would use the repository to fetch whatever data is needed and don't think it's an ugly solution:
[HttpPost]
public ActionResult Create(ProductEditModel model)
{
if (!ModelState.IsValid)
{
// manually populate Categories again if validation failed
model.Categories = Repository.GetCategories();
return View(model);
}
// convert the model to the actual entity
var product = Mapper.Map(model, new Product());
Database.Save(product);
// I would recommend you to redirect here
return RedirectToAction("Success");
}
To further refactor this I would recommend you watching the excellent Putting Your Controllers on a Diet video presentation by Jimmy Bogard.
I typically implement my lists (for drop downs) as a readonly property. When the View gets the value the property is self contained on what it needs to return the values.
public SelectList Categories
{
get
{
var categories = Database.GetCategories(); // made-up method
return new SelectList(categories, "Key", "Value");
}
}
If necessary you can grab the currently selected item (i.e. validation failed) from the property containing the id that was posted and bound to the instance of your class.
In my case I have a BaseModel class where I keep all those property list as class attributes.
Like in the following sample:
public IEnumerable<SelectListItem> CountryList
{
get
{
return GetCountryList().Select(
t => new SelectListItem { Text = t.Name, Value = Convert.ToString(t.CountryID) });
}
}
GetCountryList() is a function that ask a Singleton for data. This would only happen once in the app lifecycle
Another way for doing this, and if those lists are pretty big, would be to have a static utility class with the lookup table that returns the SelectListItem.
If you need to access a list that change from time to time then simply dont use a Singleton class.