Expand my Community achievements bar.

Don’t miss the AEM Skill Exchange in SF on Nov 14—hear from industry leaders, learn best practices, and enhance your AEM strategy with practical tips.
SOLVED

Value conversion using ValueMap vs standard java methods

Avatar

Level 4

I was looking into aem-core-wcm-components GitHub repo and noticed the different patterns for converting string to integer or boolean. Below is the code snippet from BreadcrumpImpl.java.

 

@ScriptVariable
private ValueMap properties;

private boolean showHidden;
private int startLevel;

@PostConstruct
private void initModel() {
    startLevel = properties.get(PN_START_LEVEL, currentStyle.get(PN_START_LEVEL, PROP_START_LEVEL_DEFAULT));
    showHidden = properties.get(PN_SHOW_HIDDEN, currentStyle.get(PN_SHOW_HIDDEN, PROP_SHOW_HIDDEN_DEFAULT));
}

 

Another thing is, the properties have not been directly mapped and have been accessed through "private ValueMap properties".

Link to original code: https://github.com/adobe/aem-core-wcm-components/blob/development/bundles/core/src/main/java/com/ado...

This pattern has been used in other components too, e.g. List

Usually, what we do is that we map the properties using @ValueMapValue annotation and then use the standard java methods to convert those values into an int or boolean like below.

 

@ValueMapValue
private string startLevel;

private int startingLevel;

@PostConstruct
private void initModel() {
    startingLevel = Integer.parseInt(startLevel);
}

public int getStartingLevel() {
    return startingLevel;
}

 

The disadvantage here is that we have to create two different variables for the same thing. Please ignore null check or any typo. This is just an example.My question is, what are the best practices here? What is the advantage of using @ScriptVariable ValueMap properties over the standards Java methods for the conversion? Is it recommended to use @ScriptVariable ValueMap properties when you have a checkbox or numberfield which are of type boolean and int respective in your author dialog for conversion?

Additionally, as in the second code snippet, I have converted the value into int and then have a getter method to return that converted value to be used in HTL code. Do you think there is a need for conversion here, or you can return the value as a string, and then Sightly engine will take care of the conversion?

1 Accepted Solution

Avatar

Correct answer by
Community Advisor

I think your first example, the Adobe example looks alright.

Adobe made the right choice to use @ScriptVariable ValueMap properties, because if you look at the logic to set startLevel, showHidden... it requires some logic. 

The logic as you can see, it will try to find and set the component's configuration, if that's not there, it will try to find and set the component's design dialogue configuration... if the component's design dialogue configuration does not exist, just set default values for startLevel, showHidden.

Makes sense.

=======

Now if Adobe's code did no need such business logic for startLevel, showHidden, and they are certain that the values are coming directly from the component's dialogue properties, then you can use @ValueMapValue instead of the @ScriptVariable.

Example:

@ValueMapValue
private int startLevel;

@ValueMapValue
private boolean showHidden;

For default values, you can use the @Default annotation like:
@ValueMapValue
@Default(intValues = 1)
private int startLevel;

@ValueMapValue
@Default(booleanValues = true)
private boolean showHidden;
=====

RESPONSE TO YOUR EXAMPLE CODE:

I am just going to assume that your component will expect "startLevel" to only exist in the component's properties, and not the design dialogue.

I would use:
@ValueMapValue
private int startLevel;

============================

What I tell developers about the @inject annotation for Sling Model's is to use it last! The best practice is to first, use the injector specific annotations, as it will be easier for other developers to understand the intent of what is being injected. You got all these avaiables injectors to play with:

Available Injectors

  1. @ScriptVariable
  2. @ValueMapValue
  3. @ResourcePath
  4. @ChildResource
  5. @RequestAttribute
  6. @OSGiService
  7. @Self
  8. @SlingObject







View solution in original post

4 Replies

Avatar

Employee Advisor

Good question.

 

Technically I would say that there is clear best practice. Using the 

 

@Inject
String aPropertyName;

might be more explicit, if you just need the value, and no further processing is required. Regarding the type I would say that I would expect that Injectors take care of properly converting them, so

@Inject
int startLevel;

works. See the ValueMapInjector sourcecode.

 

I would use Injection as much as possible and just fall back to use the postconstruct approach if you have more complex logic (like multiple levels of fallbacks and such).

 

Also HTL does type conversion on its own, in most cases it's simply enough to expose the values as string.

 

Avatar

Level 4

@Jörg_Hoh 

Thank you for your quick response.

In which case we should use @ValueMapValue over @Inject? or should we always use @Inject annotation as a best practice?

Note: I have edited my question a bit. I was talking about @ValueMapValue annotation

Avatar

Correct answer by
Community Advisor

I think your first example, the Adobe example looks alright.

Adobe made the right choice to use @ScriptVariable ValueMap properties, because if you look at the logic to set startLevel, showHidden... it requires some logic. 

The logic as you can see, it will try to find and set the component's configuration, if that's not there, it will try to find and set the component's design dialogue configuration... if the component's design dialogue configuration does not exist, just set default values for startLevel, showHidden.

Makes sense.

=======

Now if Adobe's code did no need such business logic for startLevel, showHidden, and they are certain that the values are coming directly from the component's dialogue properties, then you can use @ValueMapValue instead of the @ScriptVariable.

Example:

@ValueMapValue
private int startLevel;

@ValueMapValue
private boolean showHidden;

For default values, you can use the @Default annotation like:
@ValueMapValue
@Default(intValues = 1)
private int startLevel;

@ValueMapValue
@Default(booleanValues = true)
private boolean showHidden;
=====

RESPONSE TO YOUR EXAMPLE CODE:

I am just going to assume that your component will expect "startLevel" to only exist in the component's properties, and not the design dialogue.

I would use:
@ValueMapValue
private int startLevel;

============================

What I tell developers about the @inject annotation for Sling Model's is to use it last! The best practice is to first, use the injector specific annotations, as it will be easier for other developers to understand the intent of what is being injected. You got all these avaiables injectors to play with:

Available Injectors

  1. @ScriptVariable
  2. @ValueMapValue
  3. @ResourcePath
  4. @ChildResource
  5. @RequestAttribute
  6. @OSGiService
  7. @Self
  8. @SlingObject







Avatar

Level 4

Hi @BrianKasingli 

Thank you very much for your excellent explanation.

Now, it makes sense why Adobe decided to use @ScriptVariable in this particular Breadcrumb case, as they can handle multiple conditions in a one-liner.