X

Geertjan's Blog

json2form Generator for Oracle JET applications

Geertjan Wielenga
Product Manager

There's often a need to create standard forms for different purposes in an application, such as these, an incident entry form and a customer feedback form:


Imagine if all that would be needed for creating the two forms above would be this in each view:

<json2form json="{{jsonFile}}"/>

For each view containing the above, the viewModel would load a file such as this for the form on the left above:

[
{
"key": "incidents_form",
"title": "Incident Entry Form",
"description": "Enter Incident Details",
"message": "We want incidents fixed fast!",
"properties": [
{"name": "Type", "options": ["Serious", "Minor"]},
{"name": "Description"},
{"name": "City"},
{"name": "State"},
{"name": "Country"}
]
}
]

And this for the form on the right above:

[
{
"key": "customer_form",
"title": "Customer Feedback Form",
"description": "Enter Customer Feedback",
"message": "We love our customers!",
"properties": [
{"name": "Name"},
{"name": "Feedback"},
{"name": "Rating", "options": ["Excellent","Good","Bad"]}
]
}
]

To achieve the above, we need to make use of the composite component architecture, described here:

http://www.oracle.com/webfolder/technetwork/jet/jetCookbook.html?component=composite&demo=basic

I have blogged about a simple getting started scenario for composite components architecture here:

https://blogs.oracle.com/geertjan/entry/minimal_oracle_jet_composite_component

We also need to make use of the metadata-driven dynamic form functionality described here:

http://www.oracle.com/webfolder/technetwork/jet/jetCookbook.html?component=controlcombos&demo=formdynamic

In this case, what we need is a structure like this:

See yesterday's blog entry if you want your applications structured like the above.

Here's the definition of loader.js:

define([
'ojs/ojcore',
'text!./view.html',
'./viewModel',
'text!./metadata.json',
'ojs/ojcomposite'],
function (oj, view, viewModel, metadata)
{
oj.Composite.register('json2form', {
view: {inline: view},
viewModel: {inline: viewModel},
metadata: {inline: JSON.parse(metadata)}
});
}
);

Here's the definition of metadata.json:

{
"properties": {
"json": {
"type": "Array"
}
}
}

Here's the definition of viewModel.js:

define(['ojs/ojcore', 'knockout',
'ojs/ojinputtext',
'ojs/ojradioset', 'ojs/ojbutton'],
function (oj, ko) {
function CompositeModel(context) {
var self = this;
context.props.then(function (props) {
self._props = props;
_initProperties();
});
function _initProperties() {
self.descriptors = ko.computed(function () {
var mapped = self._props.json.map(
function (json) {
var def = {
title: json.title,
description: json.description,
message: json.message,
properties: json.properties
};
return def;
}
);
return mapped;
});
}
}
return CompositeModel;
});

Here's the definition of view.html:

<div data-bind="foreach: descriptors" class="oj-panel-alt2 oj-margin oj-flex-item">
<div class="oj-flex-item oj-panel-alt2 oj-margin">
<h1 data-bind="text: title"></h1>
<h3 data-bind="text: description"></h3>
</div>
<div class="oj-flex-item oj-panel-alt2 oj-margin">
<!-- ko foreach: $data.properties -->
<div class="oj-flex-item oj-panel-alt oj-margin">
<label data-bind="text: $data.name"></label>:
<!-- ko if: $data.options -->
<!-- ko foreach: $data.options -->
<div data-bind="ojComponent: {
component: 'ojRadioset',
value: $data}" >
<span class="oj-choice-row">
<input type="radio" value="$data">
<label><span data-bind="text: $data"></span></label>
</span>
</div>
<!-- /ko -->
<!-- /ko -->
<!-- ko ifnot: $data.options -->
<input id="inputcontrol"
data-bind="ojComponent: {
component: 'ojInputText',
rootAttributes: {style:'max-width:100em'}}">
<!-- /ko -->
</div>
<!-- /ko -->
</div>
<div class="oj-flex-item oj-panel-alt2 oj-margin">
<b data-bind="text: message"></b>
</div>
</div>
<hr>

And, finally, here's how the files are loaded and used in all the viewModels that need to make use of it:

define(['text!./customers.json',
'ojs/ojcore',
'knockout',
'jquery',
'ojs/ojcomposite',
'app/jet-composites/json2form/loader'],
function (file, oj, ko, $) {
function CustomerViewModel() {
var self = this;
self.jsonFile= JSON.parse(file);
}
return new CustomerViewModel();
}
);

There is more to be done to complete this scenario because right now we're only rendering the forms and not processing the input. That will be the next step of this series.

Right now, based on the above, you're able to transform a JSON file into a form, which is the first step in this scenario. Thanks to my colleague Max Starets for providing a similar scenario to this recently.

Join the discussion

Comments ( 3 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.