users@javaserverfaces-spec-public.java.net

[jsr344-experts mirror] [jsr344-experts] Re: [1089-HTML5-data-*attributes] PROPOSAL

From: Neil Griffin <neil.griffin_at_portletfaces.org>
Date: Mon, 18 Jun 2012 11:00:58 -0400

FWIW, saw this blog post today and thought I would pass it on:
http://balusc.blogspot.com/2012/06/adding-html5-attributes-to-standard-jsf.html

On Jun 10, 2012, at 9:56 AM, Frank Caputo wrote:

> Hi Phil,
>
> Am 07.06.2012 um 05:24 schrieb Phil Webb:
>
>> I do like the idea of not needing to revisit renderers to support passthough attributes, and it maps well with the information Ed sent earlier [1]. Having said that, there might be some complexities with existing code. For example, looking at PrimeFaces, in org.primefaces.component.inputtext.InputTextRenderer I noticed that no component is passed to startElement. With Mojarra, the com.sun.faces.renderkit.html_basic.MenuRenderer calls startElement several times with the same component (once for "select" and again for "option" and "optiongroup"). Not much can be done if the component is not passed, but the menu case is particularly interesting because I would expect passthough attributes and overrides to apply to "select" but not "option" or "optiongroup".
>
> I think the renderer should pass the correct child (UISelectItem(s)) to the ResponseWriter. Having this, we could embed f:passThroughAttributes inside the f:selectItem(s).
>
> Unfortunately there is no corresponding component for SelectItemGroup, so we can't passthrough attributes to the optiongroup tag.
>
> Cheers,
> Frank
>
>>
>> I guess the writer might need to keep a stack of the components (push on startElement, pop on endElement) and only apply overrides if the component does not appear higher in the stack. I am sure this can be solved, but it will definitely need some thought.
>>
>> [1] https://maven.java.net/service/local/repositories/snapshots/archive/javax/faces/javax.faces-api/2.2-SNAPSHOT/javax.faces-api-2.2-20120606.080027-1-javadoc.jar/!/renderkitdocs/index.html
>>
>>
>> ----- Original Message -----
>>> From: "Andy Schwartz" <andy.schwartz_at_oracle.com>
>>> To: jsr344-experts_at_javaserverfaces-spec-public.java.net
>>> Sent: Wednesday, June 6, 2012 6:19:47 PM
>>> Subject: [jsr344-experts] Re: [1089-HTML5-data-*attributes] PROPOSAL
>>>
>>> On 6/6/12 2:37 PM, Phil Webb wrote:
>>>> Thanks Andy, this is a fantastic summary that really helps clarify
>>>> the issues.
>>>
>>> Great, thanks for taking the time to read through my tl;dr mail! :-)
>>>
>>>> I pretty much agree with all the proposals except I wonder if it
>>>> might be sensible to start with proposal 1a (new tag) and before
>>>> 1b (namespace attributes). There are a couple of reasons for
>>>> this:
>>>>
>>>> - Having tags f:attribute, f:attributes and f:passThoughAttributes
>>>> without f:passThoughAttribute seems a little odd.
>>>> - Using f:passThoughAttribute will not break existing tooling and
>>>> so provides a more gradual route for upgrade.
>>>>
>>>
>>> Yes. Good points. The lack of symmetry between
>>> f:attribute/f:attributes
>>> and p:<attribute name>/f:passThroughAttributes was irking me as well.
>>>
>>>> I would like to see the namespace proposal implemented, but I think
>>>> it should be in addition to f:passThoughAttribute.
>>>>
>>>
>>> +1. Ending up with:
>>>
>>> - f:attribute/f:attributes
>>> - f:passThroughAttribute/f:passThroughAttributes
>>> - p:foo="bar" as a nifty conveniencee for <f:passThroughAttribute
>>> name="foo" value="bar"/>
>>>
>>> Is feeling right to me.
>>>
>>>
>>>> 6: +1 for user override. This leads to an interesting question
>>>> about how passthough attributes are rendered.
>>>
>>> Oh, yep. This is important...
>>>
>>>> At a technical level it feels like existing renderers will need
>>>> to be changed.
>>>
>>>
>>> I would like to avoid requiring Renderer changes if at all possible.
>>> Thinking we would try to hide all of this magic at the ResponseWriter
>>> implementation level. Roughly:
>>>
>>> In cases where a non-null component is provided,
>>> ResponseWriter.startElement() grabs the pass through attribute map
>>> from
>>> the component.
>>>
>>> In most cases, the pass through attribute map will be empty. Nothing
>>> interesting happens.
>>>
>>> In cases where the pass through attribute map is non-empty, the
>>> ResponseWriter kicks into a special pass through attribute handling
>>> mode.
>>>
>>> In this mode, the ResposneWriter monitors all calls to
>>> writeAttribute()/writeURIAttribute().
>>>
>>> For each attribute that is written, the ResposneWriter first checks
>>> to
>>> see whether an equivalent pass through value is available.
>>>
>>> If so, the pass through attribute value is written in place of the
>>> value
>>> that was passed into writeAttribute()/writeURIAttribute().
>>>
>>> When we reach the close of the start element (eg. startElement(),
>>> endElement(), writeText() is called), we blast out any remaining pass
>>> through attributes that haven't yet been written.
>>>
>>> Okay, I've ignored some of the complexities of doing this... it might
>>> end up being easier to just buffer up all attributes in a temporary
>>> map
>>> and blast everything out at the element close.
>>>
>>> In any case, I am hoping that we'll be able to come up with something
>>> that:
>>>
>>> a) works, and...
>>> b) doesn't impose too much new overhead, particularly for cases where
>>> no
>>> pass through attributes are present.
>>>
>>> The above proposal does impose some new overhead in all cases: we're
>>> adding one property lookup (to fetch the pass through attribute map)
>>> for
>>> every component. Adding any new per-component overhead always makes
>>> me
>>> nervous, though this seems relatively small. We'll want to
>>> performance
>>> test this, along with all of our other new JSF 2.2 features, before
>>> the
>>> spec goes final.
>>>
>>>
>>>> Do we need a ResponseWriter.writePassThoughAttributes(UIComponent
>>>> component) method. Some renderers might write more than one
>>>> element so I don’t see an easy way to indicate where passthough
>>>> attributes are applied unless per renderer
>>>
>>> I was assuming (and the above implementation sketch assumes) that
>>> pass
>>> through attributes would always be written out to component's root
>>> element.
>>>
>>> If we need something more sophisticated than this, we'll need to
>>> re-evaluate our strategy.
>>>
>>> Andy
>>>
>>>> (apologies if I have missed something in the earlier emails on
>>>> this).
>>>>
>>>> Cheers,
>>>> Phil.
>>>>
>>>>
>>>> ----- Original Message -----
>>>>
>>>>> From: "Andy Schwartz"
>>>>> To: jsr344-experts_at_javaserverfaces-spec-public.java.net
>>>>> Sent: Wednesday, June 6, 2012 9:49:04 AM
>>>>> Subject: [jsr344-experts] Re: [1089-HTML5-data-*attributes]
>>>>> PROPOSAL
>>>>>
>>>>> Gang -
>>>>>
>>>>> I wanted to take a shot at summarizing this discussion.
>>>>>
>>>>> Let's start with the good news: it seems that we agree on the core
>>>>> nature of this API - ie. we all want to add support for new
>>>>> Map<String,
>>>>> Object> that contains bonus attributes that are passed through to
>>>>> the
>>>>> rendered output.
>>>>>
>>>>> The bad news: we don't seem to agree on anything else. ;-)
>>>>>
>>>>> Open issues/questions:
>>>>>
>>>>> 1. How is this pass through attribute map populated?
>>>>>
>>>>> Let's start with the simplest case: I want to explicitly specify a
>>>>> small
>>>>> set of static pass through attributes.
>>>>>
>>>>> We discussed a few options:
>>>>>
>>>>> 1a) New tag
>>>>>
>>>>> <h:panelGroup rendered="#{el.to.rendered}>
>>>>> <f:passThroughAttribute name="role" value="banner"/>
>>>>> <f:passThroughAttribute name="aria-hidden"
>>>>> value="#{el.to.hidden}"/>
>>>>> </h:panelGroup>
>>>>>
>>>>> This would be similar to <f:attribute>, but populates the pass
>>>>> through
>>>>> attribute map instead of the component attribute map.
>>>>>
>>>>> 1b) Namespaced attributes
>>>>>
>>>>> To reduce verbosity, we could introduce a new namespace
>>>>> specifically
>>>>> for
>>>>> pass through attributes, eg:
>>>>>
>>>>> <h:panelGroup rendered="#{el.to.rendered} p:role="banner"
>>>>> p:aria-hidden="#{el.to.hidden}">
>>>>>
>>>>> Any attributes in this namespace would end up on the pass through
>>>>> attribute map.
>>>>>
>>>>> 1c) Non-namespaced/unknown attributes
>>>>>
>>>>> Another option would be to translate any "unknown" attributes
>>>>> specified
>>>>> on the tag to the pass through attribute map, eg:
>>>>>
>>>>> <h:panelGroup rendered="#{el.to.rendered} role="banner"
>>>>> aria-hidden="#{el.to.hidden}">
>>>>>
>>>>> The concerns about this are:
>>>>>
>>>>> - This is a change in behavior: Facelets already specifies that
>>>>> such
>>>>> attributes are added to the component attribute map. (Though we
>>>>> could
>>>>> make this configurable.)
>>>>> - This blurs the lines between component vs. pass through
>>>>> attributes
>>>>> in
>>>>> a way that can be confusing for page authors - ie. there is no
>>>>> easy
>>>>> way
>>>>> to determine whether a particular attribute corresponds to HTML
>>>>> markup
>>>>> or a component attribute.
>>>>> - Typos in attribute names would result in data being transferred
>>>>> to
>>>>> the
>>>>> client in a potentially non-obvious way.
>>>>>
>>>>> Proposal:
>>>>>
>>>>> Let's start with 1b).
>>>>>
>>>>> I understand the motivation behind 1c). We would like to move in
>>>>> a
>>>>> direction whereby web designers can feel more comfortable
>>>>> authoring
>>>>> JSF-based pages using familiar technologies (eg. HTML5 instead of
>>>>> <h:*>). However, I think that the way to tackle this is by
>>>>> enhancing
>>>>> our jsfc/pass through element solution. Let's see what we can do
>>>>> that
>>>>> before we consider mixing HTML + component attributes on our
>>>>> component-centric tags.
>>>>>
>>>>> 2. What about the case where we need to dynamically specify an
>>>>> arbitrary # of pass through attributes?
>>>>>
>>>>> As Paul pointed out, we need to support cases where the page
>>>>> author
>>>>> doesn't necessarily know all of the pass through attributes ahead
>>>>> of
>>>>> time. The set of pass through attributes may be determined
>>>>> dynamically
>>>>> (eg. via a managed bean/CDI or a web service).
>>>>>
>>>>> Two possibilities to address this:
>>>>>
>>>>> 2a) New tag
>>>>>
>>>>> <h:panelGroup rendered="#{el.to.rendered}>
>>>>> <f:passThroughAttributes value="#{el.to.map}"/>
>>>>> </h:panelGroup>
>>>>>
>>>>> The "value" attribute evaluates to a Map<String, Object>. Any
>>>>> entries
>>>>> in this map are added to the component's pass through attribute
>>>>> map.
>>>>>
>>>>> Imre pointed out that we've got a related problem for plain-old
>>>>> (non-pass through) component attributes, especially when dealing
>>>>> with
>>>>> composite components, eg:
>>>>>
>>>>> <cc:implementation>
>>>>> <h:commandButton styleClass="#{cc.attrs.styleClass}
>>>>> style="#{cc.attrs.style}"
>>>>> onclick="#{cc.attrs.onclick}"
>>>>> title="#{cc.attrs.title}"
>>>>> value="#{cc.attrs.value}"
>>>>> isThisAnnoying="#{cc.attrs.yes}"/>
>>>>> </cc:implementation>
>>>>>
>>>>> If we're going to add a mechanism for allowing multiple pass
>>>>> through
>>>>> attributes to be specified via EL, perhaps we should generalize
>>>>> this
>>>>> to
>>>>> include support for normal (non-pass through) component attributes
>>>>> as
>>>>> well, eg:
>>>>>
>>>>> <cc:implementation>
>>>>> <h:commandButton>
>>>>> <f:attributes value="#{some:filter(cc.attrs)}"/>
>>>>> </h:commandButton>
>>>>> </cc:implementation>
>>>>>
>>>>> We could then either:
>>>>>
>>>>> - Control the target map via an attribute, eg.
>>>>>
>>>>> <f:attributes value="#{el.to.map}" target="passthrough |
>>>>> component"/>
>>>>>
>>>>> Or...
>>>>>
>>>>> - Break this out into two tags:
>>>>>
>>>>> <f:attributes
>>>>> value="#{el.to.map.containing.bonus.component.attrs}">
>>>>> <f:passThroughAttributes
>>>>> value="#{el.to.map.containing.bonus.passthrough.attrs}"/>
>>>>>
>>>>> 2b) Namespaced attribute
>>>>>
>>>>> Similar to 1b), we could do:
>>>>>
>>>>> <h:panelGroup rendered="#{el.to.rendered}
>>>>> p:attributes="#{el.to.map}">
>>>>>
>>>>> That is, we could treat "p:attributes" as a special case of the
>>>>> pass
>>>>> through namespace that is used to explicitly populate the pass
>>>>> through
>>>>> attribute map.
>>>>>
>>>>> I kind of like 1b), though it doesn't quite extend as well to
>>>>> Imre's
>>>>> use
>>>>> case.
>>>>>
>>>>> Proposal:
>>>>>
>>>>> Let's do 2a).
>>>>>
>>>>> I would prefer that we do this via two new tags:
>>>>>
>>>>> <f:attributes
>>>>> value="#{el.to.map.containing.bonus.component.attrs}">
>>>>> <f:passThroughAttributes
>>>>> value="#{el.to.map.containing.bonus.passthrough.attrs}"/>
>>>>>
>>>>> Rather than a single multi-purpose tag:
>>>>>
>>>>> <f:attributes value="#{el.to.map}" target="passthrough |
>>>>> component"/>
>>>>>
>>>>> Though open to discussion on this.
>>>>>
>>>>> 3. When are #{el.to.map} expressions evaluated?
>>>>>
>>>>> Assuming we add something resembling:
>>>>>
>>>>> <f:passThroughAttributes value="#{el.to.map}"/>
>>>>>
>>>>> We'll need to specify when the value EL expression is
>>>>> evaluated/when
>>>>> the
>>>>> bonus attributes are made available via the target attribute map.
>>>>>
>>>>> We've got two options here:
>>>>>
>>>>> 3a) Specify that this work is performed at tag execution (Facelet
>>>>> handler appy) time.
>>>>> 3b) Specify that this work is performed during Faces lifecycle
>>>>> processing
>>>>>
>>>>> Option 3a) is way, way easier to implement. It has one small
>>>>> downside:
>>>>> this means that in stamping cases (eg. ui:repeat, h:dataTable), it
>>>>> will
>>>>> not be possible to EL bind to a different set of bonus attributes
>>>>> for
>>>>> each iteration/row.
>>>>>
>>>>> Proposal:
>>>>>
>>>>> Let's do 3a). It's slightly more limiting, but substantially more
>>>>> straightforward.
>>>>>
>>>>> As a result, these two snippets:
>>>>>
>>>>> <h:panelGroup>
>>>>> <f:attribute name="styleClass" value="pretty"/>
>>>>> </h:panelGroup>
>>>>>
>>>>> <h:panelGroup>
>>>>> <f:attributes
>>>>> value="#{el.to.map.containing.single.styleClass.entry}"/>
>>>>> </h:panelGroup>
>>>>>
>>>>> Would be equivalent, as would this:
>>>>>
>>>>> <h:panelGroup p:role="banner">
>>>>>
>>>>> And:
>>>>>
>>>>> <h:panelGroup>
>>>>> <f:passThroughAttributes
>>>>> value="#{el.to.map.containing.single.role.entry}"/>
>>>>> </h:panelGroup>
>>>>>
>>>>> 4. Do we need to support JSON literal/inline representations?
>>>>>
>>>>> The idea here would be to allow JSON literals to be specified
>>>>> either
>>>>> as
>>>>> attribute values:
>>>>>
>>>>> <f:passThroughAttributes value="{'role':'banner',
>>>>> 'aria-hidden':true}"/>
>>>>>
>>>>> Or possibly within the tag body, eg:
>>>>>
>>>>> <f:passThroughAttributes>
>>>>> {
>>>>> 'role' : 'banner',
>>>>> 'aria-hidden' : true
>>>>> }
>>>>> </f:passThroughAttributes>
>>>>>
>>>>> These JSON literals would include support for inline value
>>>>> expressions.
>>>>> We've also considered implementing a name decoration facility for
>>>>> making
>>>>> it more convenient to specify multiple attributes that start with
>>>>> the
>>>>> same prefix.
>>>>>
>>>>> Proposal:
>>>>>
>>>>> Seems like this isn't going to be a popular proposal, but, how
>>>>> about:
>>>>> no? :-)
>>>>>
>>>>> For cases where the page author needs to specify a few well-known
>>>>> attributes, our namespaced-based approach seems much
>>>>> simpler/cleaner.
>>>>>
>>>>> That is, I much prefer:
>>>>>
>>>>> <h:panelGroup rendered="#{el.to.rendered} p:role="banner"
>>>>> p:aria-hidden="#{el.to.hidden}">
>>>>>
>>>>> Over:
>>>>>
>>>>> <h:panelGroup>
>>>>> <f:passThroughAttributes>
>>>>> {
>>>>> 'role' : 'banner',
>>>>> 'aria-hidden' : #{el.to.hidden}
>>>>> }
>>>>> </f:passThroughAttributes>
>>>>> </h:panelGroup>
>>>>>
>>>>> For the arbitrary # of dynamically determined attributes case,
>>>>> literal/inline JSON representation does not help.
>>>>>
>>>>> Let's keep things simple and hold off on introducing literal JSON
>>>>> representations.
>>>>>
>>>>> This might be something that we'll want re-evaluate, though I
>>>>> would
>>>>> prefer that we tackle this separately from our pass through
>>>>> attribute
>>>>> solution. Ideally we should look at this in a generic manner that
>>>>> takes
>>>>> other collection-based attribute cases (eg. h:dataTable's value
>>>>> attribute) into account.
>>>>>
>>>>> 5. Do we need to support EL-binding to JSON strings?
>>>>>
>>>>> This would be to address the case where bonus attributes are
>>>>> determined
>>>>> via some 3rd party code (eg. a web service or a CDI producer) that
>>>>> coughs up a JSON string, eg:
>>>>>
>>>>> <f:passThroughAttributes value="#{el.to.jsonString}"/>
>>>>>
>>>>> Proposal:
>>>>>
>>>>> I would prefer that we keep our solution simple and only support
>>>>> binding
>>>>> to Map<String, Object>, at least for this first pass. Like #4, if
>>>>> we
>>>>> decide that this is an important use case to tackle, I would
>>>>> prefer
>>>>> that
>>>>> we evaluate this from a more generic perspective - eg. should
>>>>> ui:repeat
>>>>> support binding to a JSON string-specified array?
>>>>>
>>>>> Rather than complicating EL requirements, an alternate approach
>>>>> would
>>>>> be
>>>>> to expose an EL functions that are capable of performing
>>>>> translations
>>>>> from JSON string to various collection types, eg:
>>>>>
>>>>> <f:passThroughAttributes
>>>>> value="#{jsf.jsonToMap(el.to.jsonString)}"/>
>>>>>
>>>>> 6. Who wins: pass through or renderer attribute?
>>>>>
>>>>> Frank raised this use case:
>>>>>
>>>>> <h:inputText value="#{el.value} p:type="email"/>
>>>>>
>>>>> However, the inputText renderer already renders the type
>>>>> attribute.
>>>>>
>>>>> Should the result be that inputText renderer wins, in which case
>>>>> the
>>>>> rendered HTML ends up as:
>>>>>
>>>>> <input type="value">
>>>>>
>>>>> Or the pass through attribute wins, in which case the rendered
>>>>> HTML
>>>>> is:
>>>>>
>>>>> <input type="email">
>>>>>
>>>>> Proposal:
>>>>>
>>>>> Allowing page authors to override renderer-produced markup is a
>>>>> tricky
>>>>> business. This will lead to situations where the page author
>>>>> breaks
>>>>> functionality that the renderer is relying on. However, it is
>>>>> also
>>>>> tremendously powerful, as shown in Frank's trivial use case above.
>>>>>
>>>>> It's a bit unusual for me, but in this case I would recommend that
>>>>> we
>>>>> give our users this rope - ie. I would like to see pass through
>>>>> attributes trump others. I am concerned that if we don't do this,
>>>>> we
>>>>> will be artificially limiting the usefulness of our pass through
>>>>> attribute solution.
>>>>>
>>>>> Again, open to more discussion on this.
>>>>>
>>>>> 7. Do we need to add new HTML5 attributes to our tags anyway?
>>>>>
>>>>> Er… pass through attributes are great, particularly for cases
>>>>> where
>>>>> our
>>>>> components/tags fall behind markup language specs (HTML5, Aria,
>>>>> etc…).
>>>>>
>>>>> However, we probably need to take a look at whether there is some
>>>>> subset
>>>>> of HTML5-related attributes that should be exposed as proper
>>>>> component
>>>>> attributes on the h:* components.
>>>>>
>>>>> One area that is particularly interesting is events, since we'll
>>>>> likely
>>>>> want to allow ClientBehaviors (eg. f:ajax) to be registered for
>>>>> various
>>>>> new client-side events. Our pass through attributes solution
>>>>> does
>>>>> not
>>>>> help with this.
>>>>>
>>>>> No proposal here, but wondering whether perhaps this is an area
>>>>> that
>>>>> someone has already looked into? If not, we should add it to the
>>>>> 2.2
>>>>> todo list (separate from pass through attributes).
>>>>>
>>>>> SUMMARY
>>>>>
>>>>> Let's add a new namespace for specifying pass through attributes
>>>>> on
>>>>> any
>>>>> component tags, eg:
>>>>>
>>>>> <h:panelGroup rendered="#{el.to.rendered} p:role="banner"
>>>>> p:aria-hidden="#{el.to.hidden}">
>>>>>
>>>>> Let's add two tags for specifying EL bindings to Map<String,
>>>>> Object>
>>>>> that can be used to populate the component attribute and pass
>>>>> through
>>>>> attribute maps with bonus attributes, eg:
>>>>>
>>>>> <f:attributes
>>>>> value="#{el.to.map.containing.bonus.component.attrs}">
>>>>> <f:passThroughAttributes
>>>>> value="#{el.to.map.containing.bonus.passthrough.attrs}"/>
>>>>>
>>>>> (Alternatively, one <f:attributes> tag with an attribute for
>>>>> specifying
>>>>> the map to target.)
>>>>>
>>>>> Attribute maps (both component and pass through) will be populated
>>>>> at
>>>>> tag execution time (ie. no ability to bind to a row-specific map.)
>>>>>
>>>>> Pass through attributes trump renderer-specified attributes.
>>>>>
>>>>> No inline or EL JSON support.
>>>>>
>>>>> That's it.
>>>>>
>>>>> Possible follow on issues:
>>>>>
>>>>> - Support for JSON literals in collection-based attributes.
>>>>> - Support for EL-binding to JSON-based strings (possibly via an EL
>>>>> function) for collection-based attributes.
>>>>> - Support for new HTML5 attributes on h:* tags.
>>>>>
>>>>> Thoughts?
>>>>>
>>>>> Andy
>>>>>
>>>>>
>>>>>
>>>
>>>
>>
>