JSON Forms – Day 6 – Custom Renderers

JSON Forms – Day 6 – Custom Renderers

JSON Forms is a framework to efficiently build form-based web UIs. These UIs allow end users to enter, modify, and view data and are usually embedded within a business application. JSON Forms eliminates the need to write HTML templates and Javascript for databinding by hand. It supports the creation of customizable forms by leveraging the capabilities of JSON and JSON schema and providing a simple and declarative way of describing forms. Forms are then rendered with a UI framework, currently one that is based on AngularJS. If you would like to know more about JSON Forms, the JSON Forms homepage is a good starting point.

In this blog series, we wish to introduce the framework based on a real-world example application, a task tracker called “Make It happen”. On day 1 we described the overall requirements, from day 2 to 5, we have created a fully working form for the entity “Task”. If you would like to follow this blog series please follow us on twitter, where we will announce every new blog post regarding JSON Forms.

So far, on the previous days, we have created a fully functional form rendered in AngularJS, by simply using two schemata: a JSON Schema to define the underlying data and a UI Schema to specify the UI. JSON Forms provides a rendering component, which translates this two schemas into a AngularJS form, including data binding, validation, rule-based visibility, and so on. While this is very efficient, you may wonder what you should do if the form rendered by JSON Forms does not exactly look like what you expected. Let us have a look at the form we have so far:

While the generic structure and the layout look pretty good, there are two controls, which could require some aesthetic improvement.

First, the checkbox for “done” is very small, we would rather have something like this:

Second, the control for “rating” is just a plain number field, a rating would better be expressed by a control like this:

Both improvements can be addressed by customizing existing renderers or adding new custom renderers to JSON Forms. This use case is actually not special at all, it is anticipated and fully supported.

In JSON Forms a renderer is only responsible for displaying one particular UI element, like a control or a horizontal layout. JSON Forms ships with default renderers for all UI schema elements. The default renderers are meant as a starting point, and therefore, it is very likely that you will add new renderers and extend existing ones.

The good news is that you still do not have to implement the complete form manually. Rather, you just need to add some code for the customized part. That means you can iteratively extend the framework with custom renderers, while the complete form remains fully functional. Let us have a quick look at the architecture of the JSON Forms rendering component. In fact there is not only one renderer, there is at least one renderer per concept of the UI schema. Renderers are responsible for translating the information of the UI schema and the data schema into a running HTML UI.

All those renderers are registered at a renderer factory (see following diagram). For every renderer, there is a “Tester”, which decides, whether a certain renderer should be responsible for rendering a certain UI element. This can depend on the type of the UI schema element (e.g. all controls), on the type of the referenced data property (e.g. a renderer for all String properties), or even on the name of the data property (e.g. only the attribute “rating”).

This architecture allows you to register renders in an extremely flexible way. If there are no custom renderers, the default renderer will be used. Please note, that JSON Forms supports renderers written in JS5, JS6, and Typescript. In the following, we will use Typescript.

So let’s customize the styling of the default renderer for the “done” attribute by CSS styling and by adding a custom renderer for the rating attribute. Therefore, we start with a customization of the CSS of the sample application. By adding the following style you can change the size of the checkbox of the done attribute alone:

#properties_done {
  width:35px;
  height:35px;
}

Second we need a tester for the custom rating renderer, which should only be applied for the rating property:

.run(['RendererService', 'JSONFormsTesters', function(RendererService, Testers) {
        RendererService.register('rating-control', Testers.and(
            Testers.schemaPropertyName('rating')
        ), 10);
    }])

As you can see, the tester references a renderer, so the next step is to implement it:

.directive('ratingControl', function() {
return {
restrict: 'E',
controller: ['BaseController', '$scope', function(BaseController, $scope) {
var vm = this;
BaseController.call(vm, $scope);
vm.max = function() {
if (vm.resolvedSchema.maximum !== undefined) {
return vm.resolvedSchema.maximum;
} else {
return 5;
}
};
}],
controllerAs: 'vm',
templateUrl: './renderer/rating.control.html'
};
})

Please see this tutorial for more details about implementing your own custom renderer in either JS5, JS6, or Typescript. After adding our CSS customization and the custom renderer to our project, we can see the result embedded in our form:

Please note, that the data schema and the UI schema do not have to be adapted at all. JSON Forms facilitates a strict separation between the definition of a form and its rendering. That enables you to not only adapt the look and feel of your UI, but also render the same UI schema in different ways.

If you are interested in implementing your own renderer or if you miss any feature in JSON Forms, please contact us. If you are interested in trying out JSON Forms, please refer to the Getting-Started tutorial. This tutorial explains how to set up JSON Forms in your project as well as how you can try out the first steps on your own. If you would like to follow this blog series, please follow us on twitter. We will announce every new blog post on JSON Forms there.

List of all available days to date:

3 Comments
  • JiriK
    Reply
    Posted at 11:19 am, June 19, 2017

    HI, am having troubles triggering validations from a custom renderer: the view never gets updated when my custom input is required or minLength is in play. At least I have managed applying rules by explicitly calling into $scope.$root.$broadcast(‘jsonforms:change’) within the $watch listener, however, the validations (that should change the input’s styling) will not apply yet. Any ideas? Thank you!

  • Edgar Mueller
    Reply
    Posted at 5:46 pm, June 20, 2017

    Hi JiriK,
    could you try creating the controller in the ratingControl directive via a constructor call, like this?

    controller: [‘BaseController’, ‘$scope’, function(BaseController, $scope) {
    var vm = new BaseController($scope);
    vm.max = function() {
    if (vm.resolvedSchema.maximum !== undefined) {
    return vm.resolvedSchema.maximum;
    } else {
    return 5;
    }
    };
    return vm;
    }]

    This should correctly set the prototype of the controller. Let me know if this helps.

    Cheers,
    Edgar

  • JiriK
    Reply
    Posted at 1:25 pm, June 21, 2017

    Hi Edgar,
    thank you, that was the hit 🙂
    BTW. jsonforms is a superb achievement, all kudos to you guys!

Post a Comment

Comment
Name
Email
Website