What is the best approach to implement conditional widgets in Javascript, i.e., to selectively show/hide widgets based on other widget selection(s)?
As discussed, Metawidget tries hard not to 'dictate' anything about your architecture. Therefore the 'best' approach depends on your needs. However here are two broad solutions:
1. Have Metawidget Do All The Work
This approach is the most flexible. Basically:
- First, define an Inspector or InspectionResultProcessor that knows how to return different results based on different conditions. For example, Angular Metawidget has an InspectionResultProcessor that can process Angular expressions
- Next, define a WidgetProcessor that knows how to kick off a complete Metawidget re-inspection based on some change
Combining these two gives you some awesome capabilites. Here's a complete example:
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js" type="text/javascript"></script>
<script src="http://metawidget.org/js/3.8/metawidget-core.min.js" type="text/javascript"></script>
<script src="http://metawidget.org/js/3.8/metawidget-angular.min.js" type="text/javascript"></script>
<script type="text/javascript">
angular.module( 'myApp', [ 'metawidget' ] ).controller( 'myController', function( $scope ) {
$scope.metawidgetConfig = {
inspector: new metawidget.inspector.JsonSchemaInspector( {
properties: {
type: {
enum: [ 'Car', 'Truck', 'Aeroplane' ]
},
model: {
enum: [ 'Saloon', '4WD', 'Sports' ],
hidden: '{{vehicle.type != "Car"}}'
},
description: {
type: 'string',
large: true,
hidden: '{{vehicle.type != "Truck"}}'
},
details: {
hidden: '{{vehicle.type != "Aeroplane"}}',
properties: {
wingspan: {
type: 'number'
},
speed: {
type: 'number'
}
}
}
}
} )
}
$scope.vehicle = {};
} );
</script>
<style>
#metawidget {
border: 1px solid #cccccc;
width: 300px;
border-radius: 10px;
padding: 10px;
margin: 50px auto;
display: block;
}
</style>
</head>
<body ng-controller="myController">
<metawidget id="metawidget" ng-model="vehicle" config="metawidgetConfig" />
</body>
</html>
This UI will show/hide multiple widgets depending on the select box, and it's all defined declaratively!
2. Leverage Standard DOM Manipulation
This approach is less flexible, but also has less 'magic' in how it works. Basically:
- First, add metadata to denote when widgets are conditional
- Next, define a WidgetProcessor to attach standard DOM manipulation to those widgets. Metawidget helps by generating reliable IDs for everything
Here's a complete example using pure JavaScript:
<html>
<head>
<script src="http://metawidget.org/js/3.8/metawidget-core.min.js"></script>
<style>
#metawidget {
border: 1px solid #cccccc;
width: 250px;
border-radius: 10px;
padding: 10px;
margin: 50px auto;
}
#table-model-row {
visibility: hidden;
}
</style>
</head>
<body>
<div id="metawidget"></div>
<script type="text/javascript">
var mw = new metawidget.Metawidget( document.getElementById( 'metawidget' ), {
inspector: new metawidget.inspector.JsonSchemaInspector( {
properties: {
make: {
enum: [ 'BMW', 'Ford', 'Toytota' ],
onSelect: 'model'
},
model: {
enum: [ 'Saloon', '4WD', 'Sports' ]
}
}
} ),
addWidgetProcessors: [
function( widget, elementName, attributes, mw ) {
if ( attributes.onSelect !== undefined ) {
widget.setAttribute( 'onchange', 'document.getElementById("table-' + attributes.onSelect + '-row").style.visibility="visible"' );
}
return widget;
}
]
} );
mw.toInspect = {};
mw.buildWidgets();
</script>
</body>
</html>
Feedback welcome!