Opened 9 years ago

Closed 6 years ago

#10800 closed enhancement (fixed)

Pre-rendered dijit Widgets

Reported by: Sam Foster Owned by: Sam Foster
Priority: low Milestone: 1.9
Component: Dijit Version: 1.4.0
Keywords: Cc:
Blocked By: Blocking:

Description

A few people have mentioned the idea of pre-rendering the HTML normally dynamically generated by widgets which mixin dijit_Templated.

This has potential performance advantages through moving rendering logic to the server, or outside the normal page init phase. Also the ability to "unhook" a widget from its DOM, and re-hook it opens other doors - such as freezing/thawing UI state to/from a html string + json structure.

Attachments (3)

10800_prerenderedTemplated.patch (8.6 KB) - added by Sam Foster 9 years ago.
[CLA] [PATCH] Exploration of a _RenderedTemplated mixin.
dojo.dtl._Templated.r16679.js.patch (1.8 KB) - added by Mark Wubben 9 years ago.
Patches to my DTL _Templated to work with rendered widgets
RenderedWidget.js (4.1 KB) - added by Mark Wubben 9 years ago.
Subclass of dijit._Widget and dojox.dtl._Templated to support rendered widgets

Download all attachments as: .zip

Change History (17)

Changed 9 years ago by Sam Foster

[CLA] [PATCH] Exploration of a _RenderedTemplated mixin.

comment:1 Changed 9 years ago by Sam Foster

10800_prerenderedTemplated.patch has dijit._RenderedTemplated mixin, that is based on and analagous to dijit._Templated. The dijit/tests/test_RenderedTemplate.html has a simple proof of concept - a regular TitlePane instance's outer HTML is cloned and inserted into the DOM, and a new instance of dijit.tests.PrerenderedTitlePane created from it. dijit.tests.PrerenderedTitlePane is a thin TitlePane subclass which mixes in _RenderedTemplated.

The core of this is a attachQueryMap object, which pairs CSS selectors to an options object:

'> .dijitTitlePaneTitle': { 
	'point': 'titleBarNode,focusNode',
	'event': 'onclick:_onTitleClick, onkeypress:_onTitleKey'
},

'point' values correspond to dojoAttachPoint attributes you'd normally have in a template, 'event' to dojoAttachEvent. A couple more options are supported: 'position': 'first'|'last' describes which node from the query result to take, and 'optional': true/false determines if an error should be thrown if a query fails to match. Each selector is fed to dojo.query, with the srcNodeRef as the root node, and the attachments made as indicated.

The big issues here are in the performance hit running those queries. Widgets with a containerNode might have large subtrees inside them, and we risk false positives if the selectors arent' carefully anchored, and even if they are, performance may suffer.

If there was a way to walk over the DOM starting at the srcNodeRef, and at each node ask "Does this node match one of my attachQuery selectors", and to optionally stop or descend into a nodes child nodes (e.g. containerNode would not descend), this would seem viable.

comment:2 Changed 9 years ago by Sam Foster

Two more things:

1) The "does this node match my selectors" is the same problem we are discussing wrt. dojo.live (event delegation), and the closest() Nodelist method.

2) The _RenderedTemplated is a proof-of-concept, but ideally new subclasses of every widget would be unnecessary, and we could have a simple flag or even detect which strategy should be used when init-ing a widget.

comment:3 Changed 9 years ago by bill

Milestone: tbdfuture

Interesting. I figured the pre-rendered DOM nodes (in the page's markup) would have dojoAttachPoint=... and dojoAttachEvent=... on them and we'd scan the nodes like we do now. How do you think that compares to your solution?

If there was a way to walk over the DOM starting at the srcNodeRef, and at each node ask "Does this node match one of my attachQuery selectors", and to optionally stop or descend into a nodes child nodes (e.g. containerNode would not descend), this would seem viable.

Sounds simple, assuming you limit the selectors to simple non-hierarchical selectors and then use the fast path code (dojo._filterQueryResult()) to check if each node matches.

In any case, this might be a good project to host in dojox/ (or on the new plugin site) as it's not something I have the bandwidth to deal with in dijit for the forseeable future.

comment:4 Changed 9 years ago by Mark Wubben

I actually have code already that does this, including dojoAttachPoint/dojoAttachEvent, and nested widgets. Works with the DTL as well. It's not quite standalone but let me see if I can extract the useful bits.

Changed 9 years ago by Mark Wubben

Patches to my DTL _Templated to work with rendered widgets

Changed 9 years ago by Mark Wubben

Attachment: RenderedWidget.js added

Subclass of dijit._Widget and dojox.dtl._Templated to support rendered widgets

comment:5 Changed 9 years ago by Mark Wubben

OK, my code is in fact for the DTL, but hey. It's more hacked into the existing flow rather than reimagined, but perhaps it's useful.

comment:6 in reply to:  3 Changed 9 years ago by Sam Foster

Replying to bill:

Interesting. I figured the pre-rendered DOM nodes (in the page's markup) would have dojoAttachPoint=... and dojoAttachEvent=... on them and we'd scan the nodes like we do now. How do you think that compares to your solution?

Yeah my first pass did just that. But I think the attachQueryMap gives us a better progressive enhancement story, as well as more flexibility. You don't just have to be working with markup that was produced by dijit or with dijit specifically in mind. The css selector semantics are a natural fit here for me.

If there was a way to walk over the DOM starting at the srcNodeRef, and at each node ask "Does this node match one of my attachQuery selectors", and to optionally stop or descend into a nodes child nodes (e.g. containerNode would not descend), this would seem viable.

Sounds simple, assuming you limit the selectors to simple non-hierarchical selectors and then use the fast path code (dojo._filterQueryResult()) to check if each node matches.

I don't much like having that caveat. It seems like you either support CSS selectors or you don't, though you can advise strongly to keep them simple and/or specific for performance reasons. If we can't support arbitrary selectors, I'd rather just say we support a css className, and code just the dojo.hasClass fast path.

In any case, this might be a good project to host in dojox/ (or on the new plugin site) as it's not something I have the bandwidth to deal with in dijit for the forseeable future.

Agreed, it needs time to settle out in any case.

comment:7 Changed 9 years ago by merrix

Hey guys. I recently had a chat w/ Phiggins on IRC & Twitter about whether or not a Dijit could be applied to existing markup. He pointed me here. I believe YUI3 does the desired: http://developer.yahoo.com/yui/3/examples/slider/slider_from_markup.html

In certain situations, it's not acceptable to inject an element with a widget because you want it to appear without that initial delay; no matter how short it might be. For me, this situation has been with enterprise forms. I work with Zend Framework on the reg and if we can get this into Dojo it would help with some ideas I have about progressive enhancement using a combination of this, cookies, modernizr, and Zend_Form (although not exclusively tied to Zend Framework, just streamlined).

How can I brew up some interest in this again? @mark & @sfoster is your solution something I can put in my own library and require it, thus calling it in conjunction with Dijit or does it require that I modify source?

I'm optimistic.

comment:8 Changed 9 years ago by mattirwin

I can think of a few instances where this would be particularly useful as well. Hope it gets more attention.

comment:9 Changed 9 years ago by Mark Wubben

@merrix, I've got my own subclass of dijit._Widget, superc.Widget, with superc.PrerenderedWidget being a subclass of that. You can mix in the template renderer and it can either reuse the DOM or render from the template.

Some of our widgets share their template with the server, so I can push out the rendered widgets for progressive enhancement purposes, and then add more widgets dynamically later. I also use this to easily attach behavior to some DOM nodes (through dojoType attributes).

comment:10 Changed 9 years ago by Sam Foster

Owner: set to Sam Foster

Mark, can we see the source for your superc.PrerenderedWidget?? I made mine a drop-in replacement for _Templated so it could at least in theory be applied to any arbitrary dijit-based widget.

@merrix, the patch doesn't change anything in dijit/dojo, its purely additive, so you can apply it as-is (though I've not tested against recent trunk). I'm using this code on a couple of projects and it works well for me. But, there's a few things to do here before this can be considered done:

  • look into widgetsInTemplate support.
  • since I wrote this we got the custom "parser" in dijit, so I need to look into what if any implications that has here.
  • IIRC I punted on an issue with text nodes, but now I don't see it. Either way we'll need lots more tests
  • look again into a less expensive way of matching each attachPoint/attachEvent node to bind without dojo.query. I started working on the "does this node match this query" idea, but got discouraged as it was impossible to leverage *any* of the work already done in dojo.query. That's arguably an optimization that can wait anyhow, and may be best addressed as a doc note to avoid open ended selectors that use the descendant operator (" ").

I still think this is a valuable addition though, and your use case is exactly the kind of thing I had in mind. If you want to help, unit tests would be the best place to start. Lets assume the dijit._RenderedTemplated will move to dojox.widget._RenderedTemplated.

Mark: I looked over your code. Lets talk to see how to combine efforts here. I wanted to minimize DOM interactions as much as possible for speed's sake, and I see you cloneNode. But you also seem to have a lot more sanity checking in place and widgetsInTemplate support.

comment:11 Changed 9 years ago by entel

I've ran across this in the past and ended up writing my own widgets instead of utilizing Dijit. I use YUI3 now but I would love to see this sort of functionality come to Dojo. Please push this through.

comment:12 Changed 9 years ago by kevvy

Dojo kicks ass. Keep up the good work guys!

comment:13 Changed 7 years ago by bill

Priority: highlow

comment:14 Changed 6 years ago by bill

Milestone: future1.9
Resolution: fixed
Status: newclosed

It's there in 1.9 now with the new _AttachMixin mixin, plus the _rendered flag to _TemplatedMixin that tells it the widget has already been rendered on the server.

Note: See TracTickets for help on using tickets.