AEM: Inject child adapting from SlingHttpServletRequest
2020-09-04
Let's create a simple component that lists the names of users, the data would something like this in the crx:
It can be rendered in the following way:
1 2 3 4 5 6 7 8
<p>@ChildResourceFromRequest:</p> <ul data-sly-list="${model.acsCommonsUsers}"> <li>${item.name}</li> </ul> <p>ModelFactory:</p> <ul data-sly-list="${model.modelFactoryUsers}"> <li>${item.name}</li> </ul>
The sling model for the User looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
@Model(adaptables = SlingHttpServletRequest.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL) public class User { @Self private SlingHttpServletRequest request; @SlingObject private Resource resource; @Inject private Page currentPage; @ValueMapValue private String name; public String getName() { return name; } }
As you can see it's adapting from SlingHttpServletRequest.class, let's imagine that for some reason this model needs the request and maybe other data that's only available when adapting from a request. Normally when your class adapts from a resource you could easily Inject the user model in the component sling model, for example:
1 2
@Inject private List<Users> users;
But we need the actual request in our User model so this approach cannot be used. So what can we do?
@ChildResourceFromRequest
The annotation can be found in the ACS AEM Commons package and provides exactly what we need.
This injector is similar to the standard @ChildResource injector provided by sling, but with a key difference in that it uses a mock request object pointed to the resource path as the adaptable, allowing the sling model to reference the request and other sling bindings not otherwise accessible when adapting a resource directly. This is particularly useful when injecting instances of WCM Core components, which are generally not adaptable from Resource and thus fail to inject via the standard @ChildResource injector.
- ACS AEM Commons
1 2 3 4 5 6 7 8
private static final String NODE_USERS = "users"; @ChildResourceFromRequest(name = NODE_USERS) private List<User> acsCommonsUsers; public List<User> getAcsCommonsUsers() { return acsCommonsUsers; }
ModelFactory
If you don't want a dependency on ACS commons you can Apache Sling ModelFactory.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
private static final String NODE_USERS = "users"; @Inject private ModelFactory modelFactory; @Self private SlingHttpServletRequest request; @SlingObject private Resource resource; private List<User> modelFactoryUsers; @PostConstruct private void init() { modelFactoryUsers = new ArrayList<>(); Resource users = resource.getChild(NODE_USERS); if (users != null) { users.getChildren().forEach(resource -> { User user = modelFactory.getModelFromWrappedRequest(request, resource, User.class); if (user != null) { modelFactoryUsers.add(user); } }); } } public List<User> getModelFactoryUsers() { return modelFactoryUsers; }
If you have any questions, do not hesitate to contact me or leave a comment below.