I was checking out the code for this template from the OmniFaces Showcase app, when I encountered the following condition used in a p:selectOneMenu:
disabled="#{facesContext.postback and not facesContext.renderResponse}"
From using the app, it seemed that the selectOneMenu is never disabled, so what does this code really do?
I know that facesContext.postback is true whenever the page is resultant from POST requests generated by JSF components (commandButton/commandLink, etc), but what would be the usual expected state of facesContext.renderResponse when evaluated in the View?
UPDATE: oops, just now I saw the comment: They're disabled in other phases than render response, because they otherwise complain that the model value cannot be set even though there's no form.
So, I'm guessing the condition is evaluated several times during Faces lifecycle, and the component is disabled until the last state (renderResponse) is reached, when facesContext.renderResponse evaluates to true, the whole expression evaluates to false and the component is then enabled. Is that about right?
Those <p:selectOneMenu> components are actually abused to have a nice <div><ul><li> dropdown menu with a minimum of code ;) Their values represent the current menu group and page which are by design read-only (they have no setter method). The navigation takes place by JavaScript window.location which is handled during change event. They are not part of any form and do not participate in any form submit.
The disabled attribute is theoretically not mandatory, but when a non-ajax JSF form elsewhere in the very same page is submitted synchronously, then PrimeFaces SelectOneMenuRenderer will still try to decode() it in its entirety even though it is not enclosed in any form at all. It would ultimately cause the following exception when the model value is to be updated because there's no setter for that value:
javax.el.PropertyNotWritableException: /WEB-INF/templates/showcase.xhtml #28,80 value="#{parent.children[0].viewId}": The class 'org.omnifaces.showcase.Page' does not have a writable property 'viewId'.
at com.sun.faces.facelets.el.TagValueExpression.setValue(TagValueExpression.java:136)
at javax.faces.component.UIInput.updateModel(UIInput.java:818)
at javax.faces.component.UIInput.processUpdates(UIInput.java:735)
at javax.faces.component.UIComponentBase.processUpdates(UIComponentBase.java:1242)
at org.primefaces.component.panel.Panel.processUpdates(Panel.java:304)
at javax.faces.component.UIComponentBase.processUpdates(UIComponentBase.java:1242)
at javax.faces.component.UIComponentBase.processUpdates(UIComponentBase.java:1242)
at javax.faces.component.UIViewRoot.processUpdates(UIViewRoot.java:1231)
at com.sun.faces.lifecycle.UpdateModelValuesPhase.execute(UpdateModelValuesPhase.java:78)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593)
When the disabled attribute evaluates true during postback, then the SelectOneMenuRenderer will skip the decode() during apply request values phase and thus also skip the update of model value. However, if it evaluates true as well during render response phase, then it becomes unselectable (and thus unusable). So, it should not evaluate true during render response phase. The expression
disabled="#{facesContext.postback and not facesContext.renderResponse}"
achieves exactly that. All with all, it's basically kind of a workaround for the odd behavior of SelectOneMenuRenderer (for which I've not really investigated the underlying cause yet).
To test it yourself, pull the project, remove the disabled attribute and invoke a synchronous submit in for example the <o:onloadScript> showcase page.
This evaluates to the getter state for reading whether renderResponse() has been invoked. This method is used to instruct the JSF lifecycle to skip subsequent phases and go straight to emitting the response (e.g. in the case of a validation failure.)
Related
My program flow requires that I first set the value of a select option in the Viewmodel and then I load the available options into the Viewmodel. This causes problems, typically the first available option will be seen in the selection list after this, while the selected option value stays at the specified value.
I was hoping that setting the new "valueAllowUnset" would help, but instead my page becomes unresponsive when using it.
Therefore I currently set the original value again after loading the options. Strangely, this only works when I set the value, set a different value and then set the value again, e.g.
self.data()[field](orgValue);
self.data()[field]({});
self.data()[field](orgValue);
(I store values for multiple selection lists in the self.data observable and access them with the property index "field".)
I tried to strip this down to a simple JSFiddle (http://jsfiddle.net/timvdh/HN6DE/), but the behavior does not show there, although the example pretty much resembles my actual application. However, I am using about 30 select lists there.
So I was hoping, that someone can make an educated guess about what goes wrong in my application, I am hoping that the fact that I have to set the original value three times maybe gives a clue.
EDIT: This fiddle demonstrates, that setting the selected option before and after filling the options list does work with "valueAllowUnset": http://jsfiddle.net/timvdh/HT5Tp/
I am using JQuery Mobile with knockout.js and that caused the problem (sorry I did not even think about JQM being the problem in the first place). I am now setting the option before filling the list and after filling the list I call
$("#selectmenuID").selectmenu("refresh");
to display the option.
The hanging browser was not caused by knockout, there are additional subscriptions to the selectmenus in my app. "valueAllowUnset" changed the behavior of the selectmenus and triggered loops caused by problems in my code.
during the migration from RichFaces 3 to 4, we are facing an unexpected behaviour of rich:tree element. We registered a selectionChangeListener which is supposed to include a facelet with h:inputText fields next to the tree depending on the new selection. The facelet is supposed to show detailed information on the selected node.
This works correctly when the first selection is made. However, when we select another node of the same type, the same facelet is included (which is correct in this case as the node type is the same), but the values of the previous node are displayed in the facelet, even though the object holding the value of the component is instantiated correctly.
Inspecting the log files revealed that the TreeSelectionChangeEvent is processed during the Apply Request Phase (2). During that phase, the object holding the textfield's value is updated correctly, but is changed back to the old value during Update Model Phase (4).
General question on this issue is: Is it correct that this event is processed during Apply Request Phase even though the immediate attribute of the tree is set to false? (I would have expected the event to be processed during Invoke Application Phase (5)) And, if this is correct behaviour, how could we circumvent the problem that the old value is restored?
Thanks for your suggestions in advance.
as much as I understood, during the Apply Request Phase, the listener-event is executed and you update fields which are also represented as h:inputFields in the currently rendered view?
During Update Model Phase, the data out of the original h:inputText overwrites the already made changes?
Without being able to test it out locally: With switchType="ajax" the rich:tree-component gives two attributes
execute and
render.
You want the facelet-part to re-render, but not the input values to be executed (because that's what the listener already did).
Assuming you use rich:tree in ajax switch-type, have you tried to set execute="#this" and render="<theFaceletComponent>"?
If the component id wasn't found, use :#{rich:clientId('<theFaceletComponent>')} for an absolute identifier.
Hope, that helps ...
Angular newb here, thoughts appreciated...
Say I want an input field to control the window title as you type. A field with a model binding and no associated controller acts on keypress, as intended. However, there has to be a bit more logic to it -- default value before any user input, also used if the input is blanked.
Adding a controller bound to enclosing elements gives a place for that logic, but the change-on-keypress behavior is gone. I'm sure it's possible to recreate it by hand or with ui, but since it's inherently there without the controller, I'm wondering if I'm missing the simple clean way.
Simple version, acts on keypress, but with no smarts:
<title ng-bind-template="{{windowTitle}}">Default Title (not seen)</title>
<input ng-model="windowTitle" type="text">
Putting controller bindings on the head (for the title) and a containing div (for the input), and setting a default $scope.windowTitle inside the controller function does use that default value, but it breaks the auto-update.
I know in real life you'd want a real model, but what I'm really trying to understand is these two ways angular appears to work. I haven't found anything specifically describing two different implicit input binding behaviors, but I haven't been through all the docs yet.
How should I be thinking about this?
Edit: It's not the window title or default value per se that I'm interested in. I'm trying to understand this:
When there's no controller on either the field or the title, typing in the field changes the window title immediately, on keypress. The title is directly linked to the field value, with no other angular hookup.
With controller bindings around the title and the field, typing in the field has no effect on the title.
What I've since realized (I think) is that ng-controller bindings create a new instance of the controller each time. Here's the non-working code I didn't show before:
<title ng-controller="TitleCtrl" ng-bind-template="{{windowTitle || 'Foo'}}">Foo</title>
...
<label ng-controller="TitleCtrl">
<input ng-model="windowTitle" type="text">
{{windowTitle}}
</label>
The value set by the model binding to the field is shown correctly within that instance of that controller, and updates on keypress, as before. But since those two controller instances are separate, the binding to the title works but the data it points to isn't bound to the field.
Isn't that right? The reason it works with no controllers is that that makes the value global, so the title binding sees the value set by the field binding.
So what's the canonical way to reference data from some other area? Create a service?
I realize that this is basic angular stuff, just getting started here, so thanks!
Edit 2
On reflection, I've come to seriously disrespect this whole question, even though I wrote it.
It's based on way-too-early poor understanding of the Angular application model. I had worked through only part of the official tutorials, and jumped ahead to removing all the js from a not big but not totally trivial existing app, and exploring what Angular could to in that context.
I got some very quick bang for the buck, getting several pieces of functionality working with very little code, and simple, clear markup, felt good. But I really had short-circuited internalizing the Angular way of thinking, and my quick and dirty no-architecture approach broke down when different parts of the page needed to coordinate with each other, as in this question.
I've postponed that project while I go back to tutorials and other learning. If other folks think this question should be deleted, I'd add my vote. Or maybe it's a useful on some level, ignorant though it is.
Well, there are multiple ways to achieve the behavior you want without using an explicit controller and model, you could:
<title ng-bind-template="{{windowTitle && windowTitle || 'default'}}"></title>
Or in a more simple way:
<title>{{windowTitle && windowTitle || 'default' }}</title>
In both cases, we're using the conditional expression:
(condition) && (answer if true) || (answer if false)
You should however strive to remove logic from the templates.
I ask this because I have a form with a radio button set to nil :
= f.radio_button :estimate_type, nil
I have debugger right at the beginning of my method call :
def create
debugger
When I hit the debugger, I check out my params, and they say the value is on not nil.
Enter Insanity wolf. Somehow this is getting converted on click. And I've scoured the entire app looking for possibly a leaky javascript file, or anything closely resembling the word 'on'. I've checked all my bases. Defaults in schema.rb, jquery click events, model validations, you name it. Nothing with the word "on" anywhere.
So the real question is, is there a way I can throw a debugger in a place in which if I were to click submit, the debugger would appear before the model validation, and then hopefully where the params are still what they are in the form. And then I can follow it down the trail and see where it goes wrong.
It doesn't have anything to do with your JavaScript. This is something that I've experienced before as well, but I'm not sure why it converts nil to 'on'. I do know that passing in :nil as a symbol returns a null string, as well as just simply passing in false.
A better approach to trying to solve your problem may be to put the debugger in the validation callback itself.
Nothing to do with rails - you could verify this by using your browser's network inspector to see that the browser is actually sending the parameter value "on".
By trying to set the value to nil (which doesn't really make sense - parameter values are always strings) you're suppressing the value attribute entirely from the generated HTML.
The standard says that in this case the default value for the input shall be "on" and so that is what your browser submits.
In both MyFaces and Mojarra 2.1, there exists a defect where when javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL is set to true, any fields that are marked as required and pre-populated in the model, when blanked-out and submitted are re-shown with their original non-blanked-out values instead of being left blank.
Scenario is:
User loads page with a single required field that is populated with existing data from the model
User clears out the field on the page and submits the form
Validation fails, as expected, and the user is shown an error message that they must fill in the
required field.
The issue is that the field, which should show what the user submitted -- which is that they submitted a blank value for the field -- is instead populated with the original value from the model. The only workaround is to set javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL to false. Setting to false delivers the desired behavior that the field remains blanked out when the page is redisplayed with the required field error message.
A defect was logged with Mojarra (http://java.net/jira/browse/JAVASERVERFACES-2375) and MyFaces (https://issues.apache.org/jira/browse/MYFACES-3525) but no progress has been made in 6 weeks.
Note that it appears Mojarra had a similar issue reported over 6 mos
ago yet no progress has been made.
Does anyone know of a workaround for this where we can keep javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL set to true instead of false and yet not encounter this required field usability issue?
This problem is explained in detail in JSF 2 - Bean Validation: validation failed -> empty values are replaced with last valid values from managed bean. To the point, in Mojarra it's caused by a bug or at least an oversight in HtmlBasicRenderer#getCurrentValue() and has been reported as issue 2266.
In the meanwhile, the easiest way to workaround this, while taking 3rd party component libraries with their own renderers like PrimeFaces into account, is to copy the source file of UIInput straight in your project's source folder and then edit the getSubmittedValue() accordingly:
public Object getSubmittedValue() {
if (submittedValue == null && !isValid() && considerEmptyStringNull(FacesContext.getCurrentInstance())) {
return "";
}
else {
return submittedValue;
}
}
It'll end up in /WEB-INF/classes and this will get precedence in classloading above the one in the JSF JAR file. Admittedly, it's somewhat clumsy, but it's less painful than rebuilding the JSF JAR file or overriding every single input renderer.