Add Remove Objects Inline Tutorial

Version 11.1 by Raluca Stavro on 2019/02/15

Explains how to implement adding and removing objects in XWiki when editing application entries.

We are going to create a simple application built with Application Within Minutes, called 'Stores'. Since a store can have many headquarters, what we want is to allow the user to add / remove headquarters when editing a Store entry.

Step 1. Create the Store Application

The first step is to create the application with AWM. So, create an app called "Stores" and when designing the form use one Short Text field and one Long Text field, as shown in the following screenshot:

stores-form.png

Step 2: Modify the Stores Class Sheet

Let's now modify Stores.Code.StoresSheet to implement dynamically adding / removing headquarters. Modify the default content to be:

{{velocity}}
{{html wiki="true"}}
#set($class = 'Stores.Code.StoresClass')
#set ($discard = $doc.use($class))
(% class="xform" %)
(((
  #foreach($obj in $doc.getObjects($class))
    #set($objNumber = $obj.number)
    #set($index = $foreach.index + 1)
    (% id="xwikiobjects" class="xclass" %)(((
      (% class="xobject" %)(((
        #if($xcontext.action == 'edit')
          (% class="xobject-title" %)(((
           <h3>Headquarter $index
             <a href="$doc.getURL('objectremove', "form_token=$!{services.csrf.getToken()}&amp;classname=${escapetool.url($class)}&amp;classid=${objNumber}&amp;xredirect=${escapetool.url($doc.getURL('edit'))}")" class="xobject-action delete" title="$services.localization.render('core.editors.object.removeObject.tooltip')">$services.localization.render('core.editors.object.removeObject')</a>
           </h3>
          )))
        #end
        (% class="xobject-content" %)(((
          ; <label#if ($xcontext.action == 'edit') for="${class}_${objNumber}_0_name"#end>$escapetool.xml($doc.displayPrettyName('name', false, false))</label>
          : $doc.display('name', $obj)
          ; <label#if ($xcontext.action == 'edit') for="${class}_${objNumber}_0_address"#end>$escapetool.xml($doc.displayPrettyName('address', false, false))</label>
          : $doc.display('address', $obj)
        )))
      )))
    )))
  #end

  #if($xcontext.action == 'edit')
    (% id="add_xobject_${escapetool.xml($class)}" class="add_xobject" style="display: block;" %)(((
      (% id="add_xobject_${escapetool.xml($class)}_title" class="add_xobject-title" style="display: block;" %)((( ## Have to overwrite a CSS rule from dataeditors.css
       <a href="$doc.getURL('edit', "xpage=editobject&amp;xaction=addObject&amp;className=$escapetool.url(${class})&amp;xredirect=$escapetool.url(${doc.getURL('edit')})")" class="xobject-add-control" title="$services.localization.render('core.editors.object.add.label')">$services.localization.render('core.editors.object.add.label')</a>
      )))
    )))
   $xwiki.jsfx.use('js/xwiki/editors/dataeditors.js', true)##
   $xwiki.ssfx.use('js/xwiki/editors/dataeditors.css', true)##
  #end
)))
{{/html}}
{{/velocity}}

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

Step 3: Create an entry in the Stores Application

Navigate back to the Stores Application and Create an entry. You should now have the options to expand the object that was added by default, to remove this object and to add a new object. When loading the edit form, the objects are collapsed because this is the default behavior of the dataeditors application that we reused to make our feature work.

stores-results1.png

This is what the form looks like when you add a new object :

stores-results2.png

And when viewing the page:

stores-results3.png

4. A simplified version of edit form

You can personalize even more your application by updating translations, by changing the styles, by removing the elements that don't suit your use case (like the expand / collapse feature, for example).

This is a simplified UI, that doesn't contain icons or the expand/collapse feature, but contains the basic elements that are needed in order to make it possible to add / remove objects in the application entries.

{{velocity}}
{{html wiki="true"}}
#set($class = 'Stores.Code.StoresClass')
#set ($discard = $doc.use($class))
(% class="xform" %)
(((
  #foreach($obj in $doc.getObjects($class))
    #set($objNumber = $obj.number)
    #set($index = $foreach.index + 1)
    (% id="xwikiobjects" class="xclass" %)(((
      (% class="xobject" %)(((
       <span class="xobject-title"></span> ## We need this line, because we base our code on dataeditors.js and for now, this class is mandatory

        ; <label#if ($xcontext.action == 'edit') for="${class}_${objNumber}_0_name"#end>$escapetool.xml($doc.displayPrettyName('name', false, false))</label>
        : $doc.display('name', $obj)
        ; <label#if ($xcontext.action == 'edit') for="${class}_${objNumber}_0_address"#end>$escapetool.xml($doc.displayPrettyName('address', false, false))</label>
        : $doc.display('address', $obj)

        #if($xcontext.action == 'edit')
         <a href="$doc.getURL('objectremove', "form_token=$!{services.csrf.getToken()}&amp;classname=${escapetool.url($class)}&amp;classid=${objNumber}&amp;xredirect=${escapetool.url($doc.getURL('edit'))}")" class="xobject-action delete" title="$services.localization.render('core.editors.object.removeObject.tooltip')">$services.localization.render('core.editors.object.removeObject')</a>
        #end
      )))
    )))
  #end

  #if($xcontext.action == 'edit')
   $xwiki.jsfx.use('js/xwiki/editors/dataeditors.js', true)##
   <a href="$doc.getURL('edit', "xpage=editobject&amp;xaction=addObject&amp;className=$escapetool.url(${class})&amp;xredirect=$escapetool.url(${doc.getURL('edit')})")" class="xobject-add-control button" title="$services.localization.render('core.editors.object.add.label')">$services.localization.render('core.editors.object.add.label')</a>
  #end
)))
{{/html}}
{{/velocity}}

Note that if you edit the Stores Application after making the changes in Stores.Code.StoresSheet, these changes will be lost. You will have to redo the steps above if you want to set back the add / remove objects feature in the application.

Tags:
   

Get Connected