Page MenuHomePhabricator

Enhancement of OOUI PHP widgets with JS
Closed, ResolvedPublic

Description

Part of "front-end standardisation" Q2 priority project.


Version: unspecified
Severity: enhancement

Event Timeline

bzimport raised the priority of this task from to High.Nov 22 2014, 3:46 AM
bzimport added a project: OOUI.
bzimport set Reference to bz72716.

I don't like the word "resurrect" for this, the widgets ain't dead. Better words include "reconstitute" or "infuse" (as in, infuse with JavaScript behaviors) or maybe just "rebuild".

werdna renamed this task from OOjs UI: Be able to resurrect OOUI PHP widgets into OOjs UI widgets to OOjs UI: Enhancement of OOUI PHP widgets with JS.Jan 30 2015, 6:35 PM

The big open questions:

  • Do we want to automatically enhance all existing PHP OOUI widgets found on page, or explicitly for selected elements only?
  • Do we want to reuse existing DOM tree from PHP for JS widgets, or just rebuild it from scratch?
  • How do we want to pass the configuration? Plain data is simple, but what about OOUI and DOM-like objects?

Example to consider is a DropdownWidget (which has radically different PHP and JS implementations) inside a FieldLayout (whose constructor accepts positional parameters) inside a FieldsetLayout (whose constructor accepts OOUI objects as parameters).

My suggested answers (based on what would be helpful for https://gerrit.wikimedia.org/r/187141):

  • Resurrect via an API like querySelector. That is, I should be able to do something like, var jsWidget = OO.ui.infuse('#widgetid');. That should resurrect the #widgetid widget along with all its child widgets (as necessary; possibly on-demand), and cache that value so that future calls to OO.ui.infuse('#widgetid') return the same JavaScript object.
  • Reuse existing DOM tree, where possible. But really it should be pretty transparent if you end up rebuilding it from scratch, so long as you don't lose configuration along the way. Delegating this to the widget is probably the easiest way, ie OO.ui.ButtonElement.infuse can assign this.$button to the appropriate existing DOM element and return true; but any widget that can't infuse (doesn't implement the infuse class method?) can just return false and the appropriate subgraph of the DOM will be rebuilt.
  • It's reasonable to ask the PHP side to specially mark "infusable" widgets, for example by passing "infusable=>true". That could trigger some PHP-side restrictions (like throwing an error if given positional parameters), as well as emit some extra attributes. For example, every OOUI object referenced in a constructor could have a unique id attribute auto-generated, and in the serialized configuration the OOui object would be replaced by its id.

Note that I'm assuming that top-level widgets will have unique IDs assigned by the author, so that those IDs can be used in an OO.ui.infuse(...) call on the JS side. This means that most children of that widget will have a unique selector rooted at that ID (possibly using the nth-child selector). So maybe we wouldn't actually need to create many new id attributes at all. On the other hand, it's pretty trivial to generate unique IDs, and having an ID for every referenced element may be the easiest way to get started. So maybe what we really want is a nameOf(Element $elem) helper on the PHP side, which in the first implementation can add a unique ID and return it. Future work might walk the DOM a little bit and see if we can avoid creating a new uniqueID by returning a path from some named ancestor.

@cscott: That sounds pretty good. Two points though:

  • Using HTML ids is messy and unnecessary. Our widgets have pretty good classes (we can add an additional one if needed). Then use a generic script to select and infuse those elements (instead of requiring every consumer to write their own infuse-call init script). We do the same with e.g. jQuery Tablesorter and makeCollapsible in MediaWiki core (page.ready.js).
  • This would naturally require a fair amount of refactoring in the many Widget constructor functions to separate creation of nodes from the other logic. That's a good thing to do in general. Though instead of separating it into two manual processes, deferring node handling to a template library that supports binding would give us the ability to "revive" a tree for free. We can consider that later, though.

@Krinkle: I still need to be able to name a specific object created on the PHP side. IDs are the right thing for that.

For example, in https://gerrit.wikimedia.org/r/187141 (well, the way that patch *should* be written) I need to be able to fetch a specific ButtonGroupWidget and then add/remove new buttons from it. Other users might wish to attach event handlers to specific widgets. Just blindly infusing everything doesn't give me any way to name the specific widget which I want to get a JS object for.

I've got a rough implementation for this. I'll push it to gerrit by the end of the day.

gerritbot subscribed.

Change 190367 had a related patch set uploaded (by Cscott):
WIP: infusion of PHP widgets with JS

https://gerrit.wikimedia.org/r/190367

Patch-For-Review

Change 190368 had a related patch set uploaded (by Cscott):
Implement OO.ui.infuse to reconstitute PHP widgets in client-side JS

https://gerrit.wikimedia.org/r/190368

Patch-For-Review

Tomorrow I'll try to update my patch to the Collection extension ( https://gerrit.wikimedia.org/r/187141 ) to use this, to demonstrate a practical use.

There were some comments about the 'infuse' name. I don't have any favorite paint colors here. But to make the discussion concrete, we need names for:

  1. The JS side method for "reanimating" a PHP-side widget. Currently this is OO.ui.infuse.
  2. The JS side method for "reanimating" the *contents* of a widget. Currently this is OO.ui.infuseContent. See below.
  3. The PHP-side property for marking widgets which can be reanimated by JS. Currently this is the config property infusable.
  4. The PHP-side method for serializing widget properties. Currently this is serialize but MatmaRex suggests getConfig for good reasons.
  5. The PHP-side method for serializing the *content* of a widget. This is serializeContent but https://phabricator.wikimedia.org/T89687#1044215 suggests using items instead. But a private/protected method of this sort may still be useful, so let's give it a good name.
  6. A PHP protected method for ensuring that a child Tag is "infusable" when we are serializing the parent. Currently this is ensureInfusableId.

Suggestions for a naming scheme for these six items are welcome.

Getting close! Last call for bikeshed painting!

Change 190367 merged by jenkins-bot:
Serialize PHP widget state into data-ooui attribute

https://gerrit.wikimedia.org/r/190367

Jdforrester-WMF renamed this task from OOjs UI: Enhancement of OOUI PHP widgets with JS to Enhancement of OOUI PHP widgets with JS.Feb 25 2015, 11:33 PM

Change 190368 merged by jenkins-bot:
Implement OO.ui.infuse to reconstitute PHP widgets in client-side JS

https://gerrit.wikimedia.org/r/190368