[jsr-314-open] JavaScript disabled support [Was: Outcome of JSFDays discussions]

Andy Schwartz andy.schwartz at ORACLE.COM
Sun Apr 12 19:55:13 EDT 2009


Hi Martin -

Martin Marinschek wrote On 4/7/2009 8:43 AM ET:
> Hi Andy,
>
> thanks for your very long and extensive mail - you covered everything.
>   

Thanks for reading through my long email and for the comments! :-)  
Sorry I wasn't able to follow up on this last week...

>
> cs-JSF deals with this. What we do is we encode params into the
> button-value, and decode these params in a request-parameter map
> wrapper so that they are available to the rest of the lifecycle just
> as if they were normal params.
>   

Cool, makes sense.

>> The main use case for 3B is master-detail components.
>>     
>
>
> ... and a lot of other stuff - it is the most-used use-case for us for
> partial page update with regards to input-components. You change some
> input, and somewhere else, some input shows up, vanishes, gets
> disabled enabled, etc.
>   

Right, agree completely.  When I used the term "master-detail", I should 
have said that I was using this loosely - ie. I just meant any case 
where updating one (master) component results in other (detail) 
components somewhere else on the page being updated (or hidden/shown).


>> One concern that I have about this approach is that it means that the Behavior implementation needs to be familiar with the component - and possibly the component's content - to know when to render, or whether to render anything at all.  For example, if the Behavior is attached to an <h:selectOneMenu>, then it might want to render a "Refresh" button.  However, if the Behavior is attached to an <h:commandButton> (or some other component, like an <h:panelGrid>), then no "Refresh" button should be rendered.
>>     
>
> you are right - it is component dependent. but don't we have several
> decisions already in the behaviour classes, depending on the
> component-type? e.g. the default event-type (onclick, onchange, etc.).
> So how is this different?
>   


In the initial non-generic/Ajax-specific design we did have assumptions 
based on the component type - ie. <f:ajax> only supported "action" 
events for command components and "valueChange" events for 
editableValueHolders.  This was awful - and one of the main reasons why 
we decided to move forward with the generic Behavior solution.

With the current solution, the component identifies the set of supported 
events as well as the default event via the ClientBehaviorHolder 
interface.  This allows ClientBehavior implementations (such as 
AjaxBehavior) to avoid making assumptions based on the component type, 
which is important for interoperability - ie. this allows us to write 
ClientBehavior implementations which are not specific to a known set of 
components - but can work with arbitrary components from different 
component providers (eg. RichFaces, IceFaces, Trinidad, ADF Faces, etc...).

>> I would prefer that we come up with a solution where the component Renderer decides when/where to have the Behavior render its fallback content, since the component Renderer knows both whether it requires fallback content at all and also knows where the best placement of the fallback content might be.
>>     
>
>
> ok, but with the same argument we could have said that ajax-rendering
> should be happening directly in the renderer from the beginning,
> right? The idea is that any component can be AJAX enabled, now when
> JavaScript is disabled, the arbitrary component will not have any idea
> that AJAX was enabled from the beginning, so also not any knowledge at
> all that it should render a fallback now.
>   


Right.  The component renderer knows:

- Whether or not a particular event exposed via the ClientBehaviorHolder 
contract is worthy of fallback behavior.  For example, the renderer for 
<h:inputText> knows that the "valueChange" event is special compared to, 
say, the "mouseOver" event - ie. that fallback behavior should be 
provided (if available) for the "valueChange" event, but not for the 
"mouseOver" event.
- Where fallback content should be placed (if available).
- Whether any ClientBehaviors are available for an event that warrants 
fallback behavior.

As you point out, however, the component renderer does not currently 
know whether a ClientBehavior or ClientBehaviors attached to such events 
are capable of producing fallback content.  So, if we want to support 
fallback content rendering in a generic way, we could add two new 
methods to ClientBehavior:

1. public boolean isFallbackAvailable()
2. public void renderFallback(BehaviorContext)

ClientBehaviors that are capable of providing fallback content would 
implement isFallbackAvailable() to return true.  Component renderers 
that care about providing fallback content would:

- Check to see whether JavaScript is disabled (need some standard API 
for this).
- If so, check to see whether they have any ClientBehaviors attached to 
events that warrant fallback content (eg. "valueChange" for <h:inputText>).
- If so, check to see whether any of these ClientBehaviors have fallback 
content available by calling isFallbackAvailable().
- If fallback content is available, figure out where to render the 
fallback content (this may involve altering the component's own rendered 
content - eg. may mean rendering rendering additional layout elements or 
styles).
- Finally, call renderFallback() to allow the ClientBehavior to produce 
the fallback content (eg. a "Refresh" button).

If it is too late to get something like this in for 2.0 - ie. if we 
cannot add these two methods to ClientBehavior, then we can still solve 
this problem in 2.1 by introducing a FallbackClientBehavior interface.  
This means that renderers will need to do an "instanceof 
FallbackClientBehavior" test when trying to determine whether fallback 
content is available, but this seems reasonable to me.


>
> in this case, we would really need to provide something in the spec
> which checks if javascript is enabled. Can we really still do this for
> 2.0?
>   

I know that the 2.0 spec has been handed off to the JCP - not sure how 
much leeway we have to add new APIs at this point.  Were you thinking of 
new methods on FacesContext?  Perhaps something like:

- public boolean isClientScriptingDisabled()
- public void setClientScriptingDisabled()

This might be useful even if we did not provide any automatic detection 
for the disabled scripting case, since it would at least provide a 
standard way for server-side code (components/renderers/behaviors) to 
easily determine whether to script-free content is required.

I do think it is possible to do everything that we discussed here in 
2.1.  If it is too late to support this in 2.0, perhaps we should add 
"disabled JavaScript support" to our 2.1 feature list - not just for 
ClientBehavior/AjaxBehavior - but for the more general 
<h:commandButton>/<h:commandLink> case as well.

>
> ok, now this is not so extremely easy. What we do in cs-JSF is we have
> a noscript tag - in this noscript tag, we render content which
> displays a modal dialog, telling the user that javascript is disabled
> and that he is forwarded to a javascript-free version of the page.
>   

Just curious - how do you do the forward?

> I don't see how we can easily do the same in the spec - what we could
> do is we could provide some information in the faces-messages section
> of the page including a link. This information would only be rendered
> in a noscript environment and - on clicking the link - would forward
> the user to the script-free version of the page.
>   

Yeah - seems like something along these lines would work.  Thanks for 
suggesting this solution!

We could consider encapsulating this in a "noscript" component that 
renders the appropriate content.

> Without this, we cannot check if javascript is enabled on the first
> page, and therefore we cannot make sure that rendering is done
> appropriately on the first page. Another option would be to let the
> application decide how it detects the JavaScript state

Right.  If we provide standard APIs for tracking the disabled scripting 
state (eg. FacesContext is/setClientScriptingDisabled()), then we could 
simply leave it up to the app developer to figure out when to call 
setClientScriptingDisabled(true), at least for the moment.  Of course, 
it would be good to provide a standard mechanism for automatically 
detecting the disabled scripting scenario, though I imagine this would 
need to wait until 2.1.

Andy

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.jboss.org/pipermail/jsr-314-open-mirror/attachments/20090412/9c2bb6e5/attachment.html 


More information about the jsr-314-open-mirror mailing list