I am in the process of migrating PHP code to ASP.NET MVC and previously for the register page I would store if the new user had accepted the rules and also was COPPA verified by redirecting from /register to /register&readrules=1&coppa=1. I would then just parse the #readrules and #coppa in the code.
What is the best way to do this in ASP.NET? Thanks
Use query string parameters instead:
/register?readrules=1&coppa=1
This is more standard and you do not need any parsing. Just define a view model to accomodate those values:
public class MyViewModel
{
public int Readrules { get; set; }
public int Coppa { get; set; }
}
and ten have your Register controller action take this view model as parameter:
public ActionResult Register(MyViewModel model)
{
... at this stage model.Readrules and model.Coppa will contain the values passed
as query string parameters tat you could use here
}
The default model binder will automatically bind the values of the readrules and coppa query string parameters to the corresponding properties of the view model that your controller action takes.
Related
I have read this link: https://www.future-processing.pl/blog/view-code-reuse-techniques-in-asp-net-mvc/
I can not use any of those helper ways...
I have to show on multiple mvc sites this string:
1612-1
That is an inquiry number: 16 is the day of month, 12 the month of year and 1 is the database id. I am sure that will not be the final impl but for now we take it as given.
public class MyViewModel
{
public string City { get; set; }
public string PostalCode { get; set; }
public List<string> ActionItemDescriptions { get; set; }
public string InquiryNumber { get; set; }
}
Where would you create the InquiryNumber?
If I put it inside the razor view I cant reuse it.
Seems business logic to me , so it belongs in the business layer.
Then, from within your controller you:
call the business component which returns the inquiry number
store the number in your view model
pass the view model to the view.
One way you could get an inquiry number, without using a helper, is this:
In a controller, have the following action method:
public ActionResult GetInquiryNumber()
{
// TODO : The code to get the inquiry number.
return Content("1612-1");
}
You can then call that method in any view you like, using the following:
#{ Html.RenderAction("GetInquiryNumber", "Home"); }
Obviously you will need to come up with your own method, and controller, names.
This isn't the ideal way of passing data to a view (using a viewmodel is preferable), but the above approach is an option to you.
Alright...this may be a bit backwards but, I only need to do it in one spot.
I have a Model
public class LoginModel : xxx.Models.PageVars
{
public Item.LoginAttempt LoginAttempt { get; set; }
public LoginModel()
{
// does a bunch of stuff here...mainly to set the layout properties from PageVar
this.LoginAttempt = new Item.LoginAttempt();
}
}
Login Attempt is a simple obj (for now)
// login attempt
public class LoginAttempt
{
public string Email { get; set; }
public string Password { get; set; }
}
My controller
public ActionResult Login()
{
return View("Login", new Models.LoginModel());
}
[HttpPost]
public ActionResult LoginAttempt(LoginAttempt model)
{
return View("Login", model);
}
In my view
#model xxx.Models.LoginModel
Is there a way to use the property of the obj/model from LoginModel for the #model.
I can get the values from FormCollection or request but...that's not optimal.
thoughts???
tnx
The model for your GET should match the model for your POST. Otherwise, you're not playing on the same field. In order to allow the binding of data from a POST to a model, the HTML Helpers will generate a name that matches the access path of the property in the view's model. In other words, in your form, based on the model being LoginModel, your field names will be LoginAttempt.Email and LoginAttempt.Password. But, in the POST action, you're accepting just LoginAttempt, so the modelbinder is expecting to see data for Email and Password, which it won't find.
There's actually not even any need for this nested class. Just put your Email and Password fields directly on LoginModel and use that for both your view and your POST parameter. Then, you won't have any issues because everything will match up.
Why don't you have the form post controller action accept the parent model LoginModel instead of LoginAttempt? That way, the default MVC model binding should automatically parse the submitted values into the LoginModel and you'll have acces to LoginAttempt.
If it isn't then your form needs to use the prefix values in the names of the properties on the form. This is done automatically when you use TextboxFor, DropdownListFor etc.
In your example, the names of the form fields should start with LoginAttempt.Email etc
I've seen it work 2 ways. First way would be to rename your LoginAttempt model parameter to be
[HttpPost]
public ActionResult LoginAttempt(LoginAttempt loginModel)
{
return View("Login", model);
}
But i would use the Bind(Prefix) option
[HttpPost]
public ActionResult LoginAttempt([Bind(Prefix="LoginModel")] LoginAttempt model)
{
return View("Login", model);
}
you can't really return model of type LoginAttempt to the view though so you'd have to do even more work to get it to work if you're set on doing it this way. You should probably be redirecting to a different page instead of returning the Login view if it succeeds. Other wise return new LoginModel() {LoginAttempt = model}
I'm trying to get my head around why (data annotation) validation errors are triggering when the page first loads, prior to any Submit/Posts. But more importantly how to fix this.
Reading SO and the interwebs, the reason seems to be model binding triggers validation errors for the view model properties, prior to the view model properties having values. I'm not sure if this is true and what is actually happening, but it sounds legit.
And I've read two workarounds, which sound a bit hacky:
1. Use the ModelState.Clear in the controller action method on the inital page load, OR
2. Initialiase the view model properties in an empty view model constructor. (Yet to confirm this technique)
Both these techniques sound like work-arounds. I'd rather understand what is happening and design my code appropriately.
Have others come across this issue? And if so, what are you doing?
Code as requested. Below you can see the Data Annotation validation on Property1 which is being triggered on initial requests, i.e. first page load.
Controller action method (refactored for simplicity):
public ActionResult Index([Bind(Include = Property1, Property2, Property3, vmclickedSearchButton)] IndexVM vm, string Submit)
{
bool searchButtonClicked = (Submit == "Search") ? true : false;
if (searchButtonClicked)
{
PopulateUIData(vm); // Fetch data from database and pass them to VM
if (ModelState.IsValid)
{
vm.clickedSearchButton = true; // Used in the vm to avoid logic execution duing initial requests
DoWork(vm);
}
}
return View(vm);
}
// Inital request
IndexVM newVM = new IndexVM();
PopulateUIData(newVM); // Fetch data from database and pass to VM
return View(newVM);
}
Design note:
Ideally I would like to sepate the rendering and submiting logic into separate action methods.
I.e. rendering within a [HttpGet]Index() action method, and submitting within a [HttpPost]Index() action method.
But since I'm using ForMethod.Get in the View as this method is used for searching functionality, I can only use a [HttpGet] Index action method.
View Model (refactored for simplicity):
public class IndexVM
{
// DropDownLists
public IEnumerable<SelectListItem> DDLForProperty1 { get; set; }
public IEnumerable<SelectListItem> DDLForProperty2 { get; set; }
public IEnumerable<SelectListItem> DDLForProperty3 { get; set; }
[Required]
public int? Property1 { get; set; }
public int? Property2 { get; set; }
public int? Property3 { get; set; }
public bool vmclickedSearchButton { get; set; }
}
Note:
The view model is very simple. It contains drop down lists, selected properties for the DDLs, and a validation rule on one of the properties.
Adding a constructor to the view model and initialising the property workaround:
public IndexVM()
{
this.Property1 = 0;
}
The problem is that you are sending an invalid model to your view.
The Property1 in your model is being required and is a nullable int. This does not explain why validation executes, but why the model is invalid.
Your action method is executing the validation during initial load. Model binding will execute validation regardless of http method (get or post).
Since you are requiring Property1 (int?) to NOT be null, by definition your model becomes invalid when it is instantiated. There are several ways to handle this (not sure which is most appropriate though)
Create separate methods for HttpGet and HttpPost in your controller. Do not implement binding for HttpGet.
Use a default value (as you have done already)
Modify the model so that Property1 is not nullable (i.e. int).
I'm new to asp.net mvc. Basically i'm from php programmer. In my php file i can display what are all the values coming from html page or form using echo $_POST; or print_r($_POST); or var_dump($_POST). But in asp.net how can i achieve this to check what are all the values are coming from UI Page to controller.
You may take a look at the Request.Form property:
public ActionResult SomeAction()
{
var values = Request.Form;
...
}
You could put a breakpoint and analyze the values. Or simply use a javascript development toolbar in your browser (such as FireBug or Chrome Developer Toolbar) to see exactly what gets sent to the server.
But normally you are not supposed to directly access the raw values. In ASP.NET MVC there's a model binder which could instantiate some model based on the values sent to the server.
For example you could have the following model:
public class MyViewModel
{
public int Age { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
and then have your controller action take this model as parameter:
public ActionResult SomeAction(MyViewModel model)
{
... you could use the model properties here
}
and now you could invoke this controller action either wityh a GET request passing the parameters in the query string (/someaction?age=10&firstname=foo&lastname=bar) or using a POST and sending them in the body.
You can check the raw data via Request.Form.
But this is not he spirit of the ASP.NET MVC. It is preferd that you expect a model into your controller. You have all type safety mapping already done by special module called model binder.
So unless you work on some special case, you just add a model to the controller action:
public ActionResult SomeAction(SomeModel model)
{
//Handle SomeModel data further ...
}
You can create an action which will accept the parameters from the UI page like the following:
[HttpPost]
public ActionResult SomeAction(string param1, int param2)
{
//Now you can access the values here
}
or make an action which will accept the model
public ActionResult SomeAction(SomeModel model)
{
//Access the model here
}
Has anyone successfully bound 2 textboxes to one DateTime property using the model binding in MVC, I tried Scott's method http://www.hanselman.com/blog/SplittingDateTimeUnitTestingASPNETMVCCustomModelBinders.aspx but was dissatisfied as this stops the HTML fields and Model properties having the same name (so the validation could not set the correct css if it failed).
My current attempt modifies this by removing the ValueProviderResult object from the bindingcontext and the adding a new one for the key made up from the date result and a tiem (using the .Time convention in Scotts post) but I am a little wary of messing around with the bindingContext object direct.
The idea is that i can use IDateErrorInfo and VAB PropertyComparisonValidator to compare 2 datetimes on the model where one needs to be later than the other, to do this the time element needs to be included.
I use a different approach and go for two different sets of models: My view model would have two properties and the validation for those fields, while my domain model would have one DateTime. Then after the binding, I let the view model update the domain:
public ActionResult Update(DateInput date)
{
if(date.IsValid)
{
var domain = someRepository.GetDomainObject(); // not exactly, but you get the idea.
date.Update(domain);
}
// ...
}
public class DateInput
{
public string Date { get; set; }
public string Time { get; set; }
public void Update(DomainObject domain) { ... }
}
public class DomainObject
{
public DateTime SomePointInTime { get; set; }
}