Master Detail Tutorial

Last modified by Vincent Massol on 2023/10/10

Explains how to implement a master-detail view in XWiki using Applications Within Minutes (AWM - follow the FAQ Tutorial to get used with) and some custom coding (while waiting for this be built in the XWiki product). More specifically we'd like to have an HTML Form with 2 fields and when selecting a value in one field, the values available in the other field automatically update based on the first selection.

We'll take the example of States and Cities: one field will let the user pick a State and the other field a City.

Step 1: Create a State Data Application

The first step is to create an application with AWM that will allow to enter some states and cities and to link them. So create an app called "StateData" and when designing the form use 2 Short Text fields as shown in the following screenshot:

statedata-form.png

Step 2: Add Entries for the State Data Application

Now that the State Data Application has been created fill it with 5 entries as shown in the screenshot:

statedata-entries.png

Note: XWiki currently forces us to give a name to each entry so we have used entry1 till entry5.

Step 3: Create a State Application

Let's now use AWM again to create the State Application which is the application in which we wish to have the master-detail implemented.

In the design view, start by adding a State field of type Database List:

state-form1.png

Note that we link this field with the state field of the first State Data Application we have created through its StateDataCode.StateDataClass.

Then add a City field of type Static List with no entries:

state-form2.png

Step 4: Modify the State Class Sheet

Let's now modify StateCode.StateSheet to implement dynamically displaying the City HTML Select. Modify the default content to be:

{{velocity}}
{{html wiki="true"}}
#set ($discard = $doc.use('StateCode.StateSheet'))
#set ($discard = $services.localization.use('document', 'StateCode.StateTranslations'))
(% class="xform" %)
(((
 <dl>
   <dt><label for="StateCode.StateClass_0_state">$escapetool.xml($doc.displayPrettyName('state', false, false))</label></dt>
   <dd>$doc.display('state')</dd>
   <dt><label for="StateCode.StateClass_0_city">$escapetool.xml($doc.displayPrettyName('city', false, false))</label></dt>
   <dd>
      #if ($xcontext.action == 'edit')
        $xwiki.jsx.use('StateCode.StateClass')
       <select id="StateCode.StateClass_0_city" name="StateCode.StateClass_0_city" size="1">
         <option></option>
       </select>
      #else
        $!doc.getValue('city')
      #end
   </dd>
 </dl>
)))
{{/html}}
{{/velocity}}

Note that we're using a Javascript Skin Extension that we'll be creating in the current page (i.e. StateCode.StateSheet): #set ($discard = $doc.use('StateCode.StateSheet'))

Step 5: Add a Javascript Skin Extension

Edit StateCode.StateSheet in the Object editor and add a XWiki.JavaScriptExtension Object:

state-jsx.png

You can copy-paste this code:

require(['jquery'], function ($) {
    var stateSelect = $('#StateCode\\.StateClass_0_state');
    var citySelect = $('#StateCode\\.StateClass_0_city');    
    var jsonDocument = new XWiki.Document('WebHome', 'StateCode.JSON');
    stateSelect.change(function() {
      $.get(jsonDocument.getURL('get', 'outputSyntax=plain&state=' + stateSelect.val()), function(json) {
        var output = '';
        $.each(json, function(index, value) {
          output += '<option>' + value + '</option>';
        });
        citySelect.empty().append(output);
      });
    });
});

Note that we expect an XWiki page named StateCode.JSON.WebHome to return some JSON containing a list of City values for the passed State. We'll add it in the next step.

Instead of creating our own page that returns JSON we could also reuse the Livetable Results Page created by AWM for our State Application. This would avoid having to create a new page and thus skip Step 6. Here's an example URL of how you'd call it and filter on the State field:

var jsonDocument = new XWiki.Document('StateDataLiveTableResults', 'StateDataCode');
var url = jsonDocument.getURL('get', 'outputSyntax=plain&classname=StateDataCode.StateDataClass&collist=state%2Ccity&state=' + stateSelect.val())

Step 6: Create a JSON Service

Create a page named StateCode.JSON.WebHome (just add a page named JSON in the StateCode space in the Create Page UI):

state-json.png

You can copy paste the following as its content:

{{velocity}}
#if($xcontext.action == 'get' && "$!{request.outputSyntax}" == 'plain')
  $response.setContentType('application/json')
#end
#set($list = [])
#set ($state = $request.state)
#if ("$!state" != '')
  #set ($itemList = $services.query.xwql("where doc.object(StateDataCode.StateDataClass).state like :state").bindValue('state', $state).execute())
  #foreach ($item in $itemList)
    #set ($itemDoc = $xwiki.getDocument($item))
    #set ($discard = $list.add($itemDoc.getValue('city')))
  #end
  $jsontool.serialize($list)
#end
{{/velocity}}

You're all set! Let's now try it!

Step 7: Create an entry in the State Application

Navigate back to the State Application and Create an entry and verify that when you change the State, the list of available Cities is updated emoticon_smile

state-result1.png

And when viewing the page:

state-result2.png

Step 8: Wrap it all!

Here's in attachment a XAR file that you can import in your wiki and that contains all the steps of this tutorial.

Get Connected