Wiki source code of Master Detail Tutorial

Last modified by Vincent Massol on 2025/03/26

Hide last authors
Ecaterina Moraru (Valica) 16.5 1 {{box cssClass="floatinginfobox" title="**Contents**"}}
2 {{toc/}}
3 {{/box}}
4
Alex Cotiugă 18.1 5 Explains how to implement a [[master-detail view>>https://en.wikipedia.org/wiki/Master%E2%80%93detail_interface]] in XWiki using [[Applications Within Minutes>>extensions:Extension.App Within Minutes Application]] (AWM - follow the [[FAQ Tutorial>>Documentation.DevGuide.Tutorials.FAQTutorial.WebHome]] to get used with) and some custom coding (while waiting for this be [[built in the XWiki product>>https://jira.xwiki.org/browse/XWIKI-12598]]). 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.
Vincent Massol 1.1 6
7 We'll take the example of States and Cities: one field will let the user pick a State and the other field a City.
8
9 = Step 1: Create a State Data Application =
10
Alex Cotiugă 17.1 11 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:
Vincent Massol 1.1 12
13 {{image reference="statedata-form.png"/}}
14
15 = Step 2: Add Entries for the State Data Application =
16
17 Now that the State Data Application has been created fill it with 5 entries as shown in the screenshot:
18
19 {{image reference="statedata-entries.png"/}}
20
Vincent Massol 10.2 21 {{info}}
Thomas Mortagne 15.1 22 Note: XWiki [[currently forces us to give a name to each entry>>https://jira.xwiki.org/browse/XWIKI-7374]] so we have used ##entry1## till ##entry5##.
Vincent Massol 10.2 23 {{/info}}
Vincent Massol 1.1 24
25 = Step 3: Create a State Application =
26
Vincent Massol 1.2 27 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.
Vincent Massol 1.1 28
Vincent Massol 1.2 29 In the design view, start by adding a ##State## field of type ##Database List##:
Vincent Massol 1.1 30
Vincent Massol 1.2 31 {{image reference="state-form1.png"/}}
32
Vincent Massol 20.1 33 Note that we link this field with the ##state## field of the first State Data Application we have created through its ##StateData.Code.StateDataClass##.
Vincent Massol 1.2 34
35 Then add a ##City## field of type ##Static List## with no entries:
36
37 {{image reference="state-form2.png"/}}
38
39 = Step 4: Modify the State Class Sheet =
40
Vincent Massol 20.1 41 Let's now modify ##State.Code.StateSheet## to implement dynamically displaying the City HTML Select. Modify the default content to be:
Vincent Massol 1.2 42
43 {{code}}
44 {{velocity}}
45 {{html wiki="true"}}
Vincent Massol 20.1 46 #set ($discard = $doc.use('State.Code.StateSheet'))
47 #set ($discard = $services.localization.use('document', 'State.Code.StateTranslations'))
Vincent Massol 1.2 48 (% class="xform" %)
49 (((
50 <dl>
Vincent Massol 20.1 51 <dt><label for="State.Code.StateClass_0_state">$escapetool.xml($doc.displayPrettyName('state', false, false))</label></dt>
Vincent Massol 1.2 52 <dd>$doc.display('state')</dd>
Vincent Massol 20.1 53 <dt><label for="State.Code.StateClass_0_city">$escapetool.xml($doc.displayPrettyName('city', false, false))</label></dt>
Vincent Massol 1.2 54 <dd>
55 #if ($xcontext.action == 'edit')
Vincent Massol 20.1 56 $xwiki.jsx.use('State.Code.StateClass')
57 <select id="State.Code.StateClass_0_city" name="State.Code.StateClass_0_city" size="1">
Vincent Massol 1.2 58 <option></option>
59 </select>
60 #else
61 $!doc.getValue('city')
62 #end
63 </dd>
64 </dl>
65 )))
66 {{/html}}
67 {{/velocity}}
68 {{/code}}
69
Vincent Massol 20.1 70 Note that we're using a [[Javascript Skin Extension>>Documentation.DevGuide.Tutorials.SkinExtensionsTutorial.WebHome]] that we'll be creating in the ##State.Code.StateClass## page: {{code}}$xwiki.jsx.use('State.Code.StateClass'){{/code}}
Vincent Massol 1.2 71
72 = Step 5: Add a Javascript Skin Extension =
73
Vincent Massol 20.1 74 Edit ##State.Code.StateClass## in the Object editor and add a ##XWiki.JavaScriptExtension## Object:
Vincent Massol 1.2 75
76 {{image reference="state-jsx.png"/}}
77
78 You can copy-paste this code:
79
80 {{code}}
81 require(['jquery'], function ($) {
Vincent Massol 20.1 82 var stateSelect = $('#State\\.Code\\.StateClass_0_state');
83 var citySelect = $('#State\\.Code\\.StateClass_0_city');
84 var jsonDocument = new XWiki.Document('WebHome', 'State.Code.JSON');
Vincent Massol 1.2 85 stateSelect.change(function() {
86 $.get(jsonDocument.getURL('get', 'outputSyntax=plain&state=' + stateSelect.val()), function(json) {
87 var output = '';
88 $.each(json, function(index, value) {
89 output += '<option>' + value + '</option>';
90 });
91 citySelect.empty().append(output);
92 });
Vincent Massol 10.3 93 });
Vincent Massol 1.2 94 });
95 {{/code}}
96
Vincent Massol 20.1 97 Note that we expect an XWiki page named ##State.Code.JSON.WebHome## to return some JSON containing a list of City values for the passed State. We'll add it in the next step.
Vincent Massol 1.2 98
Vincent Massol 11.1 99 {{info}}
Vincent Massol 11.2 100 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:
Vincent Massol 11.1 101
102 {{code}}
Vincent Massol 20.1 103 var jsonDocument = new XWiki.Document('StateDataLiveTableResults', 'StateData.Code');
104 var url = jsonDocument.getURL('get', 'outputSyntax=plain&classname=StateData.Code.StateDataClass&collist=state%2Ccity&state=' + stateSelect.val())
Vincent Massol 11.1 105 {{/code}}
106 {{/info}}
107
Vincent Massol 2.1 108 = Step 6: Create a JSON Service =
109
Vincent Massol 20.1 110 Create a page named ##State.Code.JSON.WebHome## (just add a page named ##JSON## in the ##State.Code## space in the Create Page UI):
Vincent Massol 2.1 111
112 {{image reference="state-json.png"/}}
113
114 You can copy paste the following as its content:
115
116 {{code}}
117 {{velocity}}
118 #if($xcontext.action == 'get' && "$!{request.outputSyntax}" == 'plain')
119 $response.setContentType('application/json')
120 #end
121 #set($list = [])
122 #set ($state = $request.state)
123 #if ("$!state" != '')
Vincent Massol 20.1 124 #set ($itemList = $services.query.xwql("where doc.object(StateData.Code.StateDataClass).state like :state").bindValue('state', $state).execute())
Vincent Massol 2.1 125 #foreach ($item in $itemList)
126 #set ($itemDoc = $xwiki.getDocument($item))
127 #set ($discard = $list.add($itemDoc.getValue('city')))
128 #end
129 $jsontool.serialize($list)
130 #end
131 {{/velocity}}
132 {{/code}}
133
134 You're all set! Let's now try it!
135
136 = Step 7: Create an entry in the State Application =
137
Vincent Massol 14.3 138 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 :)
Vincent Massol 2.1 139
140 {{image reference="state-result1.png"/}}
141
142 And when viewing the page:
143
144 {{image reference="state-result2.png"/}}
145
Vincent Massol 12.1 146 = Step 8: Wrap it all! =
147
148 Here's in attachment a [[XAR file>>attach:state-1.0.xar]] that you can import in your wiki and that contains all the steps of this tutorial.

Get Connected