I want my panel to stay visible when i click on a command button and the executed method invokes an error message.
To be more specific I have a validator for an input field which should get a date. If this date is not valid my validate method in the backing bean creates an error message. This should be displayed next to the input field in the popup panel after using the command button.
Clicking on the command button closes the popup though. But if i reopen it, the error message is displayed, making me wonder why it was closed in the first place, when the maximumSeverity condition wasn't met.
My xhtml page:
<h:body>
<h:commandButton id="note" value="Neuer Satz">
<rich:componentControl target="note_panel" operation="show" />
</h:commandButton>
<rich:popupPanel id="note_panel" modal="true" autosized="true"
resizeable="false" header="Neuen Mehrwertsteuersatz vormerken"
domElementAttachment="form">
Gültig ab
<h:inputText id="newVorGueltigAb"
value="#{mehrwertsteuerBean.neuGueltigAb}" maxlength="10"
validator="#{mehrwertsteuerBean.validateNewDate}" />
<h:message for="newVorGueltigAb" style="color:red" />
<h:commandButton value="Vormerken"
action="#{mehrwertsteuerBean.addSteuersatz()}"
oncomplete="if (#{facesContext.maximumSeverity==null})
#{rich:component('note_panel')}.hide(); return false;" />
<h:commandButton value="Abbrechen"
onclick="#{rich:component('note_panel')}.hide(); return false;" />
</rich:popupPanel>
</h:body>
My validation method in the backing bean:
public void validateNewDate(FacesContext context, UIComponent toValidate,
Object value) {
String regex = "([0-9]{2}).([0-9]{2}).([0-9]{4})";
String date = (String) value;
if (date.matches(regex)) {
validNewDate = true;
} else {
validNewDate = false;
String message = "Bitte gültiges Datum eingeben!";
context.addMessage(toValidate.getClientId(context),
new FacesMessage(message));
}
}
First of all, when you want validation to fail you have to throw an exception. So it must looks something like this:
public void validateNewDate(FacesContext context, UIComponent toValidate, Object value) {
String regex = "([0-9]{2}).([0-9]{2}).([0-9]{4})";
String date = (String) value;
if (!date.matches(regex)) {
String message = "Bitte gültiges Datum eingeben!";
throw new ValidatorException(
new FacesMessage(FacesMessage.SEVERITY_ERROR, message, null));
}
}
If you do so there will be no changes in model and the action in your's commandButton WON'T be fired. This is the right flow for JSF validation.
Second thing. You have to deal with validation errors manually. To check if there was some error I'm using this:
public boolean isNoErrorsOccured() {
FacesContext facesContext = FacesContext.getCurrentInstance();
return ((facesContext.getMaximumSeverity() == null) ||
(facesContext.getMaximumSeverity()
.compareTo(FacesMessage.SEVERITY_INFO) <= 0));
}
And with this your a4j:commandButton can look like this:
<a4j:commandButton value="Vormerken" execute="note_panel"
action="#{mehrwertsteuerBean.addSteuersatz}"
render="note_panel"
oncomplete="if (#{facesHelper.noErrorsOccured}) {#{rich:component('note_panel')}.hide();}
else { console.log('OMG! I\'ve got an error!'); }" />
Update:
All should look like this:
<rich:popupPanel id="note_panel" modal="true" autosized="true" resizeable="false"
header="Neuen Mehrwertsteuersatz vormerken" domElementAttachment="body">
Gültig ab
<h:form>
<h:inputText id="newVorGueltigAb" value="#{mehrwertsteuerBean.neuGueltigAb}" maxlength="10"
validator="#{mehrwertsteuerBean.validateNewDate}" />
<h:message for="newVorGueltigAb" style="color:red" />
<a4j:commandButton value="Vormerken" execute="#form" action="#{mehrwertsteuerBean.addSteuersatz}"
render="#form"
oncomplete="if (#{mehrwertsteuerBean.noErrorsOccured}) {#{rich:component('note_panel')}.hide();}" />
<h:commandButton value="Abbrechen"
onclick="#{rich:component('note_panel')}.hide(); return false;" />
</h:form>
</rich:popupPanel>
Note:
Missing <h:form>!!!
The render attribute, because you have to render your panel in case of validation error.
There are no brackets at the end of action.
The execute attribute is set to popup only. This will cause that no other fields from the whole form will be updated.
Since h:form is inside the popup I've changed domElementAttachment to body.
Related
I have three XHTML pages;
index.xhtml
page_1.xhtml
page_2.xhtml
In the index.xhtml page, I have a commandButton which sends the user to page_1.xhtml. All this is done in the navigation rule in faces-config.xml.
How would I redirect the user to page_2.xhtml from the index.xhtml using another commandButton assuming that both commandButtons' actions are linked to a backing Java class?
Just bind the buttons to different action methods which each return a different outcome.
<h:commandButton value="Go to page 1" action="#{bean.goToPage1}" />
<h:commandButton value="Go to page 2" action="#{bean.goToPage2}" />
with
public String goToPage1() {
// ...
return "page_1";
}
public String goToPage2() {
// ...
return "page_2";
}
Navigation cases are not necessary. JSF 2.0 supports implicit navigation. The navigation outcome can just be the path/filename of the desired target view. The file extension in the outcome is optional.
If you don't necessarily need to perform any business action on navigation, or you can do it in the (post)constructor of the backing bean of the target page instead, then you can also just put the outcome value in the action directly.
<h:commandButton value="Go to page 1" action="page_1" />
<h:commandButton value="Go to page 2" action="page_2" />
A <h:commandButton> will however not perform a redirect, but a forward. The enduser won't see the URL being changed in the browser address bar. The target page isn't bookmarkable. If you can, I'd suggest to use <h:button> instead.
<h:button value="Go to page 1" outcome="page_1" />
<h:button value="Go to page 2" outcome="page_2" />
Or if you really need to invoke a business action, but would like to perform a real redirect, then append faces-redirect=true as query string to the outcome value.
public String goToPage1() {
// ...
return "page_1?faces-redirect=true";
}
public String goToPage2() {
// ...
return "page_2?faces-redirect=true";
}
See also:
How to navigate in JSF? How to make URL reflect current page (and not previous one)
When should I use h:outputLink instead of h:commandLink?
You can also do this, in any part of your code to be redirected to "example.xhtml"
ExternalContext ec = FacesContext.getCurrentInstance()
.getExternalContext();
try {
ec.redirect(ec.getRequestContextPath()
+ "/faces/jsf/example.xhtml");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Add two navigation cases as shown below. In the action methods, return outcomes corresponding to the buttons.
<navigation-rule>
<from-view-id>index.html</from-view-id>
<navigation-case>
<from-outcome>page1</from-outcome>
<to-view-id>page_1.xhtml</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>page2</from-outcome>
<to-view-id>page_2.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
that's my code:
<listbox id="boxFirma" multiple="true"
visible="#load(vm.opzioneSelezionata eq 'firma' ? 'true' : 'false')"
checkmark="true" width="400px" height="200px"
model="#bind(vm.opzioniFirma)"
selectedItems="#bind(vm.pickedItemSet)">
<template name="model" var="item"
status="s">
<listitem selected="#bind(item.preSelected)">
<listcell label="#bind(item.valore)" />
</listitem>
</template>
</listbox> <button label="Salva" style="margin-top:10px" disabled="#load(empty vm.pickedUser)"
onClick="#command('salvaPersonalizzazioneUtente')" />
The problem is when I push the button Salva, I get on the vm.pickedItemSet only the item that the user has just chosen, but nothing about the preselected items -> 'listitem selected="#bind(item.preSelected)" ' . So if there were 2 items preselected and one clicked by the user on the view model, I get just the one clicked, whereas I want all three. How do I fix this?
I think that your problem is behind the use of "preselected" property of your domain object. Without your View Model it's hard to understand what you are trying to achieve.
Hovewer, let me try to address you:
fill the set (pickedItemset) in the init method, and let zk handle that set.
remove " selected="#bind(item.preSelected)" " from you template. If you like
checkboxes, add "checkmark=true" as a listbox property
(http://books.zkoss.org/wiki/ZK_Component_Reference/Data/Listbox#Multiple_Selection).
As an example, try this View Model ( "SignOption" is a bean with a single member valore). The "Salva" button will print out the set of selected list items.
// a bunch of imports
public class MultiSelectionVM {
private String opzioneSelezionata = "firma";
private Set<SignOption> opzioniFirma = new HashSet<SignOption>();
private Set<SignOption> pickedItemSet = new HashSet<SignOption>();
private boolean pickedUser = true;
#Init
public void init(){
SignOption opt1 = new SignOption();
opt1.setValore("opt1");
SignOption opt2 = new SignOption();
opt2.setValore("opt2");
SignOption opt3 = new SignOption();
opt3.setValore("opt3");
//Init list model
opzioniFirma.add(opt1);
opzioniFirma.add(opt2);
opzioniFirma.add(opt3);
//Init selected Items
pickedItemSet.add(opt2);
}
#Command
public void salvaPersonalizzazioneUtente(){
System.out.println(pickedItemSet);
}
//Getters and setter for all members
}
Hope this helps!
I am trying to create a chrome extension - when an user clicks on a icon, the popup window with the form appears. The textarea in the form can contain more parameters which comes to URL. After filling in and clicking the GO button, multiple tabs with URLs with this parameters needs to be opened.
Example: popup.html
<form name="myform">`
<textarea id="params" name="params" style="width: 170px;height: 270px;"></textarea>`
<input id="edit" checked="checked" type="radio" name="edit" value="1" /> option 1 <input id="edit" type="radio" name="edit" value="2" /> option 2`
<div id="clicked">GO</div>`
</form>`
Then in popup.js I have:
function click(e) {
chrome.tabs.executeScript(null, {
code: "alert('starting');"
});
opener();
}
document.addEventListener('DOMContentLoaded', function () {
var divs = document.getElementById("red");
divs.addEventListener('click', click);
});
So when an user clicks on GO button, the opener() function needs to be started.
Here is the opener function - it only determines the values of textarea and radio buttons and opens as many new tabs as manz parameters are in the textarea.
function opener() {
alert('working');
var parameter = document.myform.getElementById("params").value;
for (index = 0; index < document.myform.edit.length; index++) {
if (document.myform.edit[index].checked) {
var radioValue = document.myform.edit[index].value;
break;
}
var Result = parameter.split("\n");
if (radioValue == 1) {
for (i = 0; i < Result.length; i++) {
window.open('http://mypage.com?param=' + Result[i]);
}
}
}
}
So the Result is the value of textarea splitted by \n and radio value is the value of radio button selected.
This works fine - after clicking a browser icon the popup with form comes up, but when I fill in the textarea and select the first radiobutton, then I click GO, the opener funvtion works not...
The only thing that works is the popup alert with working word and then the alert starting from the click(e) function.
So the opener function starts, writes the alert, but nothing else... no tabs will open, nothing happens...
Can someone help me please?
I've found that using the chrome.tabs.create function works much better within the extension than the window.open function does.
chrome.tabs.create({url:"https://www.facebook.com/PSChrome"});
We use Prism and from the grid we pop up a edit form that has two options, "Save" and "Save and New". My question is about re-initializing form. I am wondering if there is a better or simpler way? What I do is expose a InteractionRequest on the view model, and than use InteractionRequestTrigger in xaml to change the properties of the form, like this:
private void SubmitAndNewCommandCallback(IEnumerable<ValidationResult> errors)
{
if (errors != null && errors.Any())
{
Errors = errors.Select(x => x.ErrorMessage).ToList();
}
else
{
if (IsNew)
{
_events.GetEvent<BidAgentCreated>().Publish(this.BidAgent);
}
_intializeFormRequest.Raise(this);
}
}
<i:Interaction.Triggers>
<prism:InteractionRequestTrigger SourceObject="{Binding InitializeFormRequest}" >
<ei:ChangePropertyAction TargetName="ctlAgentType" PropertyName="SelectedIndex" Value="0" />
<ei:ChangePropertyAction TargetName="ctlAgentSearchBox" PropertyName="Text" Value=""/>
</prism:InteractionRequestTrigger>
</i:Interaction.Triggers>
A clean way is to get rid of the logic in your View and keep it in the ViewModel.
in xaml
<ComboBox ItemsSource="{Binding AgentTypes}" SelectedItem="{Binding SelectedAgentType,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}"/>
<TextBox Text="{Binding SearchText,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" />
in the ViewModel
private void SubmitAndNewCommandCallback(IEnumerable<ValidationResult> errors)
{
if (errors != null && errors.Any())
{
Errors = errors.Select(x => x.ErrorMessage).ToList();
}
else
{
if (IsNew)
{
_events.GetEvent<BidAgentCreated>().Publish(this.BidAgent);
}
SearchText="";
SelectedAgentType = AgentTypes.First(); //selects first agenttype, or set to null to select nothing in the combobox
}
}
Having multiple forms in one page, when i submit one of them, how can i tell wich one was submitted?
I thought about generating uniqe ids for each from, and saving them as hidden fields and to the user-session - while this is a solution, the problem with it is that there is no good place to remove old ids from the session.
Any better ideas how to solve this problem?
Thanks in advance!
First of all: have you considered sending the two forms to two different actions? That way you can handle each form separately in an action each. This should be the "best-pratice" if you're using the Zend MVC component.
The other option is to check for the value of the submit button which will be included in the request, e.g.
<input type="submit" name="save" value="form1" />
// in PHP:
// $_POST["save"] will contain "form1"
<input type="submit" name="save" value="form2" />
// in PHP:
// $_POST["save"] will contain "form2"
Be careful as the value-attribute will be rendered as the button's label.
So perhaps you want to distingush the forms by different submit-button names:
<input type="submit" name="save-form1" value="Submit" />
// in PHP:
// $_POST["save-form1"] will contain "Submit"
<input type="submit" name="save-form2" value="Submit" />
// in PHP:
// $_POST["save-form2"] will contain "Submit"
EDIT:
During the comment-dialog between the OP and myself the following seems to be a possible solution:
class My_Form_Base extends Zend_Form
{
private static $_instanceCounter = 0;
public function __construct($options = null)
{
parent:: __construct($options);
self::$_instanceCounter++;
$this->addElement('hidden', 'form-id',
sprintf('form-%s-instance-%d', $this->_getFormType(), self::$_instanceCounter);
}
protected _getFormType()
{
return get_class($this);
}
}
class My_Form_Type1 extends My_Form_Base
{
public function init()
{
// more form initialization
}
}
class My_Form_Type2 extends My_Form_Base
{
public function init()
{
// more form initialization
}
}
some errors in you code, shoudl be something like this:
class Application_Form_Idee_Base extends Zend_Form
{
private static $_instanceCounter = 0;
public function __construct($options = null)
{
parent::__construct($options);
self::$_instanceCounter++;
$this->addElement('hidden', 'form-id', array(
'value' => sprintf('form-%s-instance-%s', $this->_getFormType(), self::$_instanceCounter))
);
}
protected function _getFormType()
{
return get_class($this);
}
}