I'm new to MVC and I'm trying to make an if statement in the controller that accesses a value from my model. For example, if I am trying to access this radio button value from my model, how would I do it?
[Required] public bool radbutton { get; set; }.
At the top, I tried 'using PasswordTool.Models'.
Then inside my method:
if(PasswordModel.radbutton)
//do something
PasswordModel
namespace PasswordTool.Models
{
public class PasswordModel
{
[Required] public string Password { get; set; }
[Required] public bool RadioButton { get; set; }
}
}
I expect the PasswordModel.radbutton to access the value of the radio button in the model, but intellisense isn't even registering that it exists.
Hi there heisenberg3481,
Welcome to StackOverflow!
To pass Model values from the View you would need to do the following:
#model PasswordModel
// - If the bellow doesn't work try removing the 'Controller' in "MemberController" - //
#using (Html.BeginForm("GetPassword", "MemberController"))
{
Html.TextBoxFor(x => x.Password);
Html.RadioButtonFor(x => x.RadioButton);
<button type="submit">
Submit
</button>
}
Then in your controller you can retrieve the data like so:
[HttpPost]
public ActionResult GetPassword (PasswordModel objModel)
{
if (objModel.RadioButton) {
// Execute action
}
return View();
}
Related
I'm new to learning MVC and could really use some help- as I'm trying to move on from ASP.NET Web Forms. I have a custom web API for all my database transactions that is very efficient and reliable. I've recoded it in .NET Core.
My problem is that I'm finding most of the model binding examples pertaining to MVC are composed of Entity Framework examples. I'm looking for help showing how to link a ViewModel to a Controller with get() post(form) actions. I need to see how to bind to a radio button list, etc...
I'm working with the class below, which has removed the database connections to simplify the answers/suggestions.
public class BuildSearch
{
//Bootstrap date entry
public DateTime StartDate { get; set; }
//Bootstrap date entry
public DateTime EndDate { get; set; }
//Need this bound to a radio button list
public List<GeoArea> GeoAreas { get; set; }
public BuildSearch()
{
GeoAreas = new List<GeoArea>();
// StartDate = DateTime.Parse(DateTime.Now.AddDays(-31).ToShortDateString());
// EndDate = DateTime.Parse(DateTime.Now.ToShortDateString());
GeoAreas.Add(new GeoArea { GeoAreaItem = "Region", Id = 0 });
GeoAreas.Add(new GeoArea { GeoAreaItem = "Manager1", Id = 1 });
GeoAreas.Add(new GeoArea { GeoAreaItem = "Manager2", Id = 2 });
}
}
public class GeoArea
{
public int Id { get; set; }
public string GeoAreaItem { get; set; }
}
I'm trying to create a view, that will display this data, and then allow for me to post back the user edits. I'm purposely looking to keep the example simple as once I figure out how to post back, with the updated data I can handle the pass off to a web API to do the work I need to be done. Just frustrated trying to figure out how I bind to this type of class.
For the radio buttons I would add a property to your BuildSearch class called GeoAreaId. The radio button selection will be model bound to this property on post back. Your BuildSearch class therefore becomes
public class BuildSearch
{
//Bootstrap date entry
public DateTime StartDate { get; set; }
//Bootstrap date entry
public DateTime EndDate { get; set; }
public int GeoAreaId { get; set; } //added field
//Need this bound to a radio button list
public List<GeoArea> GeoAreas { get; set; }
public BuildSearch()
{
GeoAreas = new List<GeoArea>();
// StartDate = DateTime.Parse(DateTime.Now.AddDays(-31).ToShortDateString());
// EndDate = DateTime.Parse(DateTime.Now.ToShortDateString());
GeoAreas.Add(new GeoArea { GeoAreaItem = "Region", Id = 0 });
GeoAreas.Add(new GeoArea { GeoAreaItem = "Manager1", Id = 1 });
GeoAreas.Add(new GeoArea { GeoAreaItem = "Manager2", Id = 2 });
}
public class GeoArea
{
public int Id { get; set; }
public string GeoAreaItem { get; set; }
}
}
Your get method in your controller will look something like this
public IActionResult Search()
{
var buildSearch = new BuildSearch();
return View(buildSearch);
}
Your view will need to look something like this
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
#model BuildSearch
<form asp-controller="Home" asp-action="Search" method="post">
<label asp-for="StartDate">Start date</label>
<input asp-for="StartDate" />
<label asp-for="EndDate">End date</label>
<input asp-for="EndDate" />
<fieldset>
<legend>
GeoArea
</legend>
#foreach (var item in Model.GeoAreas)
{
<input type="radio" name="GeoAreaId" value="#item.Id" />
#item.GeoAreaItem
}
</fieldset>
<input type="submit" />
</form>
For the radio buttons note how the name attribute matches the new property GeoAreaId that I added to your BuildSearch class. This is important for the model binding to work.
Then your post method in your controller will need to look like this
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Search(BuildSearch buildSearch)
{
//...
}
To see what happens set a break point inside this method. Run the code, enter some values into the form and click submit. When the code has stopped inside hover over buildSearch and you will see the model binding has worked. The properties StartDate, EndDate and GeoAreaId will contain the values that you need from the form.
Here is an radio button example:
https://www.c-sharpcorner.com/article/radiobutton-in-asp-net-mvc/
here is a good example of getting your form data from the html form into the controller.
https://learn.microsoft.com/en-us/aspnet/core/tutorials/first-mvc-app/controller-methods-views?view=aspnetcore-2.2
Getting the data into the viewmodel you will do something like this
public async Task<IActionResult> Index()
{
var movies = await _context.Movies.ToList();
if (movies == null)
{
return NotFound();
}
return View(movies);
}
You will then need the form to have a post action to you Action e.g.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ID,Title,ReleaseDate,Genre,Price")] Movie movie)
{
if (ModelState.IsValid)
{
//post to api here
return RedirectToAction(nameof(Index));
}
return View(movie);
}
You will have to pass the model or view model into the html class as
#model MvcMovie.Models.Movie
#{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>Movie</h4> etc etc
we have some c# model
public class PartnerRegistrationForm
{
public string Name { get; set; }
public string Company { get; set; }
public string Email { get; set; }
}
class that contains this model
public class PartnerRegistrationFormHolder
{
public PartnerRegistrationForm PartnerRegistrationForm { get; set; }
}
view
#model WebApplication1.Models.PartnerRegistrationFormHolder
#using (Html.BeginForm("Index", "Registration", FormMethod.Post))
{
#Html.TextBoxFor(e => e.PartnerRegistrationForm.Name)
#Html.TextBoxFor(e => e.PartnerRegistrationForm.Email)
#Html.TextBoxFor(e => e.PartnerRegistrationForm.Company)
<button type="submit">Send</button>
}
this method TextBoxFor create inputs with long names like 'PartnerRegistrationForm.Company'
ok, its reflection magic
then i fill this form and submit it
i have view
[HttpPost]
public ActionResult Index(PartnerRegistrationFormHolder partnerRegistrationFormHolder)
{
return new HttpNotFoundResult();
}
i run my program with debug and take a breakpoint on this action
HOW MVC create object from form? can anyone explain me or give me some link where to read?
more reflection magic :) it's called model binding:
https://learn.microsoft.com/en-us/aspnet/core/mvc/models/model-binding?view=aspnetcore-2.1
I would like to know if I am doing something wrong or it is not possible to post only part of view model using default model binder. Let's say I have a complex viewmodel that only small part it should be posted. I would like to achieve something like this:
public class ComplexViewModel
{
public object FirstNotPostedData { get; set; }
public object SecondNotPostedData { get; set; }
//......
public object NthNotPostedData { get; set; }
public InnerModelToPost InnerModelToPost { get; set; }
}
public class InnerModelToPost
{
public string FirstProperty { get; set; }
public string SecondProperty { get; set; }
public string ThirdProperty { get; set; }
}
In view I would like to display part of model and post the other part:
#model ComplexViewModel
#* DISPLAYING DATA *#
<form>
#Html.HiddenFor( m => m.InnerModelToPost.FirstProperty )
#Html.HiddenFor( m => m.InnerModelToPost.SecondProperty )
#Html.HiddenFor( m => m.InnerModelToPost.ThirdProperty )
<button type="submit">Submit button</button>
</form>
And then I would like to be able to pass this model to my controller in this way using default model binder:
public ActionResult GetOnlyImportantPartOfModel( InnerModelToPost innermodel)
{
//I'm getting empty model when I' doing like this
return View();
}
You may ask why not to pass entire model as parameter to this action method. So the answer is: code readablity. I store ComplexViewModel in session and I read it in first line of my action method. I would like to pass only this data that I want to update my model with.
Your need to use the Prefix property of [Bind] attribute to strip the InnerModelToPost prefix from your form values.
public ActionResult GetOnlyImportantPartOfModel([Bind(Prefix = "InnerModelToPost")] InnerModelToPost innermodel)
{
....
}
Having said that, if your only using properties of InnerModelToPost, then in your GET method, you can read the parent class (ComplexViewModel) from Session, but pass only the InnerModelToPost property to the view
Can't get all my ViewModel properties back after the postback (After user entered some values on HttpPost)
There are numerous questions here related to losing data or getting nulls after the postback
I tried some of them and played around on my case, I think the scenario is a bit different,
Using a PartialView or Editor Templates(except a list property), Always the returned result properties are null.
In partialView approach always all the properties are null, I think maybe I missed a piece.
In the "custom editor template approach for the type", I'll have just "EnteredNums" List returned. (Maybe because these are what the template have EditorFor for them, but what is the solution here if that's the case?)
Don't know weather if it's important here or not, the application also uses Unity. I don't think it be the problem here.
The HttpGet Passed model is the same as HttpPost : DataVm
The name of the action is also the same : ProcessEnteredData
=================== Controller and action
[HttpPost]
public ActionResult ProcessEnteredData(DataVm vm)
{
if (ModelState.IsValid)
{
foreach (NumType num in vm.EnteredNums)
{
int i1 = num.Score1;
int i2 = num.Score2;
string profTitle = vm.Profile.Title;
Repository.Context.EnteredNums.Add(num);
}
return RedirectToAction("ShowTable");
}
else
{
return View(vm);
}
}
==============
The Partial View of Custom Editor Template are similar :
#model xxxx.NumType
#Html.LabelFor(m => m.TheTitle)
#Html.TextBoxFor(m => m.Score1)
#Html.TextBoxFor(m => m.Score2)
#Html.HiddenFor(m => m.Profile)
// Profile or ProfileId - Just used to see could it bring the property back or not as a test
============
NumType Model
[Key]
public int NumTypeId { get; set; }
[ForeignKey("Profile")]
[Required]
public int ProfileId { get; set; }
public int Score1 { get; set; }
public int Score2 { get; set; }
public int BoxId { get; set; }
public Box Box { get; set; } // something not important here
public virtual Profile Profile { get; set; }
============
The ViewModel
public class DataVm
{
public Profile Profile { get; set; }
public string TheTitle { get; set; }
public List<NumType> EnteredNums { get; set; }
// In the Editor template approach it's the only item with data and others are null
public List<Box> Boxes { get; set; }
}
=========
View for PartialView approach :
#model xxxx.DataVm
#using (Html.BeginForm("ProcessEnteredData", "Profile", FormMethod.Post))
{
#Html.AntiForgeryToken()
Model.EnteredNums = new List<NumType>();
foreach(var box in Model.Boxes)
{
NumType num = new NumType();
num.Profile = Model.Profile;
num.Box = box;
int iCount = Model.EnteredNums.Count;
Model.EnteredNums.Add(num);
#Html.Partial("NumView", Model.EnteredNums[iCount]);
}
<input type="submit" value="Do Process" />
}
===================
View for Editor for approach :
// instead of #Html.Partial :
#Html.EditorFor(m => m.EnteredNums[iCount]);
My question is related to this question and answer
The following complex model:
public class EditSubmissionModel
{
public string foo { get; set; }
public Submission submission { get; set; }
}
The simple model
[Table(Name = "Submission")]
public class Submission
{
[Column(IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
public int SubmissionId { get; set; }
[Column]
public string Title { get; set; }
}
The view:
#model Project.WebUI.Models.EditSubmissionModel
#{
ViewBag.Title = "editSubmission";
}
<h2>editSubmission</h2>
#using (Html.BeginForm())
{
<legend>SubmissionModel</legend>
#Html.EditorFor(m => m.foo)
#Html.EditorFor(m => m.submission)
<input type="submit" value="Save" />
}
the editorTemplate
#model Project.Domain.Entities.Submission
#Html.EditorFor(m => m.Title)
the controller
[Authorize]
[HttpPost]
public ActionResult editSubmission(string shortName, EditSubmissionModel model)
{
shortname = "second" (is ok)
model.foo = aaa (also ok i edited it on the view)
model.submission = null (not binded? or i dont know?)
I can't see the error, any ideas?
Status no repro. Steps:
Create a new ASP.NET MVC 3 application using the default template
Define 2 models:
public class Submission
{
public int SubmissionId { get; set; }
public string Title { get; set; }
}
public class EditSubmissionModel
{
public string foo { get; set; }
public Submission submission { get; set; }
}
Modify HomeController so that it looks like this:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(string shortName, EditSubmissionModel model)
{
return Content(model.submission.Title);
}
}
Update ~/Views/Home/Index.cshtml view so that it looks like this:
#model EditSubmissionModel
#using (Html.BeginForm())
{
<legend>SubmissionModel</legend>
#Html.EditorFor(m => m.foo)
#Html.EditorFor(m => m.submission)
<input type="submit" value="Save" />
}
Add a custom editor template for the Submission type (~/Views/Home/EditorTemplates/.cshtml) like this:
#model Submission
#Html.EditorFor(m => m.Title)
Hit Ctrl+F5, fill in the form and submit. As totally expected the value you have entered in the Title textbox will be correctly bound and shown on the screen.
So I repeat the question that I've already asked you in the comments section: what did you do differently? You answered that it is a copy-paste from your code, but as I have illustrated you (with a full step-by-step guide) this is not the case.
Now here's a suspicion that I have. Your actual POST action looks like this:
public ActionResult editSubmission(string shortName, EditSubmissionModel submission)
and not like this:
public ActionResult editSubmission(string shortName, EditSubmissionModel model)
Notice the parameter name.
#Darin Dimitrov you were completely right, what do i do different. The code above was completely fine. The problem was the get command which looked like:
[Authorize]
public ActionResult editSubmission(string confShortName, string submission)
{
//do stuff
return View();
}
And the Modelbinder will get problems if the httpPost anywhere has same name like the HttpGet in my case string submission and Editsubmission.submission. Big thanks to your detailed step by step advice!