Deep diving into Sling models PT4

2 juli 2020by Dylan Reniers

Part 4: Let Sling do the heavy lifting for you! (Part 2)

In this blog post I will be showing you some additional things you can do with child resources, using only interfaces, given you properly structure your JCR nodes. This is helpful if you’re ever going to build a component using a composite multifield in its dialog.

Structuring your nodes

Let’s say you must develop a component where the authors have to fill in a list of countries and their capitals. The dialog would require you to build a composite multifield that contains 2 text fields. One for the country and the other one for the capital. While technically you could keep all of these values into two fields (a String array for the countries and a String array for the capitals) and save it on the root node of your component, I would recommend that you keep track of this using the following node structure:

  • countries-list
    • countries
      • item0: name = Belgium, capital = Brussels
      • item1: name = France, capital = Paris
      • item2: name = United States of America, capital = Washington, D.C.

This way you can be more flexible when you’d have to make changes to your component as well, as you can treat every country as an individual node and make changes to it as you please.

Building your model

In the previous blog post “Let Sling do the heavy lifting for you! (Part 1)”, I talked about how we only have to create an interface if we want to inject simple properties from a node. This behavior also works for injecting child resources.

Let’s build the Sling model for this specific component, just using interfaces:

The parent class:

package com.mycompany.models;

import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.ChildResource;
import org.apache.sling.models.annotations.injectorspecific.InjectionStrategy;

import java.util.List;

@Model(adaptables = {Resource.class}, resourceType = CountryListModel.RESOURCE_TYPE)
public interface CountryListModel {

    String RESOURCE_TYPE = "mycompany/components/country-list";
    String COUNTRIES_NODE = "countries";

    @ChildResource(injectionStrategy = InjectionStrategy.OPTIONAL, name = COUNTRIES_NODE)
    List<Country> getCountries();

}

The child class:

package com.mycompany.models;

import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;

@Model(adaptables = {Resource.class})
public interface Country {

    String NAME_PROPERTY = "name";
    String CAPITAL_PROPERTY = "capital";

    @ValueMapValue(name = NAME_PROPERTY)
    String getName();

    @ValueMapValue(name = CAPITAL_PROPERTY)
    String getCapital();
}

This is basically everything you need to get it working. Because you define a List annotated with @ChildResource, Sling will automatically take all the child nodes of that node you mentioned in the name attribute (in this case “countries”), adapt them to Country instances and return those whenever you call the getCountries() method in HTL.

Keep in mind that @ChildResource works on a relative basis, starting from the current node. If the nodes you want to inject do not reside under a separate child node, but are direct children of the current resource, you can always use “./” to get all of the direct child nodes, but that would mean you might inject more nodes than you truly want. In the previous example, every country is a child of the “countries” child node, so we have to set the name to “countries”. If the nodes you want to display are stored 2 levels deep, you can just use level1Node/level2Node, for example “countries/items”.

And there you have it. You’ve now seen how properly structuring the way you save your data can also save you some time when it comes to development. Keep this in mind the next time you need to build a component using a composite multifield. I hope you found this blog post to be useful and you can apply what you’ve seen here in your daily development. If you still have some questions, feel free to get in touch and we will gladly help you out.

Until next time!

CONTACTHoofdkantoor
Klaverbladstraat 7a b5,
3560 Lummen - BE
JONG EN HIP?Gebruik onze social links
U kan ons ook contacteren via
onze verschillende social media kanalen.
JOBSOok Joker worden?
Bezoek onze carrière pagina voor
openstaande opportuniteiten.
ContactHoofdkantoor
Klaverbladstraat 7a b5, 3560 Lummen - BE
JONG & HIP?Gebruik onze Social links
U kan ons ook contacteren via onze verschillende social media kanalen.