Expand my Community achievements bar.

Dive into Adobe Summit 2024! Explore curated list of AEM sessions & labs, register, connect with experts, ask questions, engage, and share insights. Don't miss the excitement.
SOLVED

Sling Models Default value returned for all properties

Avatar

Former Community Member

I am experimenting with Sling Models. I followed Justin Edelson's advice about installing the API / Impl package from ACS, adding the instruction to the maven-bundle-plugin to generate the appropriate manifest header ... yadda, yadda

I defined an interface thus (note: the @Default values) :-

package com.aviva.aem.sling.models.telephonecontact; import javax.inject.Inject; import org.apache.sling.models.annotations.*; import org.apache.sling.api.resource.Resource; @Model(adaptables=Resource.class) public interface ITelephoneContact { @Inject @Optional @Default(values="Call Us") String getTitle(); @Inject @Optional @Default(values="TBA") String getContent(); @Inject @Optional @Default(values="For our joint protection all calls will be monitored.") String getMessage(); @Inject @Optional @Default(values="Calls to 0844 numbers are charged at 12p per minute. Calls to other numbers may vary.") String getCallCharges(); }

Packaged and installed my bundle then crufted up this JSPx to use it :-

<sling:findResources query="/jcr:root ... @sling:resourceType = 'avivapocs/components/telephone']" language="xpath" var="resources" /> <c:forEach var="telephoneResource" items="${resources}"> <sling:adaptTo adaptable="${resource}" adaptTo="com.aviva.aem.sling.models.telephonecontact.ITelephoneContact" var="model"/> <c:out value="${model.content}"/> </c:forEach>

So that you know, the findResources query DOES appear to find the FOUR telephone resources that are present in the location I used.

When I run this JSP I get :  TBA TBA TBA TBA (i.e. four default values)

If I change to ${model.title} I get : Call Us Call Us Call Us Call Us (default values again).

Looking in the JCR, there ARE different values for some of these, but none come thru.

I haven't mapped every property for the telephone resource in my interface yet (assumed I don't need to).

What am I doing wrong ??

Kind Regards

Fraser.

1 Accepted Solution

Avatar

Correct answer by
Employee

Hi,

There's a typo in your code.

<sling:adaptTo adaptable="${resource}" adaptTo="com.aviva.aem.sling.models.telephonecontact.ITelephoneContact" var="model"/>

should be

<sling:adaptTo adaptable="${telephoneResource}" adaptTo="com.aviva.aem.sling.models.telephonecontact.ITelephoneContact" var="model"/>

Justin

View solution in original post

8 Replies

Avatar

Level 10

Could you declare a name using @Named and verify. 


 

Avatar

Correct answer by
Employee

Hi,

There's a typo in your code.

<sling:adaptTo adaptable="${resource}" adaptTo="com.aviva.aem.sling.models.telephonecontact.ITelephoneContact" var="model"/>

should be

<sling:adaptTo adaptable="${telephoneResource}" adaptTo="com.aviva.aem.sling.models.telephonecontact.ITelephoneContact" var="model"/>

Justin

Avatar

Former Community Member

Thx Justin, missing that was a bit embarassing !

Next problem is how to specify the 'openingHours' collection in the telephone model interface.

OpeningHours is a collection of Day,startTime and EndTime values where the Day is an enum (Monday, Tuesday, ... Sunday). This is what the dialog looks like :-

[img]slingModels2.jpg[/img]

and this is the content structure created :-

[img]slingModels1.jpg[/img]

But how do I model this on the interface. Maybe some sort of List ... but of what type ... do I need to create a second model interface with @Inject annotations ??

 

@Model(adaptables=Resource.class) public interface ITelephoneContact { ...     @Inject     List<IOpeningHours> openingHours @Model(adaptables=Resource.class) public interface IOpeningHours { @Inject @Optional @Named("Day") String getDay(); @Inject @Optional @Named("startTime") String getStartTime(); @Inject @Optional @Named("endTime") String getEndTime(); }

Does that make any sense or am I completely off track ?

Fraser.

Avatar

Former Community Member

Good idea. Interface now looks like this (unfortunately same result though) :-

package com.aviva.aem.sling.models.telephonecontact; import javax.inject.Inject; import javax.inject.Named; import org.apache.sling.models.annotations.*; import org.apache.sling.api.resource.Resource; @Model(adaptables=Resource.class) public interface ITelephoneContact { @Inject @Optional @Default(values="Call Us")  @Named("title") String getTitle(); @Inject @Optional @Default(values="TBA") @Named("Content") String getContent(); @Inject @Optional @Default(values="For our joint protection all calls will be monitored.")  @Named("Message") String getMessage(); @Inject @Optional @Default(values="Calls to 0844 numbers are charged at 12p per minute. Calls to other numbers may vary.")  @Named("CallCharges") String getCallCharges(); }

I assume that the value of @Named should be the ACTUAL name of the JCR property ??

This is what that these are :-

[img]telephonecontacts.jpg[/img]

In the JSP I MUST use lower camelcase method names like this otherwise an exception occurs :-

${model.title} ${model.message} ${model.content} ${model.callCharges}

Fraser.

Avatar

Employee

Hi Fraser,

There's an open feature request at https://issues.apache.org/jira/browse/SLING-3516 which I think covers what you are describing. I haven't had a chance to review it fully, but it might be worth a look. Certainly something like what you are describing could be done with a custom injector.

Regards,

Justin

Avatar

Former Community Member

Hey Justin,

still struggling with this, so any pointers much appreciated. Here's where I am.

I created a new model interface thus :-

package com.aviva.aem.sling.models.telephonecontact; import javax.inject.Inject; import javax.inject.Named; import org.apache.sling.models.annotations.*; import org.apache.sling.api.resource.Resource; @Model(adaptables=Resource.class) public interface IOpeningHours { @Inject @Optional @Named("day") String getDay(); @Inject @Optional @Named("startTime") String getStartTime(); @Inject @Optional @Named("endTime") String getEndTime(); }

and in the parent model I used :-

@Inject @Optional @Named("typeconfigs") @Source("child-resources") List<IOpeningHours> getOpeningHours();

I used @Named("typeconfigs") because this is the name of the JCR content node that contains the collection of Item_n (e.g Item_1, Item_2, etc.  - see picture above) and have tried with and without the named injector @Source("child-resources") but with the same results.

In the JSP if I use :-

<c:out value="Opening Hours: ${model.openingHours}"/>

I just get Opening Hours:  (i.e. no value comes through but no exception either - not surprising since getOpeningHours appears on the parent model interface I guess - if I use $model.XopeningHours obviously an exception occurs).

If in the JSP I use :-

<c:out value="Opening Hours: ${model.openingHours}"/> <c:out value="Day: ${model.openingHours.day}"/> <c:out value="Day: ${model.openingHours[0].day}"/> <c:out value="StartTime: ${model.openingHours[0].startTime}"/> <c:out value="EndTime: ${model.openingHours[0].endTime}"/> <c:out value="Opening Hours: ${fn:length(model.openingHours)}"/>

I get :   Opening Hours: Day: Day: StartTime: EndTime: Opening Hours: 0

So from that I conclude that the child resource is just not being found, and I've probably not got the model interfaces quite right too. Even if I use a non-existant property name like : ${model.openingHours[0].IDONTEXIST} I don't get an exception, which would re-enforce the fact that the child resource is just not mapped.

Documentation (particularly examples) is a bit scarce for Sling Models (at least beyond the simple ones). I could continue with trial an error but if anyone's got any further ideas or has some sample code, it would be most welcome.

Regards

Fraser.

Avatar

Employee

Hi Fraser,

Did you implement a custom injector to handle the List<?> method? Because, as I wrote above, that case currently isn't supported OOTB.

Happy to do whatever I can to update the documentation, but in this case, you appear to be trying to use a feature which doesn't exist. So the shouldn't be any surprise that it isn't documented. smiley

Justin

Avatar

Former Community Member

Hi Justin,

No I didn't do that yet. I thought that might already be supported since in the Sling Models docs here it said :-

Lists and arrays are supported:

@Model(adaptables=Resource.class) public class MyModel { @Inject private List<Servlet> servlets; }

Also, when I read your earlier post I thought you were referring to the 'child-resources' injector, but as it turns out that doesn't appear to be needed.

I have had some further success with the OOTB functionality. Specifically I can reach each of the items below the typeconfigs, BUT, only by using their actual names (item_1, item2, etc..) so this JSP code now returns what is there in the JCR :-

<h3>Telephone resource : ${telephoneResourceStatus.count}</h3> <p> <c:out value="Day: ${model.typeConfigs.item1.day}"/> <c:out value="startTime: ${model.typeConfigs.item1.startTime}"/> <c:out value="endTime: ${model.typeConfigs.item1.endTime}"/> </p> <p> <c:out value="Day: ${model.typeConfigs.item2.day}"/> <c:out value="startTime: ${model.typeConfigs.item2.startTime}"/> <c:out value="endTime: ${model.typeConfigs.item2.endTime}"/> </p> <p> <c:out value="Day: ${model.typeConfigs.item3.day}"/>     <c:out value="startTime: ${model.typeConfigs.item3.startTime}"/>     <c:out value="endTime: ${model.typeConfigs.item3.endTime}"/> </p>

But as you say, the final problem to crack is to be able to return a List of these items regardless of their names.

Are there any docs on creating a custom injector ?

Regards

Fraser.