Ok guys i have a question, if this is my Form , and is generated for every item in the DB, i want to send the item with the quantity specified.
to send the quantity , from this razor view
#using (Html.BeginForm("AddToCart", "Prices"))
{
string qtname = "qt" + #item.id;
<div>
<input id="#qtname" name="#qtname" class="quantity" type="text" value="0" readonly="readonly" />
</div>
<input type="submit" value="Adauga" class="addToCart" />`
}
i need just these?
[HttpPost]
public ActionResult AddToCart(ProductsModel Products, string qtname)
{ }
and do i need some html.hidden for passing along the item.id too?
In the form you are sending only the qtname parameter as input field, whereas your controller action also expects a ProductsModel parameter which is never sent. If you want to bind it you will have to create input fields for all properties of this view model.
But in your case a better solution would be to simply include the id of the product as hidden field and then fetch the corresponding product from your datastore given this id:
[HttpPost]
public ActionResult AddToCart(string id, string qtname)
{
ProductsModel products = _repository.GetProduct(id);
...
}
Related
I am writing a simple play framework application with authentication where in passwords are hashed using BCrypt.
In simple words, my question is this : I have observed that in JPA entities, when you call the get method of a property, its set method is also somehow called..This is a problem if i am doing a non-idempotent operation within the set method.
I will describe a simplified example of my problem here
This is my Login View
#helper.form(routes.Application.authenticate) {
<h3>Login</h3>
<ul id="login_box">
<li>
<span class="loginbox_label">Username : </span>
<input type="text" class="login_input" id="usernm-input" name="email" value='#form("email").value' placeholder="Enter Username..." />
</li>
<li>
<span class="loginbox_label">Password : </span>
<input type="password" class="login_input" id="passwd-input" name="password" placeholder="Enter Password..." />
</li>
<li>
#if(form.hasGlobalErrors) {
<p class="error">
#form.globalError.message
</p>
}
</li>
</ul>
<hr />
Retrieve Forgotten Password
<input type="submit" class="alt_btn login_btn" value="Login" />
<hr />
}
On Submit it goes to the authenticate function
#play.db.jpa.Transactional
public static Result authenticate(){
Form<Login> loginForm = form(Login.class).bindFromRequest();
if(loginForm.hasErrors())
{
return badRequest(index.render(loginForm));
}
else
{
//Save Email to session
session("email", loginForm.get().email);
//Redirect to dashboard
return redirect(routes.Application.showDashboard());
}
}
This is my Login Class
public static class Login
{
public String email;
public String password;
public String validate()
{
User usr = User.findByEmail(email);
if(usr == null)
return MessageStrings.USER_NOT_FOUND; //User Not Found
if(usr.getPassword().equals(this.password))
{
return MessageStrings.NO_ERROR; //NO ERROR
}
else
{
return usr.getPassword();//MessageStrings.PASSWORD_MISMATCH; //Wrong Password
}
}
}
As you can see,I am nowhere calling the usr.setPassword method. Also, I am returning the usr.getPassword() in the else part to return it and display it on the page instead of the error message.
Finally, this is my User Class, a JPA entity
#Transient
#Constraints.Required
#Formats.NonEmpty
private String password;
#Access(AccessType.PROPERTY)
#Column(name = "password")
public String getPassword(){
return this.password;
}
public void setPassword(String password){
this.password = password + "1";//BCrypt.hashpw(password, BCrypt.gensalt());
}
Link to the complete JPA entity : http://pastebin.com/HZDEyYHZ
I only added the relevant part of the code here. Now looking at the code, you would figure that each time I submit the form it must just return the stored password(because I return usr.getPassword() in the else part). Notice the this.password = password + "1";.
Now if i keep submitting with incorrect password..this is what i am getting
Clearly the setPassword is being repeatedly called whenever I submit the login form
Why is this happening?
A very lengthy post, I know
You explicitely chose property access for your entity. This means that the setters are called to populate your entity. But your setter is not really a setter. So rename this method to something else, and add a real setter, or use field access rather than property access.
IMHO, hashing the password should not be done by the entity, and even less by a setter. The expected behavior is that the getter should return the value that has been set by the setter.
What is the best way to change data of, for example, User?
I got lots of properties like 'username', 'city', 'phone' and when I want to edit just one field ('password'), I have to do this:
<form:form action="editUser.htm?id=${user.id}" commandname="user">
<form:hidden path='username' />
<form:hidden path='city' />
<form:hidden path='phone' />
<form:input path='password' />
....
In my controller action is defined:
#RequestMapping(value = "/editUser.htm", method=RequestMethod.POST)
public ModelAndView ordertypeedit(#ModelAttribute("user") User user,
BindingResult result, HttpServletRequest request)throws Exception{
userTypeValidator.validate(orderType, result);
if(result.hasErrors()){
(...)
return new ModelAndView(...);
}
orderTypeDAO.update(orderType);
return new ModelAndView(...);
I don't like to set all the to my action form... If I miss it, it shows error 'column 'username' cannot be null".
It depends if your properties are validated or mandatory on that form:
If not, then you don't need to submit them with your form:
<form:form action="editUser.htm?id=${user.id}" commandname="user">
<form:input path='password' />
and in your controller you will get user object with only user parameter.
Alternative way is to pass regular (not Spring MVC) form and create inside user object with only id and password properties.
you can use simple (without spring tags) form with one input and get it in controller using #RequestParam
I'm new to MVC!
I am trying to use two DropDownLists (Cities, Categories) in a PartialView that will be used in MasterPage, meaning they will be visble all the time.
I tried to load them in HomeCOntroller, but that didn't work. I got an Exception.
I read something about making a baseController that the other controllers will inherit from, I have tried that, kind of, but I guess i'm doing something wrong.
This is the only code I got today:
Masterpage
<% Html.RenderPartial("SearchForm"); %>
PartialView (SearchForm.ascx)
<% using (Html.BeginForm("Search", "Search")) { %>
<% } %> // dont know why I need two BeginForms, if I dont have this the other form won't trigger at all! Weird!
<% using (Html.BeginForm("Search", "Search", FormMethod.Get)) { %>
<%= Html.DropDownList("SearchForm.Category", new SelectList(ViewData["Categories"] as IEnumerable, "ID", "Name", "--All categories--")) %>
<%= Html.DropDownList("Search.City", Model.Cities, "--All cities--") %>
<input name="search" type="text" size="16" id="search" />
<input type="submit" id="test" title="Search" />
<% } %>
Two question:
Where and how to load the DropDownLists is the problem. I have tried to load it in the HomeController, but when go to another page then it says that the DDLs is empty and I get a Excecption.
Why do I have to use two forms for the ActionMethod to trigger ?
Hope anyone can help me out!
It sounds like you're only setting the property for a single action result. The Model.Cities data will have to be populated for every single view that needs to use it.
One solution would be to move the population of it to an ActionFilter
public class CityListAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext) {
var result = filterContext.Result as ViewResult;
result.ViewData.Model = //populate model
base.OnActionExecuted(filterContext);
}
}
and then add the filter to your controller
[CityList]
public class HomeController : Controller {
public ActionResult Index() {
return View();
}
}
As for the two forms issue, there should be no reason that i can think of that you need an empty form.
Take a look at the html that's being output and make sure it's ok. Also check the action is being generated correcly
Better way to do this, is to create something like MasterController and have action method on it like this:
[ChildActionOnly]
public ActionResult SearchForm()
{
//Get city data, category data etc., create SearchFormModel
return PartialView(model);
}
I recommend you create strongly typed view (SearchForms.ascx of type ViewUserControl<SearchFormModel>). Also it may be a good idea to have a model like this:
public class SearchViewModel
{
public IList<SelectListItem> Cities { get; set; }
public IList<SelectListItem> Categories { get; set; }
}
and use a helper like this: http://github.com/Necroskillz/NecroNetToolkit/blob/master/Source/NecroNet.Toolkit/Mvc/SelectHelper.cs to convert raw data to DDL friendly format beforehand.
In any case, you now use Html.RenderAction() instead of Html.RenderPartial() and specify you want "SearchForm" action from "MasterController".
Say I have a strongly-typed view of ViewPage<Song> and some additional fields in the form of create/edit scenarios that are not directly part of the Song entity (because I have to do some parsing before I fetch the composers from a foreign table and create the bridge entities SongComposers).
public ActionResult Create(Song song, string composers) {
if (ModelState.IsValid) {
// redirect to details
}
// return a view with errors
return View(song);
}
And in the view
<% using (Html.BeginForm()) { %>
<%= Html.EditorForModel() %>
<div class="editor-label"><label for="composers">Composers</label></div>
<div class="editor-field"><input id="composers" type="text" name="composers" /></div>
<% } %>
Now when I try to create a food and get validation errors, the previously entered form values for fields in Html.EditorForModel get filled properly but the composers field is blank after a submit. How can I fix this (ie. make it so that it "remembers" what the user wrote in the composers field)?
ViewData["composers"] = composers;
In my interface I have a list of text boxes, something like this :
http://screencast.com/t/YjIxNjUyNmU
The number of textboxes is unknown as each of them is associated with a Template.
In my page, the goal is to associate a number to some of those templates.
Here is a sample HTML code :
<% // loop on the templates
foreach(ITemplate template in templates)
{
// get the content from the input dictionary
int val;
content.TryGetValue(template.Id, out val);
// convert it as a string
string value = ((val > 0) ? val.ToString() : string.Empty);
// compute the element name/id (for dictionary binding)
string id = ??????????
string name = ??????????????
%>
<label for="<%= name %>"><%= template.Name %></label>
<input type="text" id="<%= id %>" name="<%= name %>" value="<%= value %>" />
<br />
<% }
%>
What I expect, in my controller, is to get a IDictionary where the first int is the template ID , and the other is the count given by the user.
Here is what I want :
public ActionResult Save(int? id, Dictionary<int, int> countByTemplate)
I tried a lot of things but nothing worked. I tried to read the sources but it's a maze, and I'm getting a headhache trying to get information about model binding.
Questions :
is there a good ressource on how the modelbinding works ?
I'd like someting exhaustive, I'm tired of the 84093043 blogs that talk about a given specific example.
how can I build my HTML, using to get a IDictionary (or even a IDictionary in my controller's action ?
Thanks a lot for your help
Information on how to write your input elements for binding to arrays, dictionaries, and other collections can be found at http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx.
Ok... Thanks to Levi I was able to get on a solution.
Not the cleaner one, but it works.
The HTML should be written this way:
<%
int counter = 0;
// loop on the templates
foreach(ITemplate template in templates)
{
// get the value as text
int val;
content.TryGetValue(template.Id, out val);
var value = ((val > 0) ? val.ToString() : string.Empty);
// compute the element name (for dictionary binding)
string id = "cbts_{0}".FormatMe(template.Id);
string dictKey = "cbts[{0}].Key".FormatMe(counter);
string dictValue = "cbts[{0}].Value".FormatMe(counter++);
%>
<input type="hidden" name="<%= dictKey %>" value="<%= template.Id %>" />
<input type="text" id="<%= id %>" name="<%= dictValue %>" value="<%= value %>" />
<label for="<%= id %>"><%= template.Name %></label>
<br />
<% }
%>
I had to add a hidden field to store the value.
I introduced a 'fake' counter just to loop over the dictionary the way ASP.Net MVC wants it.
As a result I got a dictionary filled with my values and '0' when the textbox is empty.
Another problem appeared: the ModelState was considered not valid because "a value is required". I don't want my values to be required, but looking at the modelbinder code, I did not found a way to tell the binder that a value is NOT required.
So I tricked the ModelState in my controller, removing all error, like this:
public ActionResult Save(int? id, Dictionary<int, int> cbts)
{
// clear all errors from the modelstate
foreach(var value in this.ModelState.Values)
value.Errors.Clear();
Well... I effectively got a solution, but the HTML is kind of ugly now, and counterintuitive (using an index to loop over a non-indexed collection ??).
And I need to trick each time I'll use this kind of binding to have it all work properly.
So I will now open a new post to make dictionary binder something better.
Here it is: ASP.Net MVC 2 - better ModelBinding for Dictionary<int, int>
EDIT - There is a cleaner solution, thanks to Pavel Chuchuva.
In the controller code, use a nullable int as value for the dictionary.
A bit more code to add, but much cleaner.
public ActionResult Save(int? id, Dictionary<int, int?> cbts)
{
// this is our final dictionary<int, int>
Dictionary<int, int> cbtsFinal = new Dictionary<int, int>();
// loop on the dicitonary with nullable values
foreach(var key in cbts.Keys)
{
// if we have a value
if(cbts[key].HasValue)
// then put it in the final dictionary
cbtsFinal.Add(key, cbts[key].Value);
}