Monday, March 11, 2013

Lightweight JSON to User Interface (UI) generator

I write a lot on this blog about customizing Metawidget for different scenarios. But it's worth stressing Metawidget also works as a lightweight, no fuss solution out-of-the-box.

For example, say you just wanted to quickly render a JSON object on the client. Here's the entire code:

<!DOCTYPE HTML>
<html>
   <head>
      <script src="http://metawidget.org/js/3.4/metawidget-core.min.js"></script>
      <style>
         #metawidget {
            border: 1px solid #cccccc;
            width: 250px;
            border-radius: 10px;
            padding: 10px;
            margin: 50px auto;
         }
      </style>
   </head>
   <body>
      <div id="metawidget"></div>
      <script type="text/javascript">
         var mw = new metawidget.Metawidget( document.getElementById( 'metawidget' ));
         mw.toInspect = {
            firstname: 'Homer',
            surname: 'Simpson',
            age: 36
         };
         mw.buildWidgets();

      </script>
   </body>
</html>

This will handily render:

Of course you can go much (much) deeper, but for once I'll resist the temptation to show that. If you're interested, start with the tutorial here.

27 comments:

Max said...

Does the java version support json too ?

Richard Kennard said...

Hi Max,

Not at this time, no. However that should be pretty easy to write. Can you give me a scenario you'd like it to cover?

Regards,

Richard.

Max said...

Well, by this blog I just realized it could be interesting to try use metawidget for our webservice tester in JBoss Tools - it supports wsdl and rest but it would mostly be for rest to allow editing and presenting the json response.

Thinking about pointing metawidget on a piece of json or wsdl xml and let it edit it but I guess I would still need to convert the UI back into json/wsdl ;)

Just a thought.

Richard Kennard said...

What would be your preferred UI framework for this (SWT, JSF, GWT etc)?

Max said...

that would be for SWT since it would be in the eclipse tooling first....but the general comprehension of json into what fields and types it has we could use independently of any UI framework.

i.e. to "scaffold" based on a rest/json service.

Richard Kennard said...

Hi Max,

Apologies for the delay. I have written a new blog for using JSON with SWT: http://blog.kennardconsulting.com/2013/04/java-based-json-and-json-schema-user.html

Could you please download the latest 3.3-SNAPSHOT release and give it a go? https://repository.jboss.org/nexus/content/repositories/snapshots/org/metawidget/modules/metawidget-all/3.3-SNAPSHOT/

Regards,

Richard.

ohad shai said...

Very nice!
Is it possible to transform the updated values back to json in the same format of the original one, in order to submit it to a server?

Richard Kennard said...

Ohad,

Thanks for your interest.

Yes, there are different BindingProcessors for each supported platform (AngularJS, JQuery UI, straight JavaScript etc). You just call 'processor.save()' to save the values back. See the documentation, or for the code see:

/integration-tests/js/corejs/allwidgets/src/main/webapp/index.html

Regards,

Richard.

ohad shai said...

Thanks.
I will try that. It will be nice to see in the above simple example how to process it back to json. from the documentation I understand it will be something like that:

mw.getWidgetProcessor( function( widgetProcessor ) {
return widgetProcessor instanceof metawidget.widgetprocessor.SimpleBindingProcessor;
} ).save( mw );

Richard Kennard said...

Yes. Exactly that, in fact.

If you wrap your line of code inside a save() function, and add a 'button onclick="save()"' tag to the page, that's all you need to do.

Richard Kennard said...

I posted a simple example as you requested:

http://blog.kennardconsulting.com/2013/07/generate-ui-from-json.html

ohad shai said...

Thank you very much.
I have one more questions that I couldnt find in documentation:
when I have a json array inside my object, is there any way to add/remove items by metawidget gui?

Thanks,
Ohad.

Richard Kennard said...

Manipulating arrays is fraught with personal choices about UI design. For example: is deletion done by a right-click menu? Or a delete icon on the end of each row? Or at the start of each row? Is addition done using a blank row at the end of the table, or as a footer row? Etc etc.

Because of this, out-of-the-box Metawidget only goes so far. You should see it render the array as a table? This is done by metawidget.widgetbuilder.HtmlWidgetBuilder.

For custom use cases, you can create your own WidgetBuilder and chain it together with the original ones using CompositeWidgetBuilder. Then your own WidgetBuilder can handle special cases (like arrays) and 'fall back' to the original WidgetBuilders for everything else.

As a shortcut, the existing HtmlWidgetBuilder also has some methods you can override like 'createTable' and 'addColumn'. You can see an example of doing this here:

https://github.com/kennardconsulting/digitalstructure/blob/master/js/controllers.js

That code overrides '_myWidgetBuilder.addColumn' to wrap the column text with an anchor tag.

Richard Kennard said...

So, just roughly, add something like the below to the code in this blog. It'll remove a row when you click on it.

---

var _myWidgetBuilder = new metawidget.widgetbuilder.HtmlWidgetBuilder();
var _superAddColumn = _myWidgetBuilder.addColumn;
_myWidgetBuilder.addColumn = function( tr, value, attributes, mw ) {

var td = _superAddColumn( tr, value, attributes, mw );

// Warp column contents with an anchor

var anchor = document.createElement( 'a' );
anchor.setAttribute( 'href', '#' );
anchor.setAttribute( 'onclick', 'this.parentNode.parentNode.parentNode.removeChild( this.parentNode.parentNode )' );
anchor.innerHTML = td.innerHTML;
td.innerHTML = '';
td.appendChild( anchor );
};
var mw = new metawidget.Metawidget( document.getElementById( 'metawidget' ), {
widgetBuilder: _myWidgetBuilder
} );
mw.toInspect = {
firstname: 'Homer',
surname: 'Simpson',
age: 36,
family: [ {
firstname: 'Marge',
surname: 'Simpson'
}, {
firstname: 'Bart',
surname: 'Simpson'
} ]
};

ohad shai said...

I think that my use case is a little bit more than that. I have a configuration that I would like to edit, that means sometimes add new sub-objects that weren't there before (and their structure can be realized from the schema). It's similar to how jenkins allows you to edit configuration but more complex (objects inside objects and arrays).
So it feels to me that I might end up writing a framework inside that framework.

What do you think? is metawidget should to be used that way?

Thanks,
Ohad.

Richard Kennard said...

Ohad,

Yes, like I said, array manipulation is fraught with personal choices around particular UI use cases.

Metawidget's basic answer is: WidgetBuilders.

Remember that Metawidget only strives to automate what you'd normally have to do manually. Metawidget is not trying to be a big new framework. So the correct approach is:

1. Work out how to do it manually
2. Write a WidgetBuilder to automate it

It sounds like in your case you may end up creating a little array widget to display/manipulate arrays. Or maybe just some HTML with some standard JS support functions. Either way, get that working first.

Then you just need to write a WidgetBuilder to hook that in. The WidgetBuilder instantiates your widget as opposed to a table, or a select box, or whatever it would normally do.

More information here: http://blog.kennardconsulting.com/2010/10/metawidget-collections-support.html

Richard Kennard said...

I've added a new blog post for you, with better examples:

http://blog.kennardconsulting.com/2013/07/json-ui-generator-array-support.html

ohad shai said...

Hi Richard,
Thanks for all the effort. I think this array implementation is much more similar to what I think as a sensible widget ( I would have add an "add" ability somehow at the bottom). I think that since other widgets has property of "readonly" it might be sensible to do something similar to arrays.
However, as I mentioned before my biggest concern regarding my use case is about items that appears in schema but are null (do not appear) in json object itself. is there any way to add the widget a possibility to add such objects?
Hope my question is clear.
Thanks anyway,
Ohad.

Richard Kennard said...

When you say 'items that appears in schema but are null' do you mean 'properties that appears in schema but are null'? So essentially a blank column in the table?

The example I have written at...

ttp://blog.kennardconsulting.com/2013/07/json-ui-generator-array-support.html

...includes exactly this. There is a property 'Employer' that is defined in the schema, but null in the JSON object.

Richard Kennard said...

Sure. Please send to support@metawidget.org

Charlie Kuharski said...

This is pretty impressive!

Vivek Todi said...

Hi Richard,

MetaWidget looks really impressive. We are using JSONSchema to express our forms, but I couldn't find much about it in the documentation. Have I missed something?

Regards,
Vivek

Richard Kennard said...

Vivek,

Thanks for your kind words. Please see the tutorial here:

http://metawidget.sourceforge.net/doc/reference/en/html/ch01s02.html

And the angular-addressbook and jqueryui-addressbook applications included in the examples download.

Also some more documentation here:

http://metawidget.sourceforge.net/doc/reference/en/html/ch04s02.html#section-inspectors-javascript-jsonschema

If there is something else you need, I can add more documentation?

Regards,

Richard.

Vivek Todi said...

Hi Richard,

Thanks for the help! The example is good!

Could you tell me which JSON-Schema version does metawidget work great with?

Regards,
Vivek

Richard Kennard said...

JSON Schema version 3.

The main difference to version 4 is that 'required' goes on the property itself (whereas in version 4 they moved 'required' outside to a separate use case, for reasons I don't fully understand).

Vivek Todi said...

Richard,

Thanks for the quick response, is that the only difference that affects the use of MetaWidget?

Or are there more differences?

Thanks & Regards,
Vivek

Richard Kennard said...

That's the only difference I'm aware of. If you find others please let me know!