I have a very simple issue. Below is my JSP code , where I display multiple check boxes in rows. By default all checkbox are checked, and if user wants to ignore certain row, he unchecks that row's checkbox.
My issue is, if a row's checkbox is unchecked and because of some other fields, if Struts throws a form validation error on screen, the unselected checkbox is displaying back as checked. Am I missing something? If no struts validtion, then everything looks correct.
<c:forEach var="map" items="${form.displayList}" varStatus="index">
<html-el:checkbox styleId="someCheckbox_${index.index}" property="someCheckboxes[${index.index}]" />
<td> .....</td>
<td> .....</td>
<td> .....</td>
</c:forEach>
When you submit data to Struts the following things happen:
Struts has a front controller and all request are going through this controller. This is the ActionServlet class which then based on what was configured in struts-config.xml passes the request to the appropriate Action class.
before that happens, an ActionForm instance is constructed or reused if it's a session saved form;
reset() is called on the ActionForm. This is where you reset checkbox properties to false. This is needed because unchecked HTML checkboxes are not sent on the request when you submit, so Struts does no request data binding for them (if they were checked when you loaded the page they remain checked even if the user unchecked them on submit);
the ActionForm is populated with the request data (any checked checkboxes are now checked, the ones who were not sent on the submit remain unchecked because of the previous reset);
the ActionForm's validate() method is called if so specified in struts-config.xml;
if validation fails, the control is returned to the input JSP if so specified in struts-config.xml (the submitted data is what the page will display because it's on the ActionForm);
if instead validation is successful, the execute() method on the Action class is called;
Action class does it's job and forwards to some JSP.
Is this how you are using Struts? My guess is you are doing some populating/resetting/validations in the Action class and when validation fails you reload the ActionForm with the default data.
Check your ActionForm for the reset() and validate() code and your Action class for execute() and see where the data is getting checked back.
Related
I am learning the "html fragments over the wire" approach of htmx.
I have a "parent" tag in my HTML page which contains a child which is a form.
Case1: If the child form gets submitted and is not valid, then I want to re-display the child form (hx-swap="outerHTML"). This works fine.
Case2: if the child form gets submitted and is valid, then I want to re-fresh the parent.
I had a look at hx-swap-oob, but this needs to be an attribute of the HTML send from the serve to the client.
This is possible, but it is a bit dirty to get the attribute into the parent tag.
Is there a way to trigger something like oob, but why http-header?
Depending on the rest of your website, I'd suggest using the hx-trigger response header to trigger a new event on your website when your form is posted successfully. You can use htmx, hyperscript, javascript, or anything else listen for this event and take the necessary action.
I am trying to verify a page based on who logs in. Certain users have more security and will see more items on the page. When a user with lower security logs in, they will not see options. I need to verify those options are not present on the page.
I have a page object for the option that displays based on user security
testObject {$("#test")}
I have tried using isDisplayed()
boolean hidden = testObject.isDisplayed()
assert !hidden
But I keep getting an error that says
geb.error.RequiredPageContentNotPresent: The required page content
geb.navigator.EmptyNavigator' is not present
The error message is what I want to verify. The object is not present, and I need to verify that is true and pass the test.
You want to use the required option for your content element.
If the page is dynamic, maybe you also want to wait for a while before you let Geb decide that the element is empty. Checks for non-existence of elements can be tricky, because they could just pass because a dynamic element has not finished loading yet.
testObject(required: false, wait: 2) { $("#test") }
In your Geb test you just do this (no helper method needed):
given:
def page = to MyPage
expect:
page.testObject.empty
The problem is that i have a remote form that, based on condition, id like to convert to a non-remote form (using UJS), and then submit.
note the form has a file upload.
Here's the details: I have initially rendered the remote form using
= form_for #myobj, :url => {:action=>"remoteAction", :controller=>"myobjects"}, :remote => true do |f|
... (f.fields....)
which produces the HTML:
<form id="new_myobj" class="new_myobj" method="post" accept-charset="UTF-8" data-remote="true" action="/remoteAction">
when i click submit, as expected, the form is submitted 'AS JS'.
in the controller action, i am doing some validation of the fields inside the submitted form.
If all the validations pass, i execute the following .js.haml template:
$('form#new_myobj').removeAttr("data-remote");
$('form#new_myobj').attr('enctype', 'multipart/form-data');
$('form#new_myobj').attr('action', '/myobjects/regularAction');
which successfully changes the HTML on the page (witnessed via Firebug) to:
<form id="new_myobj" class="new_myobj" method="post" accept-charset="UTF-8" enctype="multipart/form-data" action="/myobjects/regularAction">
since the form contains an f.file_field, i have to submit as multipart so the image can be uploaded, and i cannot submit 'AS JS'
now, when i click submit, the controller action 'regularAction' is indeed called, but its still 'AS JS'
the question is, what else do i need to change in the HTML so the form can be submitted non-xhr? is it related to the headers?
jQuery is a bit tricky with the data attributes since it both
reads the HTML5 data tags as well as its own storage bound to the
DOM element, that is also called data. When writing to an attribute
that value gets copied into jQuerys own data storage (presumably
when data("remote") is being called).
However, this only happens
if jQuery’s data is empty for that name. Thus setting the attribute will only work once, after that the "cached" value is being used
even if the attribute changes. In order to really get rid of the
value, we need to remove the attribute and jQuerys own storage
method in that order. The reason is that there’s a high-level
(element.removeData(…)) function and a low level one (jQuery.
removeData(element, …)). The former re-reads the HTML5 data
attribute and stores it in jQuery’s own storage. Using the rather
unusual low level function obviously works as well.
Also, we do really need to remove the attribute -- setting it to
false is not enough since Rails only checks if form.data('remote')
is not undefined (look for it in jquery_ujs.js).
TL;DR:
attr("data-remote") != data("remote")
These two lines make a form non-remote (again). Order matters.
$("form").removeAttr("data-remote");
$("form").removeData("remote");
It’s documented, if you actually know what you’re looking for:
http://api.jquery.com/jQuery.data/ (low level function)
http://blog.madebydna.com/all/code/2011/12/05/ajax-in-rails-3.html
StackOverflow doesn’t allow me to post more than two links, but you can guess the removeData one. The high-level functions are linked from the low level ones.
Avoiding the token authenticity error in Rails 4+:
As Stan commented below, just doing the above will fail with an InvalidAuthenticityToken error. The workaround is easy though, see here for details: https://stackoverflow.com/a/19858504/1684530
The problem is that your approach to disable the Ajax submission isn't quite correct. You need to unbind the JavaScript events that have already been added by rails.js (Rails UJS adapter) to the form.
You can do that by:
$('form#new_myobj').unbind() to unbind all events attached to the form. You also need to $('form#new_myobj').removeAttr('data-remote') and $('form#new_myobj').removeAttr('data-type') to remove data-remote and data-type attributes (if existent).
I'm using MonoRail and was wondering how it decides when to use client-side vs. server-side validation? In my model class I have [ValidateNonEmpty] on two properties, one is a textbox, the other is a dropdown. The textbox triggers client-side validation on form submission, if I leave the dropdown empty though it posts back to the server and returns back the validation error from server-side. Is there a way to get the dropdown to trigger client-side validation? Also it's odd because after the postback, it clears what I had entered in the dropdown but maintains the state of the textbox (viewstate anyone??)
Thanks,
Justin
It viewed source and I saw that it was using jQuery for the client-side validation. It had:
"business.businesstype.id":{ required: "This is a required field" },
for the dropdown, which wasn't working. I noticed that it was using 0 as the default dropdown value so I manually put in firstoptionvalue and that got it working:
$FormHelper.Select("business.businesstype.parent.id", $businessTypes, "%{value='id', text='name', firstoption='Select a Business Type', firstoptionvalue=''}")
I have a j2ee application running on weblogic. I was confused with my multibox.
What I know of multibox is that the checked items will be passed as an array of strings on submit.
I don`t know why in my application it works fine when i uncheck a checkbox or more, as long as a single box remains checked but when I uncheck everything, the submitted array is the array of the previously checked multiboxes when it was supposed to be empty.
Can you help me please?
Are you familiar with the reset() method on the ActionForm class?
The purpose in life for this method is to reset checkboxes. If you have a checked checkbox in your form and you submit it, that checkbox will be on the request. If the checkbox is unchecked nothing will be sent on the request for it (a GET submit is a simple way to observe this behavior).
When Struts performs the request bind, it matches by name the parameters from the request to the parameters in the form. That is, if there is something to match.
Now consider these steps:
I have a boolean field on the ActionForm;
I also have a matching checkbox in the form;
I submit the form => Struts binds the request, so now my property is true in the ActionForm;
I uncheck the checkbox in the form and submit again => nothing is sent on the request for the checkbox => Struts has nothing to bind = > your field remains true on the ActionForm;
The above applies for multi checkboxes, but you get an array instead of just one value.
Enter the reset() method. This is called by Struts before binding the request. Here you can set your field value to false. If it arrives in the request Struts will replace it with true => OK. If it does not arrive on the request (because it's unchecked) the value will remain false = > OK again.
The same goes for multiboxes. You have to reset the list of values from the ActionForm by reducing the array to zero length (but not null).
If your ActionForm has a request scope, it usualy does not matter because the object is recreated at each request. But for a session scoped ActionForm with checkboxes, reset() is a must.