Tapestry 5 custom component in form - access during validation - forms

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;
}

Related

jQuery unobtrusive validation - can't add in simple custom client validation rule

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.

JSF form not processed correctly

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

JSF nested components with forms

Is it considered correct to have a composite component (compA) that holds a form, when compA can itself be contained in another form (in the using page)?
<!-- composite -->
<cc:implementation>
<h:form id="innerForm">
... composite stuff
</h:form>
</cc:implementation>
<!-- using page/component -->
<h:form id="outerForm">
<util:compA ... />
</h:form>
When trying to remove nested forms, any ajax call in the composite will submit the whole form, and as has some values may not yet be filled, validation fails.
Is there any best-practice approach to this?
Nested forms are always a bad idea and will result in invalid HTML output.
A Composite Component itself is a Naming Container so it should not be a problem to process only the CC or even some parts of it.
To prevent the whole form from being even submitted I would suggest PrimeFaces AJAX calls with partialSubmit="true". See here for reference.
If there are still problems with your AJAX calls you habe to provide the related code of your CC.

How to dynamically add new row to p:dataTable without validating the form?

I'm developing a form for adding/editing product prices with JSF and PrimeFaces. A single product can have multiple prices depending on volume which is shown in a <p:dataTable>.
The backing bean:
#ManagedBean
#ViewScoped
public class ProductBean {
protected Product product;
protected List<ProductPrice> productPrices;
public void addNewPrice() {
ProductPrice productPrice = new ProductPrice();
productPrice.setPrice(new BigDecimal(0));
this.productPrices.add(productPrice);
}
// ...
}
The Facelet page:
<h:form id="productForm">
<p:inputText value="#{productBean.product.name}" required="true">
<f:ajax event="blur" render="nameMessage" />
</p:inputText>
<p:message id="nameMessage" for="name" />
<p:dataTable id="pricesList" ...>
</p:dataTable>
<p:commandButton value="Add another price" update="pricesList" action="#{productBean.addNewPrice()}" />
<p:commandButton value="Submit" action="#{productBean.submit}" />
</h:form>
The first button "Add another price" does, what it is supposed to do: Adding a new row to "pricesList". But only if form is valid (form is invalid, if product-name is not set).
My problem is, that I am having two commandButtons for the form, but I don't know how to get my wished functionallity without a commandButton. I tried a lot of ways: Changing the "Add another price" to a standard <p:button> with ajax-functionality; doesn't work because of buttons' outcome. I tried "type=button" for this button, but in this case simply nothing happens.
Are there any suggestions have to achieve my wished functionality? It is not necessary to have a button solving my problem.
The <p:commandButton> submits and processes by default the entire form. This will indeed validate all input fields. You can control this with the process attribute which thus defaults to #form. In your particular case, you could just use #this so that only the command button's own action is invoked.
<p:commandButton value="Add another price" process="#this" update="pricesList" action="#{productBean.addNewPrice()}" />
what's wrong with using p:commandButton to add rows dynamically?
If i undersatnd your question right, you obviously need a request to your managed bean in order to add your new row information into your datatable list. you could always go for p:commandLink if you are not comfortable with p:commandButton. and for editing the row data you could use p:rowEditor with p:datatable.
check out the showcase example for datatable row editing
hope this helps :)

Custom forms in Magento

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.