Added support for immediate widget initialization (versus waiting for DOM ready event)

Also, better support for initializing widgets in async fragments.
This commit is contained in:
Patrick Steele-Idem 2014-11-04 11:05:48 -07:00
parent 0279d17c46
commit 7c8c8a8f49
8 changed files with 94 additions and 56 deletions

View File

@ -6,9 +6,9 @@ var initWidgets = require('./init-widgets');
var EventEmitter = require('events').EventEmitter;
var inherit = require('raptor-util/inherit');
function WidgetsContext(context) {
function WidgetsContext(out) {
EventEmitter.call(this);
this.context = context;
this.out = out;
this.widgets = [];
this.widgetStack = [];
}
@ -54,7 +54,7 @@ WidgetsContext.prototype = {
_this.widgets.push(widgetDef);
}
widgetStack.push(widgetDef);
this.emit('beginWidget', widgetDef);
return widgetDef;
@ -67,7 +67,7 @@ WidgetsContext.prototype = {
this.widgetStack = [];
},
_nextWidgetId: function () {
return 'w' + uniqueId(this.context);
return 'w' + uniqueId(this.out);
},
initWidgets: function () {
var widgetDefs = this.widgets;
@ -81,11 +81,11 @@ WidgetsContext.prototype = {
inherit(WidgetsContext, EventEmitter);
WidgetsContext.getWidgetsContext = function (context) {
var attributes = context.attributes;
WidgetsContext.getWidgetsContext = function (out) {
var global = out.global;
return attributes[WIDGET_CONTEXT_KEY] ||
(attributes[WIDGET_CONTEXT_KEY] = new WidgetsContext(context));
return global[WIDGET_CONTEXT_KEY] ||
(global[WIDGET_CONTEXT_KEY] = new WidgetsContext(out));
};

View File

@ -304,21 +304,29 @@ exports.initClientRendered = function(widgetDefs) {
* @param {boolean} If the
*/
exports.initServerRendered = function(scanDOM) {
ready(function() {
var idsEl = document.getElementById('rwidgets');
if (!idsEl && !scanDOM) { // If there is no index and "scan DOM" is not set to true then do nothing
return;
function doInit() {
var dataIds;
if (typeof scanDOM === 'string') {
dataIds = scanDOM;
scanDOM = false;
} else {
var idsEl = document.getElementById('rwidgets');
if (!idsEl && !scanDOM) { // If there is no index and "scan DOM" is not set to true then do nothing
return;
}
// Make sure widgets are only initialized once by checking a flag
if (document.rwidgetsInitialized === true) {
return;
}
// Set flag to avoid trying to do this multiple times
document.rwidgetsInitialized = true;
dataIds = idsEl ? idsEl.getAttribute('data-ids') : null;
}
// Make sure widgets are only initialized once by checking a flag
if (document.rwidgetsInitialized === true) {
return;
}
// Set flag to avoid trying to do this multiple times
document.rwidgetsInitialized = true;
var dataIds = idsEl ? idsEl.getAttribute('data-ids') : null;
var initContext = new InitContext();
if (dataIds == null || dataIds === '*') { // If the data-ids attribute is * then server is tell us we need to scan the DOM
initAllWidgetsInDOM(initContext);
@ -336,5 +344,13 @@ exports.initServerRendered = function(scanDOM) {
initWidgetFromEl(el, initContext);
}
}
});
}
if (typeof scanDOM === 'string') {
doInit();
} else {
ready(doInit);
}
};

View File

@ -60,4 +60,8 @@ raptorPubsub
if (widgetsContext) {
widgetsContext.initWidgets();
}
});
});
window.$rwidgets = function(ids) {
initWidgets.initServerRendered(ids);
};

View File

@ -112,17 +112,19 @@ exports.attrs = function(widget) {
return attrs;
};
exports.writeInitWidgetsCode = function(widgetsContext, context, options) {
exports.writeInitWidgetsCode = function(widgetsContext, out, options) {
var clearWidgets = true;
var scanDOM = false;
var immediate = false;
if (options) {
clearWidgets = options.clearWidgets !== false;
scanDOM = options.scanDOM === true;
immediate = options.immediate === true;
}
if (scanDOM) {
context.write(TAG_START + '*' + TAG_END);
out.write(TAG_START + '*' + TAG_END);
} else {
var widgets = widgetsContext.getWidgets();
@ -134,12 +136,6 @@ exports.writeInitWidgetsCode = function(widgetsContext, context, options) {
var commaRequired = false;
var writeWidgets = function(widgets) {
for (var i = 0, len = widgets.length; i < len; i++) {
writeWidget(widgets[i]);
}
};
var writeWidget = function(widget) {
if (widget.children.length) {
@ -156,9 +152,19 @@ exports.writeInitWidgetsCode = function(widgetsContext, context, options) {
ids += widget.id;
};
var writeWidgets = function(widgets) {
for (var i = 0, len = widgets.length; i < len; i++) {
writeWidget(widgets[i]);
}
};
writeWidgets(widgets);
context.write(TAG_START + ids + TAG_END);
if (immediate) {
out.write('<script type="text/javascript">$rwidgets("' + ids + '")</script>');
} else {
out.write(TAG_START + ids + TAG_END);
}
}
if (clearWidgets !== false) {

View File

@ -1,7 +1,7 @@
module.exports = function (context) {
var attrs = context.attributes;
if (!attrs._nextId) {
attrs._nextId = 0;
module.exports = function (out) {
var global = out.global;
if (!global._nextWidgetId) {
global._nextWidgetId = 0;
}
return attrs._nextId++;
return global._nextWidgetId++;
};

View File

@ -38,14 +38,16 @@
"renderer": "./taglib/init-widgets-tag.js",
"attributes": {
"function-name": "string",
"include-script-tag": "boolean"
"include-script-tag": "boolean",
"immediate": "boolean"
}
},
"init-widgets": {
"renderer": "./taglib/init-widgets-tag.js",
"attributes": {
"function-name": "string",
"include-script-tag": "boolean"
"include-script-tag": "boolean",
"immediate": "boolean"
}
}
}

View File

@ -1,22 +1,32 @@
var raptorWidgets = require('../');
var markoWidgets = require('../');
module.exports = function render(input, context) {
var widgetsContext = raptorWidgets.getWidgetsContext(context);
module.exports = function render(input, out) {
var widgetsContext = markoWidgets.getWidgetsContext(out);
if (context.featureLastFlush === false) {
// If the rendering context doesn't support the ability to know when all of the asynchronous fragmnents
var options = input.immediate ? {immediate: true} : null;
if (input.immediate === true) {
out.on('asyncFragmentFinish', function(eventArgs) {
var asyncFragmentOut = eventArgs.out;
var widgetsContext = markoWidgets.getWidgetsContext(asyncFragmentOut);
markoWidgets.writeInitWidgetsCode(widgetsContext, asyncFragmentOut, options);
});
}
if (out.featureLastFlush === false) {
// If the rendering out doesn't support the ability to know when all of the asynchronous fragmnents
// have completed then we won't be able to know which widgets were rendered so we will
// need to scan the DOM to find the widgets
raptorWidgets.writeInitWidgetsCode(widgetsContext, context, {scanDOM: true});
markoWidgets.writeInitWidgetsCode(widgetsContext, out, {scanDOM: true});
} else {
var asyncContext = context.beginAsync({ last: true, timeout: -1 });
context.once('last', function() {
if (!widgetsContext.hasWidgets()) {
return asyncContext.end();
var asyncOut = out.beginAsync({ last: true, timeout: -1 });
out.onLast(function(next) {
if (widgetsContext.hasWidgets()) {
markoWidgets.writeInitWidgetsCode(widgetsContext, asyncOut, options);
}
raptorWidgets.writeInitWidgetsCode(widgetsContext, asyncContext);
asyncContext.end();
asyncOut.end();
next();
});
}
};

View File

@ -4,16 +4,16 @@ var DUMMY_WIDGET_DEF = {
elId: function () {
}
};
module.exports = function render(input, context) {
module.exports = function render(input, out) {
var modulePath = input.module;
var config = input.config || input._cfg;
var widgetArgs = context.attributes.widgetArgs;
var widgetArgs = out.global.widgetArgs;
var id = input.id;
var scope = input.scope || context.getAttribute('widget');
var scope = input.scope || out.getAttribute('widget');
var assignedId = input.assignedId;
var events;
if (widgetArgs) {
delete context.attributes.widgetArgs;
delete out.global.widgetArgs;
scope = scope || widgetArgs.scope;
assignedId = assignedId || widgetArgs.id;
events = widgetArgs.events;
@ -21,7 +21,7 @@ module.exports = function render(input, context) {
if (!id && input.hasOwnProperty('id')) {
throw new Error('Invalid widget ID for "' + modulePath + '"');
}
var widgetsContext = widgets.getWidgetsContext(context);
var widgetsContext = widgets.getWidgetsContext(out);
if (modulePath) {