Inspired by a recent forum post and blog the next release of Metawidget (3.4) will be available as a Node.js module. This lets you use Metawidget to perform server side UI generation using JavaScript, if that's your requirement.
To install, simply use npm:
npm install metawidget
Then use the Metawidget API as normal:
var metawidget = require( 'metawidget' );
...
var mw = new metawidget.Metawidget( element );
mw.toInspect = {
name: "Joe Bloggs",
"DOB": "1/1/2001"
};
mw.buildWidgets();
// Print what was rendered
console.log( element.toString() );
This will render:
<table>
<tbody>
<tr id="table-name-row">
<th id="table-name-label-cell"><label for="name" id="table-name-label">Name:</label></th>
<td id="table-name-cell"><input type="text" id="name" name="name" value="Joe Bloggs"/></td>
<td></td>
</tr><tr id="table-DOB-row">
<th id="table-DOB-label-cell"><label for="DOB" id="table-DOB-label">DOB:</label></th>
<td id="table-DOB-cell"><input type="text" id="DOB" name="DOB" value="1/1/2001"/></td>
<td></td>
</tr>
</tbody>
</table>
Which of course can be customized using Metawidget's pipeline architecture.
Metawidget must be used in combination with a DOM
implementation. This can either be jsdom,
envjs, or even a simple implementation
of your own. For example:
var simpleDocument = {
createElement: function( elementName ) {
return {
nodeType: 1,
tagName: elementName.toUpperCase(),
attributes: [],
childNodes: [],
setAttribute: function( name, value ) {
for ( var loop = 0, length = this.attributes.length; loop < length; loop++ ) {
if ( this.attributes[loop].nodeName === name ) {
this.attributes[loop].nodeValue = value;
return;
}
}
this.attributes.push( {
nodeName: name,
nodeValue: value
} );
},
hasAttribute: function( name ) {
for ( var loop = 0, length = this.attributes.length; loop < length; loop++ ) {
if ( this.attributes[loop].nodeName === name ) {
return true;
}
}
return false;
},
getAttribute: function( name ) {
for ( var loop = 0, length = this.attributes.length; loop < length; loop++ ) {
if ( this.attributes[loop].nodeName === name ) {
return this.attributes[loop].nodeValue;
}
}
return null;
},
appendChild: function( childNode ) {
this.childNodes.push( childNode );
},
cloneNode: function() {
var clone = simpleDocument.createElement( elementName );
for ( var loop = 0, length = this.attributes.length; loop < length; loop++ ) {
var attribute = this.attributes[loop];
clone.setAttribute( attribute.nodeName, attribute.nodeValue );
}
for ( var loop = 0, length = this.childNodes.length; loop < length; loop++ ) {
clone.appendChild( this.childNodes[loop].cloneNode() );
}
return clone;
},
removeChild: function( childNode ) {
for ( var loop = 0, length = this.childNodes.length; loop < length; loop++ ) {
if ( this.childNodes[loop] === childNode ) {
this.childNodes.splice( loop, 1 );
return childNode;
}
}
throw new Error( "childNode not found: " + childNode );
},
ownerDocument: this,
toString: function() {
var toString = "<" + elementName;
for ( var loop = 0, length = this.attributes.length; loop < length; loop++ ) {
var attribute = this.attributes[loop];
toString += ' ' + attribute.nodeName + '="' + attribute.nodeValue + '"';
}
if ( this.value !== undefined ) {
toString += ' value="' + this.value + '"';
}
toString += ">";
for ( var loop = 0, length = this.childNodes.length; loop < length; loop++ ) {
toString += this.childNodes[loop].toString();
}
if ( this.innerHTML !== undefined ) {
toString += this.innerHTML;
}
toString += "</" + elementName + ">";
return toString;
}
};
},
createTextNode: function( data ) {
return {
nodeType: 3,
toString: function() {
return data;
}
}
}
};
var element = simpleDocument.createElement( 'div' );
Metawidget must be wrapped around a DOM element. The Metawidget constructor takes this
element, and thereafter always uses element.ownerDocument rather than
referencing any global document object.