I'm writing an update user-data form in Grails. It must show an old user data in the fields before update. This is a part of this form:
<g:form class="form-signin" controller="VisitorSpace">
<label for="login" class="sr-only">Login</label>
<g:textField id="login" class="form-control" name="login" placeholder="Login"
value="${applicationContext.springSecurityService.principal.username}" required="" autofocus=""/>
<label for="firstName" class="sr-only">Your name</label>
<g:textField id="firstName" class="form-control" name="firstName" placeholder="Your name"
value="${applicationContext.springSecurityService.principal.firstName}" required="" type="text"/>
...
</g:form>
This is a part of my domain User-class:
class Person {
transient springSecurityService
String username
String password
boolean enabled = true
boolean accountExpired
boolean accountLocked
boolean passwordExpired
String firstName
String lastName
String email
boolean isAdminCafee = false
static transients = ['springSecurityService']
static constraints = {
username blank: false, unique: true
firstName blank: false
lastName blank: false
password blank: false
email blank: false, unique: true
}
...
}
Generated username-property of the domain class is getting correctly, but while I trying request ungenerated firstName-property I get an error:
URI:/restorator/visitorSpace/editPrivateDataClass:groovy.lang.MissingPropertyExceptionMessage:
No such property: firstName for class: grails.plugin.springsecurity.userdetails.GrailsUser
Solved. I've solved this problem via rendering gsp with parameters.
Render-action of gsp-controller:
def editPrivateData(){
def user = springSecurityService.currentUser
render (view:'editPrivateData.gsp', model: [user: user])
}
Using in gsp-form:
<g:textField id="firstName" class="form-control" name="firstName" placeholder="Your name" var = "user" value="${user.firstName}" required="" type="text"/>
Related
I have a bit issue related to binding result thymeleaf validation.
on form submit with empty string binding result is not validate.
please you help me to check and advise me what should I fix this issue.
Thank!!
UserDTO
#Entity
public class UserDTO {
#Id
#NotEmpty(message = "First Name is required!!")
private String firstName;
#NotEmpty(message = "Last Name is required!!")
private String lastName;
#NotEmpty(message = "Username is required!!")
private String username;
#NotEmpty(message = "Password is required!!")
private String password;
#NotEmpty(message = "Confirm password is required!!")
private String confirmPassword;
#NotEmpty(message = "Role is required!!")
private String authentication;
//setter getter
Controller
#PostMapping(value = "/addUser")
public String saveUser(#Valid #ModelAttribute("userDTO") UserDTO userDTO, BindingResult
bindingResult, ModelMap model) {
if (bindingResult.hasErrors()) {
return "createUser";
}
User userDb = userRepo.findUserByUsername(userDTO.getUsername());
if (userDb != null) {
throw new RuntimeException("User already exist.");
CreateUser.html
<form th:action="#{/addUser}" class="form-horizontal"
novalidate="novalidate" th:object="${userDTO}" method="post">
<div class="form-body">
<div class="form-group">
<label class="control-label col-md-3">Fist Name
<span class="required" aria-required="true"> * </span></label>
<div class="col-md-4">
<input type="text" th:field="*{firstName}" th:value="null"
data-required="1" class="form-control">
<span th:if="${#fields.hasErrors('firstName')}" th:errors="*{firstName}">First Name is required!</span>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-3">Last Name
<span class="required" aria-required="true"> * </span>
</label>
<div class="col-md-4">
<input type="text" th:field="*{lastName}"
data-required="1" class="form-control">
<span th:if="${#fields.hasErrors('lastName')}" th:errors="*{lastName}">Last Name is required!</span>
</div>
</div>
I have a simple model and view. THough, the modelbinder seems to fail when trying to bind my model because I always receive NULL into my controller action. What am I doing wrong?
Razor code:
#model BikeSharing.Views.Shared.Widgets.Popups.LoginInputModel
#using (Ajax.BeginForm("Login",null, new AjaxOptions
{
UpdateTargetId = "login-partial-update",
HttpMethod = "POST"
}, new { id = "js-form-login" }))
{
#Html.TextBoxFor(x => x.Email, new {placeholder = "Email address"})
<div class="errormessage">
#Html.ValidationMessageFor(x=>x.Email)
</div>
#Html.PasswordFor(x => x.Password, new {placeholder = "Password"})
<div class="errormessage">
#Html.ValidationMessageFor(x => x.Password)
</div>
}
Controller Action:
[HttpPost]
public ActionResult Login(LoginInputModel lmod)
{
if (ModelState.IsValid)
{
// this code is never reached because lmod is always NULL
}
return PartialView("Widgets/Popups/_LoginInput", lmod);
}
Model code:
public class LoginInputModel
{
[Required(ErrorMessage = "Your email address is required.")]
[EmailAddress]
public string Email { get; private set; }
[Required(ErrorMessage = "Please provide your password.")]
[MinLength(6,ErrorMessage = "Your password is too short.")]
[MaxLength(50, ErrorMessage = "Your password is too long.")]
public string Password { get; private set; }
public LoginInputModel()
{
}
public LoginInputModel(string email, string password)
{
Email = email;
Password = password;
}
}
The form submit is done via jquery-unobtrusive-ajax and Ajax.BeginForm()
I am only firing it via $('#js-form-login').submit();
Rendered HTML in browser:
<form action="/Home/Login" data-ajax="true" data-ajax-method="POST" data-ajax-mode="replace" data-ajax-update="#login-partial-update" id="js-form-login" method="post" novalidate="novalidate">
<input data-val="true" data-val-email="The Email field is not a valid e-mail address." data-val-required="Your email address is required." id="Email" name="Email" placeholder="Email address" type="text" value="">
<div class="errormessage">
<span class="field-validation-valid" data-valmsg-for="Email" data-valmsg-replace="true"></span>
</div>
<input data-val="true" data-val-maxlength="Your password is too long." data-val-maxlength-max="50" data-val-minlength="Your password is too short." data-val-minlength-min="6" data-val-required="Please provide your password." id="Password" name="Password" placeholder="Password" type="password">
<div class="errormessage">
<span class="field-validation-valid" data-valmsg-for="Password" data-valmsg-replace="true"></span>
</div>
</form>
Try adding the FormBody prefix to hint to the ModelBinder to look in the POST body.
[HttpPost]
public ActionResult Login([FromBody]LoginInputModel lmod)
{
if (ModelState.IsValid)
{
// this code is never reached because lmod is always NULL
}
return PartialView("Widgets/Popups/_LoginInput", lmod);
}
I have the following domain class:
class User {
String name
String contactName
String primaryEmail
String url
String phoneNumber
String address
static hasMany = [users: User]
static constraints = {
name blank: false
contactName blank: false
primaryEmail email: true
url blank: false
phoneNumber blank: false
address blank: false
}
}
And controller for the User:
class UserController {
def create() {
User user = new User()
[user: user]
}
def save(User user) {
if (!user.save(flush: true)) {
render (view : 'create', model: [user: user])
}
redirect action: 'create'
}
}
I want show validation errors in case if validation fails. My create.gsp looks like this:
<body>
<g:form action="save" >
<g:renderErrors bean="${user}"/>
<g:textField name="user.name" id="message" value="${user.name}"/>
<g:textField name="user.contactName" id="contactName" value="${user.contactName}"/>
<g:textField name="user.primaryEmail" id="primaryEmail" value="${user.primaryEmail}"/>
<g:textField name="user.url" id="url" value="${user.url}"/>
<g:textField name="user.phoneNumber" id="phoneNumber" value="${user.phoneNumber}"/>
<g:textField name="user.address" id="address" value="${user.address}"/>
<g:submitButton name="submit" value="Save"/>
</g:form>
</body>
</html>
But after sumbit of create.gsp with invalid data two strange thing happen
1) Despite the fact that all fields have value property mapped to some field of User bean all fields are empty
2) There are no validation errors on the page
What I'm doing wrong?
Thank you!
you must return after calling render() or use else
def save(User user) {
if (!user.save(flush: true)) {
render (view : 'create', model: [user: user])
return // either return here
}else // or else here
redirect action: 'create'
}
In your original code you redirect to create and pass no models into it
I want to bind a boolean property to a hidden input controller, but the output html code was error
code as follows:
public class TestModel
{
public bool IsOk { get; set; }
public bool IsSuccess { get; set; }
}
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new TestModel { IsOk = false, IsSuccess = true });
}
}
<h2>Index</h2>
<p>#Model.IsOk</p>
<p>
<input type="hidden" value="#Model.IsOk" />
</p>
<p>
<input type="hidden" value="#Model.IsSuccess" />
</p>
Html Output
<h2>Index</h2>
<p>False</p> //works
<p>
<input type="hidden" /> //where is value?
</p>
<p>
<input type="hidden" value="value" /> //wath's this?
</p>
But if i use ToString(), all above works well, so is it my mistake?
In HTML when you have an attribute which functions as an on/off or true/false switch you remove the attribute when the attribute is off/false and add the attribute with the same value as the attribute name when the attribute is on/true. Razor provides you with that functionality as you have already experienced.
Perhaps you intend to use Html.HiddenFor in the view?
<p>
#Html.HiddenFor(m => m.IsOk)
</p>
<p>
#Html.HiddenFor(m => m.IsSuccess)
</p>
This will produce this HTML where you have value="False" and value="True" as you expect:
<p>
<input data-val="true" data-val-required="The IsOk field is required."
id="IsOk" name="IsOk" type="hidden" value="False" />
</p>
<p>
<input data-val="true" data-val-required="The IsSuccess field is required."
id="IsSuccess" name="IsSuccess" type="hidden" value="True" />
</p>
Also, the model binder will be able to round-trip you view model properties.
Html attributes requires string objects
It's not automatically converted
So you have to use ToString()
Please try this.
$('#controlId').is(":checked");
This is the model with it's validation:
[MetadataType(typeof(TagValidation))]
public partial class Tag
{
}
public class TagValidation
{
[Editable(false)]
[HiddenInput(DisplayValue = false)]
public int TagId { get; set; }
[Required]
[StringLength(20)]
[DataType(DataType.Text)]
public string Name { get; set; }
//...
}
Here is the view:
<h2>Create</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Tag</legend>
<div>#Html.EditorForModel()</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
And here is what get's renderd:
<form action="/Tag/Create" method="post">
<fieldset>
<legend>Tag</legend>
<div><input data-val="true" data-val-number="The field TagId must be a number." data-val-required="The TagId field is required." id="TagId" name="TagId" type="hidden" value="" />
<div class="editor-label"><label for="Name">Name</label></div>
<div class="editor-field"><input class="text-box single-line" data-val="true" data-val-length="The field Name must be a string with a maximum length of 20." data-val-length-max="20" data-val-required="The Name field is required." id="Name" name="Name" type="text" value="" /> <span class="field-validation-valid" data-valmsg-for="Name" data-valmsg-replace="true"></span></div>
...
</fieldset>
</form>
The problem is that TagId validation gets generated althoug thare is no Required attribute set on TagId property. Because of that I can't even pass the client-side validation in order to create new Tag in db.
What am I missing?
I found the answer. Just add this to Application_Start:
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
Make the view-model value-types nullable. Then they won't be Required by default.
Note also if you put the attribute 'required="false"' in html 5 (if you set html 5 in your doctype meta data), it will see "required" and make it required. You can use dojo-data-props="required:false".
frennky's solution only removed data-val-required but in my case I still had data-val-number and data-val
I had to add the two lines below to Application_Start to get rid of everything.
ModelValidatorProviders.Providers.Clear();
ModelValidatorProviders.Providers.Add(new DataAnnotationsModelValidatorProvider());
The problem is that the value of the hidden field is empty. This shouldn't happen if you use integer type. I suppose that the TagId property is defined as a nullable type in the Tag class. So either assign it a value before rendering the view or use an integer type:
[MetadataType(typeof(TagValidation))]
public partial class Tag
{
public int TagId { get; set; }
public string Name { get; set; }
}
so that the generated hidden field looks like this:
<input
data-val="true"
data-val-number="The field TagId must be a number."
data-val-required="The TagId field is required."
id="TagId"
name="TagId"
type="hidden"
value="0"
/>
Also normally client side validation shouldn't be triggered for this hidden field.
jquery validate target cheking "disabled" html attribute.
$(function () {
$("#TagId").attr("disabled", "disabled")
});
or use Nullable.
hope this code!
With MVC4 you can also use this:
#{ Html.EnableClientValidation(false); }
#Html.EditorForModel()
#{ Html.EnableClientValidation(true); }
Make your Model or View-Model property value-types "nullabel". This will solve your problem.One important thing that remove "required" attribute from your tag otherwise it will take i "required"
Example:-
public class ViewModle
{
public int? foo{get;set;}
}
Here in example foo is integer nullable type, this will no longer required in mvc.
Hope this will help you.
I had the unfortunate experience that my model attributes were suddenly required causing my web APIs to return 400 errors when attributes were missing from web requests.
I found out this was caused by an accidental change of the "Nullable" setting in the project (Project properties -> Build -> Nullable: Enable"
After changing Nullable to Disable, all was good again.
I found a more detailed explanation of the issue here:
https://learn.microsoft.com/en-us/dotnet/csharp/nullable-references#nullable-contexts