Wednesday, October 16, 2013

Angular Form Generator: third-party widgets

I've been asked about supporting date pickers in IE8 through Metawidget. There are two answers:

Answer #1: Metawidget only automates what you'd do manually

Metawidget tries hard not to introduce any 'magic' into your UI development. It only automates what you'd normally have to do manually. So by default if you have a property of type date it will generate:

<input type="date">

This is an HTML5-specific input type. It will only render as a date picker in HTML5-compliant browsers. And even then, only in some HTML5-compliant browsers (Chrome works, IE 10 and Firefox do not). IE8 will fall back to a text box.

Answer #2: Metawidget supports third-party widget libraries

Of course, there's no shortage of third-party widget libraries that can supply JavaScript-based date pickers. And these will work great in IE8. For this example, we'll pick Angular-UI Date. To use ui-date manually (outside of Metawidget) you have to do:

<input type="date" ui-date>

That's only a minor tweak over what Metawidget generates anyway, so we can plug-in a WidgetProcessor to process the generated widget and add ui-date. If the required HTML was radically different from what Metawidget usually generates (say some nested divs) we could plug-in a whole new WidgetBuilder.

Here's a complete example, tested in IE8:

<html ng-app="myApp">
   <head>
      <!--[if lte IE 8]>
      <script>
         document.createElement( 'metawidget' );
      </script>
      <![endif]-->
      <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.9.0/themes/base/jquery-ui.css" type="text/css" media="all" />
      <script src="jquery-1.8.3.js" type="text/javascript"></script>
<script src="jquery-ui-1.9.0.js" type="text/javascript"></script>
      <script src="angular-1.0.7.min.js" type="text/javascript"></script>
      <script src="ui-date.js" type="text/javascript"></script>
      <script src="http://metawidget.org/js/3.6/metawidget-core.min.js" type="text/javascript"></script>
      <script src="http://metawidget.org/js/3.6/metawidget-angular.min.js" type="text/javascript"></script>
      <script type="text/javascript">
         angular.module( 'myApp', [ 'metawidget', 'ui.date' ] )
            .controller( 'myController', function( $scope ) {
               $scope.metawidgetConfig = {
                  addWidgetProcessors: [ function( widget, elementName, attributes, mw ) {

                     if ( attributes.type === 'date' ) {
                        widget.setAttribute( 'ui-date', '' );
                     }

                     return widget;
                  } ]

               };
               $scope.person = {
                  firstname: 'Homer',
                  surname: 'Simpson',
                  date: new Date()
               };
            } );
      </script>
      <style>
         #metawidget {
            border: 1px solid #cccccc;
            width: 250px;
            border-radius: 10px;
            padding: 10px;
            margin: 50px auto;
            display: block;
         }
      </style>
   </head>
   <body ng-controller="myController">
      <metawidget id="metawidget" ng-model="person" config="metawidgetConfig"/>
   </body>
</html>

Feedback welcome!