Friday, February 20, 2015

Create HTML forms from JavaScript objects: Metawidget 4.1

Version 4.1 of Metawidget, the library for creating HTML forms from JavaScript objects is now available!

This release was focused on:
  • JQuery Mobile layout improvements (suppressDivAroundLabel, suppressDivAroundWidget)
  • Improved support for JQuery Mobile overridden widgets
  • Top-level styleClass (JavaScript Metawidget)
  • Fix recursive save on Web Components
  • Bootstrap improvements (wrapInsideLabels, wrapWithExtraDiv)
  • Support table footers in HtmlWidgetBuilder (JavaScript Metawidget)
  • Windows Mobile Internet Explorer compatibility
  • Bug fixes, documentation and unit tests
As always, the best place to start is the Reference Documentation:

http://metawidget.org/doc/reference/en/pdf/metawidget.pdf

Your continued feedback is invaluable to us. Please download it and let us know what you think.

Friday, February 13, 2015

JavaScript Form Generator: from Java EE to Bootstrap 3 (Part 2)

I was recently asked to extend one of my previous blog posts with the ability to inspect nested, annotation-based, Java EE back-end domain objects using a JavaScript, Bootstrap 3 front-end.

Metawidget generates widgets for domain object properties using a five-stage pipeline, specifically WidgetBuilders. There are WidgetBuilders for creating text boxes for string types, number inputs for number types, and so on. However whenever Metawidget encounters a property that its WidgetBuilders don't have a widget for, it creates a nested Metawidget and starts the pipeline process all over again:

The nested Metawidget shares the same pipeline objects as the outer Metawidget (i.e. same Inspectors, WidgetBuilders, etc). This makes nested Metawidgets very lightweight and performant. However it also means the Inspectors must be aware they can be used in multiple modes: in 'top-level' mode, and in 'nested mode'.

Inspectors can tell the difference because of the names array they're passed. In nested mode, the names array will be populated with a path of sub-properties to traverse. It's important the Inspectors honour this.

Back to our Java EE example. We'll expect two back-end REST schema calls: one for /rest/schema/person and one for /rest/schema/person/address. Clearly the back-end must also be expecting this and return different schemas for each (we recommend using client-side expiry headers on REST-based schemas: schemas don't change very often, and you can increase the performance of your UI by not making calls unnecessarily).

I've put together a complete example you can download here.

Of course, you may find multiple REST calls unacceptable for your use-case. Another approach would be to have your REST service return a JSON schema containing all your nested schemas at once. Then use metawidget.inspector.JsonSchemaInspector to inspect it. JsonSchemaInspector includes the ability to automatically traverse nested schemas, based on the names array.

Monday, December 1, 2014

We won Hackagong 2014!

I spent last weekend down at Hackagong 2014. It's an annual hackathon where entrants are challenged to make something awesome in 30 hours. The idea is to form small teams mixing designers, programmers, engineers, entrepreneurs, game creators or anybody with an idea or problem to solve, and build a technology driven product or service prototype. There were over $100,000 of prizes on offer.

I didn't know what to expect going in, other than it was a great opportunity to get out my comfort zone and play with new technology. Competition was pretty fierce, with nearly 40 teams submitting some amazing projects using everything from Augmented Reality goggles to drones to 3D printers. But by the end, I was thrilled to be a member of the winning team!

Team mates Brooke, Caleb, Michael and I developed a 3D-printed, Internet-of-Things Egg Hatchery over the course of 30 hours. The judges described it as "an awesome effort":

Winner: Major Prize Winner
Winner: Most Innovative 3D Print

Here's a video of our winning entry (the rules dicatated the video had to be silent):

This was my first hackathon, and it was a fantastic experience. Many thanks to the organizers and everyone who participated!

Sunday, November 2, 2014

Domain Driven Forms: Metawidget 4.0

Version 4.0 of Metawidget, the library for domain driven forms is now available!

This release was focused on:
  • Web Components support
  • clearWidgets support (JavaScript version)
  • JsonSchemaTypeMappingProcessorConfig
  • Improved Node.js support
  • Boolean radio buttons (JavaScript version)
  • Minor refactoring
  • Bug fixes, documentation and unit tests
As always, the best place to start is the Reference Documentation:

http://metawidget.org/doc/reference/en/pdf/metawidget.pdf

Your continued feedback is invaluable to us. Please download it and let us know what you think.

Breaking Changes

We've taken advantage of the major version number bump to make some minor breaking changes for the sake of clarity and consistency. Specifically:
  • For metawidget.js, addInspectionResultProcessors has been renamed to appendInspectionResultProcessors
  • Similarly for metawidget.js, addWidgetProcessors has been renamed to appendWidgetProcessors
  • XmlUtils.elementToJsonSchema has been renamed to XmlUtils.inspectionResultToJsonSchema
  • For SwtMetawidget, setInspectionPath has been renamed to setPath

Saturday, November 1, 2014

Metawidget meets Web Components

The next release of Metawidget (v4.0) introduces Web Components!

Web Components are an exciting new HTML standard that promise to bring much of the UI goodness of frameworks like AngularJS to HTML without any additional libraries. As usual with emerging technologies, not all browsers fully support Web Components, but there are fantastic 'stop gap' libraries such as Polymer that can get you most of the way there today.

With Web Components, you can use Metawidget in fewer lines of code than ever before! Here's a complete example:

<!DOCTYPE HTML>
<html>
   <head>
      <script src="metawidget-core.min.js"></script>
      <style>
         #metawidget {
            border: 1px solid #cccccc;
            width: 250px;
            border-radius: 10px;
            padding: 10px;
            margin: 50px auto;
            display: block;
         }
      </style>
      <script>
         var person = {
            firstname: 'Homer',
            surname: 'Simpson',
            age: 36,
            save: function() {
               document.getElementById( 'metawidget' ).save();
               console.log( person );
            }
         }
      </script>
   </head>
   <body>
      <x-metawidget id="metawidget" path="person"></x-metawidget>
   </body>
</html>

Note the new x-metawidget tag. This is registered automatically just by including metawidget-core.min.js. It's available to your code just like any other HTML tag. It has custom methods such as save to make it easy to bind data back from the Metawidget, and automatically watches attributes such as path to rebuild the Metawidget.

Try it today in Chrome:

We've also ported the existing Address Book sample application to Web Components to provide a more meaty example:

Download it and give it a try! And welcome to the future :)

Thursday, June 26, 2014

Metawidget meets Bower

I was recently asked whether I could add Bower support to Metawidget.

I'm delighted to say this has now been set up. The repository is here and you can install it just like any Bower repo...

bower install metawidget

...or add it into your project's bower.json file...

{
   "name": "my-app",
   "dependencies": {
      ...
      "metawidget": "~3.9.0"
   },
   "private": true
}

Other Metawidget modules, such as metawidget-angular.min.js and metawidget-bootstrap.min.js, are also included in this repository.

Feedback welcome!

Monday, May 26, 2014

"Hey Bro, I heard you liked Dynamic UIs, so I put a Dynamic UI inside your Dynamic UI"

I was recently asked:

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:

<html ng-app="myApp">
   <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:

<!DOCTYPE HTML>
<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!