I'm trying to add a single, simple client-only validation rule while using jQuery unobtrusive. My validation function is never called, the alert never fires. What am I doing wrong? I've stripped this down from a much more complex MVC application.
<html>
<head>
<script src="//ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.3.min.js"></script>
<script src="//ajax.aspnetcdn.com/ajax/jquery.validate/1.13.1/jquery.validate.js"></script>
<script src="//ajax.aspnetcdn.com/ajax/mvc/5.2.3/jquery.validate.unobtrusive.js"></script>
<script>
jQuery.validator.addMethod("mysplit", function () {
alert("in validator");
return false;
}, "Barf");
result = jQuery.validator.unobtrusive.adapters.addBool("mysplit");
</script>
</head>
<body>
<form method="post">
<input type="text" data-val="True", data-val-mysplit="error msg" />
<input type="submit" />
</form>
</body>
</html>
Not sure if this matters for a simple .htm file but in my web.config, everything looks correct:
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
<add key="ValidationSettings:UnobtrusiveValidationMode" value="none" />
I am still unable to decorate an HTML element to make it work with unobtrusive validation. It appears that if you don't have an adaptor for your custom rule, then manually adding the unobtrusive HTML attributes will not work. However I did discover that you can decorate elements to be validated directly by jQuery.validate (more or less skipping the unobtrusive validation). I don't know if manually adding jQuery.validation data attributes this way will conflict with the validation done by unobtrusive if you have, for example, a [Required] attribute on the element that gets converted to the "data-val" attributes by unobtrusive.
In short you can add a client validation method with the usual jQuery.validator.addMethod() then trigger it with the proper data-rule attributes (not data-val, which unobtrusive uses). For example to validate that an input field has the value "hello", I can add a rule:
jQuery.validator.addMethod("hello", function (value, element, params) {
return this.optional(element) || /^hello$/.test(value);
});
Note: don't add the error message param to addMethod, it will conflict with #Html.ValidationMessageFor() if you are using that.
Then manually decorate your HTML element like:
#Html.EditorFor(model => model.sendOn, new
{
htmlAttributes = new
{
data_rule_hello="true"
}
})
Any jQuery.validate will fire.
Note that trying to use data-rule-pattern="hello" just refused to work for me so I went the addMethod() route.
In my case rather that switch totally away from unobtrusive just because I can't validate this one input is a much worse option than this bit of a hack to do my custom validation of an input element or two here or there.
This article was key to exposing the data-rule magic.
I have yet to find out if this technique causes any serious problems in production but it seems to work for me.
I would also recommend that people consider writing a custom adaptor for their own model attributes if they need custom validation instead of a one-off hack like this.
Related
I'm blocked for couple of days on a JSF issue.
I have a web app where I create the page content quite dynamically from database data. Every page has several sections containing a form with h:commandButton (or a set of buttons).
Some forms work correctly and the form action method is launched as expected. Some other forms however don't work - the action method is not being called at all.
And I don't know why :-(
I know this response: action method is not called in JSF which lists conditions which must be fulfilled and I believe that everything is ok here, but it simply doesn't work for some forms...
Some points:
The problem is 100% repeatable
The same piece of XHTML is used for both successful and unsuccessful requests
The same action method (in the same bean) is being called for all forms
the console output differs in both cases
...RESTORE_VIEW phase is the same (my code logs seem to be equal)
...APPLY_REQUEST and few other phases are empty for the wrong case (only the final RENDER_RESPONSE phase is being executed
...APPLY_REQUEST and the following phases are not empty for the correct phase
(using ui:debug) Scoped variables / Request parameters contain ONLY vallues passed via f:param for the successfull case
Scoped variables / Request parameters contain however also formid, formid:action_name and an input box content for the UNsuccessfull case
the console shows absolutely no exception in any case
the correct request returns HTTP code 302 followed by another GET request with the target parameters (as build in the action method)
the incorrect request returns directly 200 (and no action is called)
when the JSF debug is switched on (javax.faces.level = ALL, com.sun.faces.level = ALL) still no exception is being shown, I see only couple of "javax.faces.component.UIComponentBase getRenderer\nFINE: No renderer-type for component j_idt171" messages and one "com.sun.faces.facelets.util.DevTools writeAttributes
FINEST: Error writing out attribute" followed by a NullPointerException - during RENDER_RESPONSE phase
So most probably there is a problem with restoring the view, but I have no idea why. The same XHTML block generates form and command button for both (successfull and unsuccessfull) cases (in a c:forEach loop).
But the strange think is also difference in the parameters in the correct case an in the wrong case...
Can anyone plase give me some directions what/where I should be looking for?
Thanks a lot in advance!
EDIT: some code...
This is the XHTML (unnecessary code cis cut)
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
template="/templates/base.xhtml">
<ui:define name="title">IS runtime</ui:define>
<ui:define name="menu">
<h:link value="Home" outcome="/index" /> | <h:link value="IS home"
outcome="/runtime">
<f:param name="env" value="#{param.env}" />
</h:link>
</ui:define>
<ui:define name="content">
<c:forEach var="pv" items="#{runtimeBean.pageViews}">
<div id="view_#{runtimeBean.ISViews[pv.view].code}"
class="view_#{runtimeBean.ISViews[pv.view].code}">
<h2>#{runtimeBean.ISViews[pv.view].code}</h2>
<h:form id="form_#{runtimeBean.ISViews[pv.view].code}">
<h:messages />
<c:if
test="#{runtimeBean.getEnvView(pv.view).type == 'RECORD_DETAIL'}">
<c:forEach var="item" items="#{runtimeBean.getViewItems(pv.view)}">
<h:outputText value="#{item.sqlColumn}" />:
<ui:fragment rendered="#{item.type == 'INPUT_FIELD'}">
<h:inputText id="#{item.sqlColumn}"
value="#{runtimeBean.sqlData0[item.sqlColumn]}" />
</ui:fragment>
<ui:fragment rendered="#{item.type == 'READ_ONLY'}">
<h:outputText value="#{runtimeBean.sqlData0[item.sqlColumn]}" />
</ui:fragment>
<br />
</c:forEach>
</c:if>
<c:forEach var="action"
items="#{runtimeBean.getViewActions(pv.view, 'BOTTOM_LEFT')}">
<h:commandButton id="action_BL_#{action.code}"
value="#{action.code}" action="#{runtimeBean.doPageAction}">
<f:param name="env" value="#{param.env}" />
<f:param name="view" value="#{pv.view}" />
<f:param name="action" value="#{action.id}" />
<c:forEach var="actionParam"
items="#{runtimeBean.getActionParams(pv.view)}">
<f:param name="#{actionParam}" value="#{param[actionParam]}" />
</c:forEach>
</h:commandButton>
</c:forEach>
</h:form>
</div>
</c:forEach>
<ui:debug hotkey="z"
rendered="#{facesContext.application.projectStage == 'Development'}" />
</ui:define>
</ui:composition>
This is Scoped Variables / Request Parameters for the correctly processed action:
Name Value
env 5
id 22
page 3
After the correct action the next page contains the parameters as passed:
http://localhost:8080/metais/runtime.jsf?env=5&page=3&id=22
and the same for the incorrect action:
Name Value
action 3
env 5
form_prj_detail form_prj_detail
form_prj_detail:action_BL_delete form_prj_detail:action_BL_delete
form_prj_detail:name p5
id 22
view 3
In the wrong case the next page doesn't show the arguments. Just simple:
http://localhost:8080/metais/runtime.jsf
In both cases the parameters are passed already in the HTTP (POST) request. It seems to me more as a problem of javascript part of the JSF library...
EDIT2:
I made some progress in investigating the problem and I've found the following:
The page is being generated dynamically including the forms. They are generated based on parameters passed to the page.
However when applying the form data, they are being applied to page built with missing parameter. If the particular form is NOT present on the same page rendered w/o this parameter, the JSF then doesn't know the form instance and thus its values are not applied and the rest of the page processing chain is invalid.
Using different words: if I add the problematic form to a "default page" (with missing page parameter), the form is processed also from different pages (the same XHTML but different parametrs causing showing different forms on the page).
So for some reason when the page is restored or when the form data are being applied not all page parameters are used to restore the view.
...I made one small step but still don't have a solution and I'm frustrated :-(((
BR,
Rada
So, finally I've understood the problem.
The problem is in the Restore View phase when the server reconstructs the submitted page before any form values could be set and before the form action could be performed.
The point is that the page is not being restored from internal JSF view state but it's restored as a "new" page - and using arguments used to build the original page.
My app. creates the forms dynamically and concrete page content depends on the page parameters (set in the HTTP GET message) and then data read from DB. Pressing a command button builds a request with parameters necessary for making the action - which however don't match with parameters necessary to reconstruct the original/previous page (I don't care of it).
This means that the Restore view is reconstructing DIFFERENT page than the one the command button is pressed from. This means that the reconstructed forms don't match with the original page forms. And this finally means that they can't be matched and thus the follow up life cycle steps are not successfull and no action method could be called.
So... this is either my misunderstanding of the JSF principles OR it's a JSF design issue.
I'd simply expect that the Restore View must be performed implicitly and automatically...
Comments welcome!
BR,
Rada
A have problem with accessing my custom components (which are used as parts of the form).
Here is the story:
I have dynamic form which have few modes of work. Each mode can be selected and loaded into form body with AJAX. It looks like that (template):
<t:form t:id = "form">
<p class= "calcModeTitle">
${message:modeLabel}: <select t:id="modeSelect"
t:type="select"
t:model="modesModel"
t:value="selectedMode"
t:blankOption="NEVER"
t:encoder="modeEncoder"
t:zone = "modeZone"
/>
</p>
<div class="horizontal_tab">
<t:errors/>
</div>
<t:zone t:id="modeZone" id="modeZone" t:update="show">
<t:if test="showCompany">
<t:delegate to="block:companyBlock" />
</t:if>
<t:if test="showPersonal">
<t:delegate to="block:personalBlock" />
</t:if>
<t:if test="showMulti">
<t:delegate to="block:multiBlock" />
</t:if>
</t:zone>
<t:block id="companyBlock">
<t:modes.CompanyMode t:id="company"/>
</t:block>
<t:block id="personalBlock">
<t:modes.PersonalMode t:id="personal" />
</t:block>
<t:block id="multiBlock">
<t:modes.MultiMode t:id="multi" />
</t:block>
<div class="horizontal_tab">
<input type="submit" value="${message:submit_label}" class="submitButton thickBtn"/>
</div>
</t:form>
AJAX works pretty well and form changes accordingly the "modeSelect" state. But i run into problem when submitting the form. I have in class definition hooks for components placed as:
//----form elements
#Component(id = "form")
private Form form;
#InjectComponent
private CompanyMode company;
#InjectComponent
private PersonalMode personal;
#InjectComponent
private MultiMode multi;
where *Mode classes are my own components, containing form elements and input components. I planned to get access to them during validation, and check values supplied by user with form, but when I am trying to reach anything from them I've got nullPointerException - it seems that component are not initialized in my class definition of form. On the other hand form component is injected properly (I am able to write some error for example). I am a bit lost now. How to properly inject my components to class page containing the form?
Dynamic forms in tapestry are a bit complicated. Tapestry passes a t:formdata request parameter which contains the serialized form entities. This is then used serverside in the POST to re-hydrate initial form state. This must be kept up-to-date with what the client sees.
If you want to add dynamic content to a form via ajax, you will need to use the FormInjector. You might want to take a look at the source code for the AjaxFormLoop to see an example.
If you want to render hidden form fragments and make them visible based on clientside logic, you can use the FormFragment
From tapestry guide:
A block does not normally render; any component or contents you put
inside a block will not ordinarily be rendered. However, by injecting
the block you have precise control over when and if the content
renders.
Try to use here either "t:if" or "t:delegate".
Something like this:
<t:zone t:id="modeZone" id="modeZone" t:update="show">
<t:delegate to="myBlock" />
</t:zone>
<t:block t:id="companyBlock">
<t:modes.CompanyMode t:id="company"/>
</t:block>
<t:block t:id="personalBlock">
<t:modes.PersonalMode t:id="personal" />
</t:block>
<t:block t:id="multiBlock">
<t:modes.MultiMode t:id="multi" />
</t:block>
java:
#Inject
private Block companyBlock, personalBlock, multiBlock;
public Block getMyBlock(){
if (getShowCompany()) return companyBlock;
if (getShowPersonal()) return personalBlock;
return multiBlock;
}
I am building a Lift application, where one of the pages is based on the "File Upload" example from the Lift demo at: http://demo.liftweb.net/file_upload.
If you look at the source code for that page... you see that there is a Lift "snippet" tag, surrounding two "choose" tags:
<lift:snippet type="misc:upload" form="post" multipart="true">
<choose:post>
<p>
File name: <ul:file_name></ul:file_name><br >
MIME Type: <ul:mime_type></ul:mime_type><br >
File length: <ul:length></ul:length><br >
MD5 Hash: <ul:md5></ul:md5><br >
</p>
</choose:post>
<choose:get>
Select a file to upload: <ul:file_upload></ul:file_upload><br >
<input type="submit" value="Upload File">
</choose:get>
</lift:snippet>
The idea is that when a user hits the page for the first time (i.e. a GET request), then Lift will show the form for uploading a file. When the user submits the form (i.e. a POST request to the same page), then Lift instead displays the outcome of the file being processed.
With my application, the new wrinkle is that my "results" POST view needs to also contain a form. I want to provide a text input for the user to enter an email address, and a submit button that when pressed will email information about the processed file:
...
<choose:post>
<p>
File name: <ul:file_name></ul:file_name><br >
MIME Type: <ul:mime_type></ul:mime_type><br >
File length: <ul:length></ul:length><br >
MD5 Hash: <ul:md5></ul:md5><br >
</p>
<!-- BEGIN NEW STUFF -->
Output: <br/>
<textarea rows="30" cols="100"><ul:output></ul:output></textarea>
<br/><br/>
Email the above output to this email address:<br/>
<ul:email/><br/>
<input type="submit" value="Email"/>
<!-- END NEW STUFF -->
</choose:post>
...
However, both the GET and POST versions of this page are wrapped by the same Lift-generated form, which has its "action" set to the same snippet in both cases. How can I change this such that in the POST version, the form's action changes to a different snippet?
In a typical web framework, I would approach something like this with an "onclick" event and two basic lines of JavaScript. However, I haven't even begun to wrap my mind around Lift's... err, interesting notions about writing JavaScript in Scala. Maybe I need to go down that route, or maybe there's a better approach altogether.
First, I will suggest you use Lift's new designer friendly CSS binding instead of the custom XHTML tag.
And one thing you should remember when you're using Lift's snippet, is that it is recursive, you could put an lift snippet inside another snippet's HTML block.
For example, if you wish there is another form after POST, then just put it into the block.
<choose:post>
<p>
File name: <ul:file_name></ul:file_name><br >
MIME Type: <ul:mime_type></ul:mime_type><br >
File length: <ul:length></ul:length><br >
MD5 Hash: <ul:md5></ul:md5><br >
</p>
<!--
The following is same as <lift:snippet type="EMailForm" form="post" multipart="true">
-->
<form action="" method="post" data-lift="EMailForm">
<input type="text" name="email"/>
<input type="submit" />
</form>
</choose:post>
Then deal with the email form action at snippet class EMailForm.
Finally, you may pass the filename / minetype and other information by using hidden form element or SessionVar.
I agree with Brian, use Lift's new designer friendly CSS binding.
Use two separate forms, one for the file upload and one for the submitting the email. Use S.seeOther to redirect the user to the second form when the first has finished processing.
I also prefer the new 'data-lift' HTML attribute.
File upload HTML:
<div data-lift="uploadSnippet?form=post">
<input type="file" id="filename" />
<input type="submit" id="submit" />
</div
File upload snippet:
class uploadSnippet {
def processUpload = {
// do your processing
....
if (success)
S.seeOther("/getemail")
// if processing fails, just allow this method to exit to re-render your
// file upload form
}
def render = {
"#filename" #> SHtml.fileUpload(...) &
"#submit" #> SHtml.submit("Upload", processUpload _ )
}
}
GetEmail HTML:
<div data-lift="getEmailSnippet?form=post">
<input type="text" id="email" />
<input type="submit" id="submit" />
</div
Get Email Snippet:
class getEmailSnippet {
def processSubmit = {
....
}
def render = {
"#email" #> SHtml.text(...) &
"#submit" #> SHtml.submit("Upload", processSubmit _ )
}
There's a bit more on form processing in my blog post on using RequestVar's here:
http://tech.damianhelme.com/understanding-lifts-requestvars
Let me know if you want more detail.
Hope that's useful
Cheers
Damian
If somebody comes up with a more elegant (or "Lift-y") approach within the next few days, then I'll accept their answer. However, I came up with a workaround approach on my own.
I kept the current layout, where the view has a GET block and a POST block both submitting to the same snippet function. The snippet function still has an if-else block, handling each request differently depending on whether it's a GET or POST.
However, now I also have a secondary if-else block inside of the POST's block. This inner if-else looks at the name of the submit button that was clicked. If the submit button was the one for uploading a file, then the snippet handles the uploading and processing of the file. Otherwise, if it was the send email submit button shown after the first POST, then the snippet processes the sending of the email.
Not particularly glamorous, but it works just fine.
I have a spring application where i validate all validations in custom spring validator class.
My form has nearly 15 fields.If there is a error in the last field , how to set the focus to that element.
Does spring tags support that. Any technical ideas ? Currently i display my error messages near the fields itself.
It is possible to use spring:bind tag to determine whether a field or the whole model object has error and the overall status of the object after processing (submission).
<spring:bind path="user.username">
<input id="inputId" type="text" name="${status.expression}" value="${status.value}"/>
<c:if test="${status.error}">
<c:forEach items="${status.errorMessages}" var"error">
<span class="field-error">${error}</span>
</c:forEach>
<script type="text/javascript">
document.getElementById('inputId').focus();
</script>
</c:if>
</spring:bind>
Note:
The status variable is exposed by the spring:bind tag.
The javascript part will focus the element when the specific field
has validation errors.
You can set autofocus to all fields that have an error, but your browser will only autofocus (and scroll) to the first one.
<spring:bind path="someFieldName">
<input type="text" name="someFieldName" value="${status.value}" ${status.error?'autofocus="autofocus"':''} />
</spring:bind>
You can't use spring's <form:input /> here as it doesn't work with conditional attributes.
I've used this:
<spring:bind path="*">
<c:if test="${status.error}">
<script type="text/javascript">
window.onload = function(){
document.getElementById('${status.errors.fieldErrors[0].field}').focus();
};
</script>
</c:if>
</spring:bind>
If there is an error it will find the first fieldError and its id and set the focus on that element
forgot to say that the beauty is that you don't need to put this code next to every input. One time is enough per jsp
Can anyone provide a dummy guide \ code snippets on how to create a front end form in Magento that posts data to a controller action.
Im trying to write a variant of the contact us from. (I know its easy to modify the contact us form, as outlined here). I'm trying to also create a feedback form with additional fields.
Given this basic form:
<form action="<?php echo $this->getFormAction(); ?>" id="feedbackForm" method="post">
<div class="input-box">
<label for="name"><?php echo Mage::helper('contacts')->__('Name') ?> <span class="required">*</span></label><br />
<input name="name" id="name" title="<?php echo Mage::helper('contacts')->__('Name') ?>" value="<?php echo $this->htmlEscape($this->helper('contacts')->getUserName()) ?>" class="required-entry input-text" type="text" />
</div>
<div class="button-set">
<p class="required"><?php echo Mage::helper('contacts')->__('* Required Fields') ?></p>
<button class="form-button" type="submit"><span><?php echo Mage::helper('contacts')->__('Submit') ?></span></button>
</div>
</form>
What are the basic step I need to take to get inputted name to a controller action for processing?
If any one is interested, I solved this by building my own module which was heavily based on the Magento_Contacts module.
Here are some links that helped me figure things out.
http://www.magentocommerce.com/wiki/custom_module_with_custom_database_table
http://inchoo.net/ecommerce/magento/magento-custom-emails/
To make $this->getFormAction() return the URL to your custom controller, you have two options:
call setFormAction() somewhere else on the block.
use a custom block type that implements getFormAction().
(1) is what happens in Mage_Contacts_IndexController::indexAction(), but (2) is the cleaner approach and I'm going to explain it in detail:
Create a custom module
app/etc/modules/Stack_Form.xml:
<?xml version="1.0"?>
<config>
<modules>
<Stack_Form>
<active>true</active>
<codePool>local</codePool>
</Stack_Form>
</modules>
</config>
app/code/local/Stack/Form/etc/config.xml:
<?xml version="1.0"?>
<config>
<modules>
<Stack_Form>
<version>0.1.0</version>
</Stack_Form>
</modules>
<frontend>
<routers>
<stack_form>
<use>standard</use>
<args>
<module>Stack_Form</module>
<frontName>feedback</frontName>
</args>
</stack_form>
</routers>
</frontend>
<global>
<blocks>
<stack_form>
<class>Stack_Form_Block</class>
</stack_form>
</blocks>
</global>
</config>
This configuration registers the stack_form block alias for own blocks and the feedback front name for own controllers.
Create custom block
app/code/local/Stack/Form/Block/Form.php
class Stack_Form_Block_Form extends Mage_Core_Block_Template
{
public function getFormAction()
{
return $this->getUrl('stack_form/index/post`);
}
}
Here we implemented getFormAction() to generate the URL for our custom controller (the result will be BASE_URL/feedback/index/post).
Create custom controller
app/code/local/Stack/Form/controllers/IndexController.php
class Stack_Form_IndexController extends Mage_Contacts_IndexController
{
public function postAction()
{
// your custom post action
}
}
If the form should behave exactly like the contact form, just with a different email template and additional form fields, there are two solutions that I have outlined at https://magento.stackexchange.com/q/79602/243 where only one of them actually requires a custom controller action to send the form:
If you look at the contacts
controller
used in the form action, you will find that
the transactional template is taken directly from the configuration
all POST data is passed to the template (as template variable data), so that you can add any additional fields to the form
template and use them in the email template. But validation is hard
coded for "name", "comment", "email" and "hideit".
So, if you need a completely different email template or
additional/changed input validation, your best bet is to create a
custom controller with a modified copy of the postAction of
Mage_Contacts_IndexController.
But there is another solution that is a bit limited but without any
custom code involved:
create a hidden input that determines the type of the form. It could be just <input type="hidden" name="custom" value="1" />.
in the contact transactional email template, use the if directive to show different content based on the form type:
{{if data.custom}}
... custom contact form email ...
{{else}}
... standard contact form email ...
{{/if}}
How to use this custom block
You can add the form anywhere in the CMS using this code (CMS directive):
{{block type="stack_form/form" template="path/to/your/form.phtml"}}
If you do this, you need to add "stack_form/form" to the block whitelist under System > Permissions > Blocks!
Or in the layout using this code (layout XML):
<block type="stack_form/form" name="any_unique_name" template="path/to/your/form.phtml" />
Solution without custom module
If you use the solution without custom controller and a single email template mentioned above, you can set the form action using layout XML as well.
To achieve this, we use the feature to call helpers as parameters for block actions. Unfortunately, the core helper does not have a public method to get a URL but the helper from Mage_XmlConnect has, so you can use that one:
<block type="core/template" name="any_unique_name" template="path/to/your/form.phtml">
<action method="setFormAction">
<param helper="xmlconnect/getUrl">
<route>contacts/index/post</route>
</param>
</action
</block>
In the CMS directive you cannot use helpers, so there you would need to put the actual URL:
{{block type="stack_form/form" template="path/to/your/form.phtml" form_action="/feedback/index/post"}}
Since you probably have different CMS pages/blocks in different store views, this should not be a big problem.