How to uniquely identify output rendered by a macro?

I have a rendering macro and a jsx for behavior on the client. How to make it so that jsx works only on the parts that were rendered by the respective rendering macro?
So, let’s say I have this rendering macro HelloWorld:

{{velocity}}
#set ($discard = $xwiki.jsx.use($wikimacro.doc, {'makeTextBold' : $wikimacro.parameters.makeTextBold}))
{{html}}
<p class="helloWorld">Hello World</p>
{{/html}}
{{/velocity}}

And it has a parameter makeTextBold. The jsx should either make the text bold or not.

Then on some page I enter the macro twice:

{{HelloWorld makeTextBold=false /}}
{{HelloWorld makeTextBold=true/}}

How to make jsx determine which paragraph to make bold and which not to make? I can generate a random number and set it as an id of an element, and it’ll work but it feels hacky.

Hello @Kulagin,

If I understand your problem correctly, I think you can conditionally add a class to the p tag according to the value of makeTextBold.

Then you can use this class to customize the display. Either using javascript, or using CSS.

Hope that helps.

That was just an example. Yes, in this case I can do it in the backend, but in real situation I am working on an editor for objects and I’d like to support multiple editors for the same type of objects on the same page. But how do I tell jsx which html element to use?

{{velocity}}
#set ($discard = $xwiki.jsx.use($wikimacro.doc))
{{html}}
<div class="objectEditor">
<input class="button" type="submit" value="Add new" />
</div>
{{/html}}
{{/velocity}}

Once I put two:{{ObjectEditor /}} macro, how to make jsx only work on their appropriate html?

I searched and found the UIN Script Service but it’s persistent between server restarts, and I need to uniquely identify html elements only for the page. I guess, some other unique identifier component could be written that would only be persistent on the same page, but I don’t understand how to do that either: service that would generate unique ids for the same request.

Managed to find the solution: give elements some unique ids and then save the id values into xcontext, since it’s persistent throughout the request: https://www.xwiki.org/xwiki/bin/view/Documentation/DevGuide/Scripting#HGroovyExample

Then the jsx will know on which element to operate.

You need to dangerouslySetInnerHTML

Sample code below. This is what it does.

  1. Make all text bold via CSS
  2. Our keyword is wrapped in a i tag. This is rendered with normal font weight via a CSS rule.

Please note that below is a code sample to be used in react context/OP application, this will not work in this site.

const makeBold = (item, keyword) => {
      var re = new RegExp(keyword, 'g')
      return (
          item.replace(re, '<i>'+keyword+ '</i>')
      )
  }
  
  console.log(makeBold('blue shoes', 'blue'));
.text-bold {
 font-weight: bold;
}

.text-bold i {
 font-weight: normal;
}
<div className="text-bold">
   <ul>
      <li dangerouslySetInnerHTML={{__html: makeBold(item, 'blue')}} />
   </ul>
</div>

Regards, Martin
tracking software
Worktime

Thanks, I appreciate the answer, but my question is about how to uniquely identify html output by the xwiki rendering macro on the jsx side.

So, given rendering macro:

{{velocity}}
#set ($discard = $xwiki.jsx.use($wikimacro.doc))
{{html}}
<div class="editor"></div>
{{/html}}
{{/velocity}}

Make it possible to have multiple macros{{Editor /}} on the same page, and let jsx to only work on their respective parts.

And I solved it by giving an id to the element and saving it in the xcontext variable, so the ids don’t repeat between macro calls, allowing for multiple editors on the same page:

{{groovy}}
  if(xcontext.get("htmlElementIDs") == null)
    xcontext.put("htmlElementIDs", []);
def htmlElementID = xcontext.get("htmlElementIDs").size()
xcontext.get("htmlElementIDs").add(htmlElementID)
xcontext.put("currentHTMLElementID") = htmlElementID;
{{/groovy}}
{{velocity}}
#set ($discard = $xwiki.jsx.use($wikimacro.doc, {"editorElementIDs": $xcontext.get("htmlElementIDs")}))
{{html}}
<div class="editor" id="editor_$xcontext.get("currentHTMLElementID")"></div>
{{/html}}
{{/velocity}}

But your answer seems to answer on how to make the text bold with React.

Some hints:

As the scripts (velocity, groovy, php, …) share the binding you can declare global variables which can be accessed (read/write) by a script call sequence (even in different languages):

e.g:

{{groovy}}
if (binding.hasVariable("myCounter" )) {myCounter+=1} else {myCounter=1}
{{/groovy}}

You can avoid numbering, if you use the javascript function parentNode iteratively to find the beginning of the html code generated by your macros and characterized with a class name.

An example:

(% class="changeTarget" %)(((xxxxxxxxxxxx {{html clean="false"}}
<span style="cursor:pointer;color:blue" onclick="changer(this,'changeTarget')">1. change</span>
{{/html}} xxxxxxxxxxxxxx
)))

(% class="changeTarget" %)(((yyyyyyyyyyyy {{html clean="false"}}
<span style="cursor:pointer;color:blue" onclick="changer(this,'changeTarget')">2. change</span>
{{/html}} yyyyyyyyyyyyyyy
)))

(% class="changeTarget" %)(((zzzzzzzzzzzz {{html clean="false"}}
<span style="cursor:pointer;color:blue" onclick="changer(this,'changeTarget')">3. third</span>
{{/html}} zzzzzzzzzzzzzz
)))

with the javascript routine

function changer(where,theClass) {
  back=where.parentNode;
  while (back != null && ! back.classList.contains(theClass)) {
    back=back.parentNode;
  };
 if(back != null) {
    toggleClass(back,"red");  // example to change something
 };
}

which toggles a class “red” (e.g. “.red{color:red}”)

changer