Skip to end of metadata
Go to start of metadata

The protocomponent expander was introduced as an experimental facility to speed up work in Fluid Engage, but was only partially implemented. However, it was considered useful enough to base work on from Collectionspace, which is exposing many limitations of the incomplete implementation. This is a set of very sketchy development notes to aid thinking, which will eventually be upgraded to a proper specification and API.

Here is an example of the kind of tree the expander supports from Engage:

  var proto = {
            navBarTitle: "%title",
            displayDate: "%displayDate",
            shortDescription: "%shortDescription",
            description: {markup: model.introduction ? model.introduction : model.content},
            guestbookHeader: {messagekey: "guestbookHeader", args: {size: "%comments"}},
            guestbookLink: guard(model.comments, {target: "%guestbookLink"}),
            guestbookLinkText: guard(model.comments, {messagekey: "guestbookLinkText"}),
            image: {
                target: "%image",
                decorators: {
                    type: "attrs",
                    attributes: {
                        alt: model.title
            catalogueLink: {target: "%catalogueLink"},
            catalogueLinkText: {messagekey: "catalogueLinkText"},
            aboutLink: {target: "%aboutLink"},
            aboutLinkText: {messagekey: "aboutLink"},
            title: "%title",
            guestbookInvitation: guard(!model.comments, {messagekey: "guestbookInvitationString"})

Some notes - the EL style is "variable" - this aspect is fairly harmless. The most problematic aspect of the expander (for implementation) is its capability to "instrospect" into the structure of components - and presumably also other things, such as decorators, to look for EL strings of the proper kind. One of the most serious bugs that it has at present is misrecognising the elements of a UISelect like the following:

            var protoTree = {
                "authority-history": "${fields.history}",
                "contact-addressType1": {
                    "selection": "${fields.addressType1}",
                    "optionlist": ["Home", "Work"],
                    "optionnames": ["home", "work"]

The elements

["Home", "Work"]

which appear inside "optionlist" at present are simply expanded according to the same string rules, and end up upgraded to "false UIBound" components with the structure [{value: "Home"}, {value: "Work"}] which as well as being unhelpfully verbose, are not supported by the renderer. Various aspects of the early year work are now coming back to me, with the point that the Renderer was upgraded to recognise this notation in a number of cases (in particular the "flat components" UILink, UIVerbatim, UIMessage now all support the "spurious value syntax", e.g.

  aboutLink: {target: {value: {messagekey: ...

I THINK but am not sure that it should actually be possible to just keep nesting value/messagekey indefinitely with no effect but have "the last one win". This is probably undesirable.

Anyway - we should certainly forbid "spurious value" as a member of the lists in UISelect. Or even in UIBoundMany which hasn't really existed since the RSF days but is still probably "morally valid" if it were possible to render it.

Stab at characterising a rule

It seems that a string appearing as a bare value in an array should simply be left alone. However - just because we are inside a list presumably doesn't mean we should stop recursing. Certainly for the UISelect lists it doesn't seem helpful/desirable that any of the elements may be independently bound, e.g.

{{["Home", {valuebinding: "%path"]}} 

is not supported and probably shouldn't be. BUT - for example, the "args" structure of a decorator might well contain values to be fixed up. However, value/valuebinding would be just as unhelpful here. All the same, it seems desirable to support some form of EL referencing - but perhaps we should just punt on this and wait until we are coding with IoC trees. On the other hand, having derived this EL syntax, perhaps we should

The current impl currently has no "sense"

It doesn't recognise whether it is traversing component material or not, and has no sense of "where it is" in a structure. Presumably after passing a "raw array boundary" or something like "args" it should realise that it is no longer expected to be recognising/producing components. But perhaps it should still be "resolving EL references in the raw".

"Double valuebinding"

A similar issue is the tendency of AC in CSpace currently to write things like

{componentID: {
  valuebinding: "${path}"}

which currently gets sxpanded to

{componentID: {
  valuebinding: {
    valuebinding: "path"

There is currently explicit code in the CSpace fix up to find this and unwrap it. The question is whether to "support" this syntax? This connects with the previous discussion in that seeing "valuebinding" could be considered another "component territory boundary"

Current issues

"expandComponentSingle" is the current de facto entry point "back into" the expander, both from itself and from breakout expanders. Currently it dispatches directly ONLY to expandComponent, indicating these should be fused. This combined operation is the one that has "no sense" - and is the reason for the difference in "pushers". There is actually a tighter grammar on component trees than this code tried to be aware of - a "trunk component" can be recognised in closed form by the renderer, and each of its fields can be known to be either UIBound or "terminals". Possibly the concept of the "pusher" can go away.

The logic in "expandChildren" looks really paradoxical - the list of children is only created ONCE WE RECOGNISE WE ARE IN A CHILD, which would seem to imply that we could only deal with sets of children which are two deep... but this doesn't seem to be the case.

Presumably some of the final "else" block in "expandMembers" is redundant with the odd branch in "expandComponent" that makes some kind of opportunistic attempt to recognise a UIBound.

  • No labels