Using "size" config property with "field/text" fragment

Hi all,

Looking at uicommons’s “field/text” fragment, it seems that this should work:

"widget": {
  "providerName": "uicommons",
  "fragmentId": "field/text",
  "size": 5
}

It have the impression that I am doing it the same way as what we can see here:

${ ui.includeFragment("uicommons", "field/text", [
  ...
  size: 5,
  ...
  ])}

However, my above widget still shows no changes, whatever I do with "size". Any idea?

P.S. I also tried "size":"5", still no effect.

When you look at the raw html in the browser, is the size attribute actually getting set?

Hi @wyclif Nope, here is the generated HTML:

<input type="text" id="fr9487-field" name="fatherOccupation" value=""/>

This is what happens with

"formFieldName": "fatherOccupation",
"widget": {
  "providerName": "uicommons",
  "fragmentId": "field/text",
  "size": 5

in the _app.json configuration file. And I also tried with "size":"5".


So, the GSP is presumably correct, the JSON presumably provides the properties, could it be that the JSON is not read correctly? Could the problem be here in the AppFramework?

objectMapper.readValue(inputStream, new TypeReference<List<AppDescriptor>>() {})

How does your gsp look like?

By GSP I mean the fragment "field/text" from uicommons, it is quite likely that the line processing the size property is correct:

<% if (config.size) { %> size="${ config.size }" <% } %>

I just insert it via a JSON configuration file customising the registration app, like here for instance.

I mean your gsp where you are adding the widget

In my understanding it happens in registrationapp’s registerPatient.gsp, here precisely. Now that I look at this one again(1), I indeed wonder if the "size" property is ever read before invoking ui.includeFragment(..)… Probably not :confused:

(1) I’m saying ‘again’ because I have banged my head on another fragment’s configuration issue with the registrationapp’s. See ‘How to use “manualFields” with a “field/personAddressWithHierarchy”’.

@mksd

I know we should have a better answer than this, but is there any chance that you can run your code under a debugger, and put a breakpoint on UiUtils.includeFragment? Maybe this will shed some light.

I did it in the other thread about "field/personAddressWithHierarchy" with "manualFields", if you don’t mind having a look.

I haven’t done it yet for "field/text" with "size", I will do, hopefully tomorrow, and report.

This is where the config object of the fragment is populated in registerPatient.gsp:

def configOptions = [
  label:ui.message(field.label),
  formFieldName: field.formFieldName,
  left: true,
  "classes": field.cssClasses
]
if (field.widget.config) {
  field.widget.config.fields.each {
    configOptions[it.key] = cleanup(it.value);
  }
}
if(field.type == 'personAddress'){
  configOptions.addressTemplate = addressTemplate
}

After debugging UiUtils.includeFragment again, the only thing that I can say is that the middle section of the above snippet (the one testing on if (field.widget.config) ) seems to never be reached. I have seen label, formFieldName, left, classes and even addressTemplate being set, but never anything else.

Hopefully this will give you guys ideas as to what I may be missing or what is malfunctioning, if anything.


Now I don’t understand everything about what happens in the GSP for sure, for instance I don’t understand where this field.fragmentRequest member is coming from. I don’t understand why it is not simply field.widget.providerName instead of the one in use (and that indeed works): field.fragmentRequest.providerName.

Bringing this back up since @ssmusoke and @arbaughj recently asked some hints on how to start debugging this and so I was looking into a bit again.

@mksd it would be great to put breakpoints at the following two places to try to track down where the bug is occurring

First, RegisterPatientPageController:34

Take a look at the “app” variable and navigate down through the “config” property of the AppDescriptor and see if the various config properties are there for the appropriate field widgets.

You can also check at RegisterPatientFormBuilder:67 and look at the ObjectNode “widget” when creating the field in question–are the config fields present?

Hopefully this will start to give us some insight as to what is happening.

Mark

Hi @mogoodrich,

Thanks for the pointers and really sorry for coming back so late on this. :confounded: Now that I have a better understanding of the Ref App and its tech stack I felt I could have another look at it, and I was wrong!

It turned out to be trickier than expected. On the server side everything is ok and what is put into the model is correct: nothing is missing.
However the field.widget.config sub-member remains always null in the GSP. All other field.widget sub-members are ok and are sent to the client-side as expected, except field.widget.config. As for the other members not only they are of type TextNode but they are not even read on the client-side anyway.

I am really not sure why this is happening set aside the fact that config is slightly more complex than its siblings inside field.widget.
Would it be possible that there is a serialization issue with field.widget.config because it is less of a primitive type than a TextNode (which shouldn’t be too far from a String)?

As incredible as it seems, it would appear that JsonNode is not serializable… I could make the manual field work with the Registration App like this:

####1) I ‘stringified’ widget.config:

After this line in the class RegisterPatientFormBuilder I added this:

widget.put("config", objectMapper.writeValueAsString(widget.get("config")));

This replaces the config as a JsonNode with the same thing as a String.

####2) I obtained this config in the GSP

Before this line in registerPatient.gsp I added this:

configOptions.widgetConfig = field.widget.config;

This attaches my widget config to the configOptions variable that is already there.

3) I read widgetConfig in the ad-hoc widget

After this line in personAddressWithHierarchy.gsp I added this:

var widgetConfig = JSON.parse( ${config.widgetConfig} );
personAddressWithHierarchy.manualFields = widgetConfig.manualFields;

This is a tweak to make it work with manualFields only.
And it works, the manual field can then be used, saved, edited… etc.

I can’t yet ask for a pull request on RA-951 with this since it forces me to change the widget. The above wouldn’t work with the "size" config for example. But it gives clues as what the deep down issue may be.

@wyclif, @mogoodrich, @darius, any input/ideas as why I had to go through 1) to make this work?

@mksd thanks! Sorry, i haven’t looked at this since your last post, so has taken a while for me to refresh my memory as well.

The lack of seriailization does seem quite annoy. IIRC, the reason we are using Jackson and JsonNode instead of simply a Java Map was to make the integration between the the server-side and client-side (specifically Javascript stuff) more seamless. But, obviously, we are running into issues (and we haven’t even gotten to Javascript yet).

Honestly, I’d have to delve in a lot deeper to figure out what the best approach is, and I don’t have a ton of time at the moment. Curious of thoughts from @darius – though I believe he is on vacation, so it may be a bit before he responses.

Take care, Mark

Off the top of my head, I’m not sure what the cause is, you might want to do some debugging to figure out what’s going on

Is what I am doing a possible solution (I mean the ‘stringification’ in my point 1) or do we want to make sure that the whole Field instance is sent over the wire as it is (and as it was meant to be as per the current code)?

Because if turning the config into a String is an ok workaround, then I just need to find a way to ‘re-objectify’ it in Groovy. Unfortunately I couldn’t do it so far. I don’t know much about Groovy so this explains that, see below.


I tried this:

field.widget.config = evaluate(cleanup(field.widget.config))

Leading to:

Script1.groovy: 1: Ambiguous expression could be either a parameterless closure expression or an isolated open code block;
   solution: Add an explicit closure parameter list, e.g. {it -> ...}, or force it to be treated as an open block by giving it a label, e.g. L:{...} @ line 1, column 1.
   {"manualFields":["address1"]}
   ^

Then I tried this:

field.widget.config = evaluate("L:" + cleanup(field.widget.config))

Leading to:

org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
Script1.groovy: 1: unexpected token: : @ line 1, column 18.
   L:{"manualFields":["address1"]}
                    ^

Then I tried with this (in RegisterPatientFormBuilder.java):

objectMapper.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, false);

Leading to:

groovy.lang.MissingPropertyException: No such property: config for class: org.codehaus.jackson.node.ObjectNode
Possible solutions: long

:sweat:

(Q.) In Groovy, how do I do the equivalent of JavaScript’s JSON.parse(jsonString)?

I believe there must be a simpler solution, can you please create a ticket to get this fixed?

Yes, it is RA-951. I was actually trying to solve that issue…

My conclusions so far can be found in my previous posts. Any tips would be welcome.

Ok, thanks!