Grails hasErrors method with ternary operator? - grails

I'm developing application using Grails framework and I'm having problems with hasErrors when invoked as a method from a gsp view.
I have a form that get's populated by values from a database (default values). Those values are stored in a session object. Users can edit values in form fields and send results back to the database. Before data gets persisted I have a command object that validates data. If there are errors command objects renders view with the same form and errors highlighted.
What I'm trying to do is to have form fields populated by values stored in a session object unless there are errors from command object. In that case field(s) should be populated by the wrong values entered by the user.
Here's the code snippet:
<g:textField name="somename" id="someid" value="${hasErrors(bean: commandobject, field: 'somename') ? fieldValue(bean: commandobject, field: 'somename') : session.somevalue}" />
Problem with above code is that no matter the value entered in the field, whether be right or wrong, field always ends up with the value from the session object. Is there a solution to this or am I doing something wrong in the first place?

When you call hasErrors like that you're invoking the <g:hasErrors/> tag which doesn't return a boolean value - it looks at the condition and conditionally invokes the tag body. Under that description, it makes sense why it's behaving the way it is.
What I'd recommend is to create your own TagLib and use commandobject.errors.hasFieldErrors('somename') [docs] in your condition (to get the boolean value you're looking for).

The hasErrors as a method call in GSP works a bit differently than as a tag <g:hasErrors>. The former is intended to set CSS class in divs or spans etc...
e.g. <div class="prop ${hasErrors(bean:parent, field:'child.name', 'errors')}">
where errors is the CSS class name. So if you don't specify the output string, it seems to return false by default, so to workaround your case, return '1'. So your code should look like:
<g:textField name="somename" id="someid" value="${hasErrors(bean: commandobject, field: 'somename', '1') ? fieldValue(bean: commandobject, field: 'somename') : session.somevalue}" />
This will also work with logical operations in <g:if>

Related

Thymeleaf - Add attribute without value

In Thymeleaf I would like to generate following HTML
<span data-my-attr>
</span>
I would like to display data-my-attr conditionally, but there seems to be no way how to display or NOT to display an empty attribute conditionally.
In case of required attribute there is th:required but for custom attributes there is nothing.
I tried to use th:attr="'data-my-attr'=${value}" and value is true or false, but it does not work.
Let's assume condition is true when your attribute should be shown and false when it shouldn't. You can have following:
<span data-my-attr th:attr="${condition} ? 'meaningless' : 'data-my-attr'=''"></span>
Explanation:
According to this thread when you specify the empty value for an attribute within th:attr then Thymeleaf will delete this attribute. So in above snippet:
data-my-attr is added by default.
The empty value is assigned to an attribute using th:attr.
The name of the attribute overriden with empty value is selected accordingly to ${condition}.
When ${condition} is true then data-my-attr should stay so any meaningless name (not present within the tag) should be picked.
Otherwise data-my-attr should be deleted so this name is picked.
Feels hacky but such a way of attribute removal seems working since 2012. Therefore I'd consider it stable.

Thymeleaf th:field model evaluation

I'm including dynamic content to a view using a custom Thymeleaf attribute processor that simply adds additional nodes while processing the attribute itself.
The code I use is very similar to the one below:
final Template template = arguments.getTemplateRepository().getTemplate(
new TemplateProcessingParameters(arguments.getConfiguration(), "name-of-a-view", arguments.getContext()));
final List<Node> children = template.getDocument().getChildren();
// Add to the tree.
for (final Node node : children) {
element.addChild(node);
}
This works fine, but breaks when the included nodes contains forms that use th:object and th:field.
I put the model I need inside the node variable map and in fact th:object does find and retrieves the object, but th:field does not seems to care and breaks with a
Neither BindingResult nor plain target object for bean name 'model' available as request attribute
From my understanding (step-by-step debugging), it seems to me that th:field only search for the model in the request context.
Am I missing something here?
Thank you in advance.
No, you're spot on. I'm still not sure why the binding is different for th:field than other th: attributes, but it definitely works differently. Essentially, you can't use th:field unless your th:object is on the model. The workaround is to stop using th:field and just specify your input attributes manually, like:
<form action="#" th:action="#{/process}" th:object="${objectFromList}" method="post">
<input type="text" id="fieldName" name="fieldName" th:value="*{fieldName}" />
</form>
I realize this post is old. Hopefully, this will help someone who is running into this quirk.

How to serialize the checkbox in a form into Json data

We know that in MVC, a CheckBoxFor will generate a checkbox with a value="true" and a hidden with a value=false. Both input controls will share the same name.
It is very reasonable because the form will be able to POST a false value if the box is unchecked. And the model binder will ignore the hidden input when the checkbox return a true.
But now i have overridden the form submit event in order to send the form data into a WebAPI controller in JSON format.
When serializing the form data, there is no mechanism to parse the relationship between the checkbox and the hidden correctly. Therefore, when unchecked, it returns a false, which is okay. But when checked, it returns a {true, false} instead of true, because the serializeArray() function goes through every input and find two values goes to a same name.
The question is: What is the best way to correct it?
My solution to this problem was to write my own HtmlHelper method that renders a single <input type="checkbox" /> tag. Any other solution just seemed too hacky.
You can use dotPeek or .NET Reflector to look at how the Microsoft Team created the HtmlHelper.CheckboxFor method if you need any help accomplishing that task.
The 2 tag approach was taken to prevent MVC action parameters from throwing an exception when a "bool" parameter did not have a matching parameter sent to the controller (an unchecked checkbox doesn't send any value).

Misbehaving Asp.net MVC helper methods (ie: Html.TextBox() and Html.Hidden())

I've been trying to debug an issue and I pinned pointed it down to this scenario:
When the statement Html.TextBox("ID", "What the heck..") is executed, I expect it to render:
<input id="ID" name="ID" type="text" value="What the heck.." />
But I get a Guid as its TextBox value such as so:
<input id="ID" name="ID" type="text" value="2e369d2c-071d-4733-8382-cc9e77d0b912" />
Why is Asp.net MVC outputting Guids? I'm not overriding asp.net mvc's framework methods. Please refer to the screenshot.
Update:
Here's another screenshot using Html.Hidden() instead of Html.TextBox(). I couldn't use Html.HiddenFor() directly into the Watch window because HiddenFor() uses lambdas.
#Eric Petroelje and #TLS: You two are correct. TextBox() and Hidden() is retrieving ID's value from the POST variables and not from the current Model or the function's value parameter. Though, I've expected different from HiddenFor() and TextBoxFor(). I expected it to get its value from the POST'ed variables only if it cant get it from the current Model. How can I achieve this?
Maybe you have a POST variable named ID that is a GUID? If that's the case, the HTML Helper method will use that POST value. If no POST value is present, it will fall back to the value in the model.
You are correct that Html.TextBox("ID", "What the heck..") is expected to output the attribute values that you give in your first example; however, if you are using the Html.TextboxFor method, then you're using a MVC Helper that dynamically loads the value of the ID property and places that into the value attribute in the HTML. When you use the Html.TextboxFor method, your second example is the expected HTML output if your ID property is a Guid.

How can I get Html.CheckBox() as boolean during POST in custom IModelBinder?

I am using Html.CheckBox(). The resulting HTML is:
<input id="IsMultiGraph" name="IsMultiGraph" value="true" type="checkbox">
<input name="IsMultiGraph" value="false" type="hidden">
On the server I have an Action which is accepting the form post information and using a custom IModelBinder to bind the form results to one of my models. Here is a snippet of the code I am running in the IModelBinder:
bool isMultiGraph;
if (!bool.TryParse(bindingContext.HttpContext.Request["IsMultiGraph"], out isMultiGraph))
bindingContext.ModelState.AddModelError("IsMultiGraph", "Invalid boolean for \"IsMultiGraph\""); //this should not ever happen unless someone is programatically posting
result.IsMultiGraph = isMultiGraph;
The problem is that since Html.CheckBox() is rendering a checkbox as well as a hidden input field if I change the state of the textbox the postback value is doubled (ie. "true,false").
I understand why this is done and I'm looking for the best way to parse the current value of the CheckBox during a postback (checked = true, unchecked = false). Is there another helper method in MVC for this or should I just write my own?
One way is to use the GetValues method of the NameValueCollection class in order to get the first value of the array property like this:
bindingContext.HttpContext.Request.Form.GetValues("IsMultiGraph")[0]

Resources