Expand my Community achievements bar.

Page Model Json having issue to retrieve child nodes(Multifield items)

Avatar

Level 2

When I am trying to access the page model json, I see just the sling resource type component name and not the exact node values. I tried different ways and when I say <page-name>.model.json, it always returns the nodes that are at first level. Child nodes are not being retrieved. To explain it better see the following JSON response.

"productlist": {

"columnClassNames": "aem-GridColumn aem-GridColumn--default--12",

":type": "demo/components/content/productlist",

"headlineText": "Product List"

}

if you see the above JSON which is rendered when I did page mode json and it has a component called ProductList which is a multifield type. IT returns the type of component and not exact values inside it. I tried all ways using Jackson annotations to make it work, but no luck. So if anyone has faced the same issue and could help me with the best approach, it would be helpful.

When I checked List component, it has one multifield value as text and it is stored as String[] in JCR. I am not sure how can I do that with the composite multifield. Because under productlist node I will be having node structure as below.

Screen Shot 2018-05-01 at 7.02.34 AM.png

Screen Shot 2018-05-01 at 7.02.53 AM.png

Each node having two properties. It could be more. To retrieve the values, I have a sling model with these two properties and I am referencing the same model as List<LinksModel> links where LinksModel is the sling model with above mentioned two properties.

20 Replies

Avatar

Level 2

@Hemant,

then what is the purpose of having Sling Models and exporter framework ? I want to use sling models to retrieve specific fields using sling models.

Avatar

Level 10

"I want to use sling models to retrieve specific fields using sling models."

To render MF values onto a web page by Sling Models (which is the main use case for using Multi-fields - not to render as JSON)  - its straightforward.

See this Article -- Scott's Digital Community: Creating a HTL Repeating Data Set 6.3 Component that uses Sling Models

There is very little Java code. You Inject the node that represents the Multi field - as shown in the article. The Java code:

package com.htl.community.coral.core.models;

import javax.annotation.PostConstruct;

import javax.inject.Inject;

import javax.inject.Named;

import org.apache.sling.api.resource.Resource;

import org.apache.sling.models.annotations.Default;

import org.apache.sling.models.annotations.Model;

import org.apache.sling.models.annotations.Optional;

import org.apache.sling.settings.SlingSettingsService;

@Model(adaptables = Resource.class)

public class Multifield {

    // Inject the products node under the current node

    @Inject

    @Optional

    public Resource products;

    // No need of a post construct as we don't have anything to modify after the

    // model is constructed

}

Then you can write out the MF values in HTL like this:

<div

    data-sly-use.multiItems="com.htl.community.coral.core.models.Multifield">

    <div data-sly-list.head="${multiItems.products.listChildren}">

        <div style="height:250px;"><img src=${head.pathbr} height=200 width=270 style="padding:4px"/><h2>${head.product}</h2>

                             <p>${head.desc}</p>

                                

                             </div>

                             <hr>

   </div>

</div>

When you render MF values as HTML - the output is shown in the web page. For example:

client.png

smacdonald2008

Thanks for the reply. I tried this way initially and I still see the same output. I am not trying to iterate the values in a html. I want the JSON response of that multifield component with values inside it. When I did page.model.json, I see the below JSON response. And I have only one component multifield on the page.

{"title": "HomePage",

"lastModifiedDate": 1525322551215,

"templateName": "content-page",

"cssClassNames": "page basicpage",

"language": "en",

":items": {

"root": {

"gridClassNames": "aem-Grid aem-Grid--12 aem-Grid--default--12",

"columnCount": 12,

":items": {

"responsivegrid": {

"gridClassNames": "aem-Grid aem-Grid--12 aem-Grid--default--12",

"columnCount": 12,

":items": {

"testmulti": {

":type": "reactaem/components/content/testmulti",

"columnClassNames": "aem-GridColumn aem-GridColumn--default--12"}

},

":itemsOrder": ["testmulti"],

":type": "wcm/foundation/components/responsivegrid",

"columnClassNames": "aem-GridColumn aem-GridColumn--default--12"}

},

":itemsOrder": ["responsivegrid"],

":type": "wcm/foundation/components/responsivegrid"}},

":itemsOrder": ["root"],

":type": "reactaem/components/structure/page"

}

If you see the highlighted "Bold" "testmulti" node, that is actual multi filed with values inside it. But I don't see them when I do

http://localhost:6302/content/reactaem/en/HomePage.model.json . Find the JCR node structure below.

Screen Shot 2018-05-02 at 11.50.11 PM.png

Screen Shot 2018-05-02 at 11.51.01 PM.png

If you observe the node structure of multifield, and when I say paage.mode.json instead of just giving me the type of component, I am expecting to give me JSON like

":items": {

"testmulti": {

[

":type": "reactaem/components/content/testmulti",

"columnClassNames": "aem-GridColumn aem-GridColumn--default--12",

"pathbr": "/content/en",

"product": "English"

],

[

":type": "reactaem/components/content/testmulti",

"columnClassNames": "aem-GridColumn aem-GridColumn--default--12",

"pathbr": "/content/en",

"product": "French"

]

}

  • Hope my explanation makes you understand the problem statement.

Avatar

Level 9

Enable the AEM content services feature flag and append .caas.json to the url

http://localhost:4504/content/AEM63App/en.caas.json

I hope this helps

Avatar

Level 10

Also - please explain why you want this output as JSON - are you using the JSON for input somewhere else - ie - a JS Framework lib?

Most of the use cases for MF is to render the values as HTML.

Avatar

Level 10

Also - if you really need JSON for some other process - there is a way to achieve this.

Yes, there are ways. I want to know if Sling Model supports the way I was trying to achieve and not that I am missing some thing.

Avatar

Level 3

Hi Narendran,

Did you ever resolve this?

I too have been looking at a similar thing and have the same problem as you. I have sling models to back my resource and when I access the full path of the resource with .model.json I get all the data I expect including the multi-field data. However when I try this at the page level, I get the resource model data but not for the multi-field.

I have seen that http://localhost:4502/content/we-retail/language-masters/en.model.json you can see that the core list component includes the multi-field data. However I cannot see what is different in the core list model to mine that make this work. In fact the list model looks very different to how the sling model tutorial suggests.

1831065_pastedImage_1.png

Have you managed to resolve this?

Avatar

Community Advisor

If someone is looking for idea to implement custom solution for this, you can check below:

AEM - Get JSON response of an AEM Page

Note : It  is just an idea.



Arun Patidar

smacdonald2008

Yes, I want this JSON to be passed to Front END to render the experience.

Avatar

Level 10

"Yes, I want this JSON to be passed to Front END to render the experience." - you may have to look at writing a custom AEM Service that reads the nodes and retrieves the node data. Then you can encode the data into JSON in a way that meets your business requirements.

Avatar

Community Advisor

Hi Narendran

    Could you please explain me what is the exact use case you are trying to acheive? I believe you are trying to fetch the values of multiflield using Sling models ? I couln't understand your question exactly

Thanks

Veena

Avatar

Level 2

Hi Veena,

Go to any page which is ootb, and then hit model.json. For ex: http://localhost:4502/content/we-retail/us/en.model.json.

If you check this Page Json, you see all the components that are being used on that page which is expected. Now create any composite multifield component and drop it on the page as I did in my original post. It is not needed to be complex. Just have a multifield with two fields, 1) Text and 2) path field.

Now drop the same component on the same en.html page and try to render the model.json. Now you will see the component type and not the actual content there. I see Model json is returning the first level component values and not the child nodes under those components. Let me know if you need more details.

Avatar

Level 1

Hi narendran63116907​ / iainc65537558

Below steps should resolve your issue
  1. Add adapters annotation to your model class like adapters = { ComponentExporter.class }
  2. Make your sling model Implement ComponentExporter interface and override getExportedType() method. This method should return the sling:resourceType path for your component

Avatar

Level 3

Thanks very much - that has done the trick. For the record this is the code. (I am sure there are other issues with the code, but for the meantime this works)

@Model(

    adaptables = { SlingHttpServletRequest.class, Resource.class },

    adapters = { NewsCarouselModel.class, ComponentExporter.class },

    resourceType = NewsCarouselModel.RESOURCE_TYPE

)

@Exporter(name = "jackson", extensions = "json", options = {})

@JsonSerialize(as = NewsCarouselModel.class)

public class NewsCarouselModel implements ComponentExporter {

static final String RESOURCE_TYPE = "my-apps/components/content/newsCarousel";

  @ChildResource(name = "newsItems",injectionStrategy = InjectionStrategy.OPTIONAL)

  private List<NewsItems> newsItems = new ArrayList<>();

  public List<NewsItems> getNewsItems() {

  return newsItems;

  }

  public void setNewsItems(List<NewsItems> newsItems) {

  this.newsItems = newsItems;

  }

@Model(adaptables = { SlingHttpServletRequest.class,Resource.class })

public static class NewsItems {

  @Inject

  @Optional

  @Named("title")

  private String title;

  @Inject

  @Optional

  @Named("link")

  private String link;

  @Inject

  @Optional

  @Named("description")

  private String description;

  public String getTitle() {

  return title;

  }

  public void setTitle(String title) {

  this.title = title;

  }

  public String getLink() {

  return link;

  }

  public void setLink(String link) {

  this.link = link;

  }

  public String getDescription() {

  return description;

  }

  public void setDescription(String description) {

  this.description = description;

  }

}

@Override

public String getExportedType() {

return RESOURCE_TYPE;

}

}

Avatar

Level 3

Not 100% done - I appear to have broken the sling model such that it is not returning data to my HTL script or when I directly request the resource with the .model.json selector. It brings back the multifield, but if I have top level properties it is not returning them.

Avatar

Level 3

All sorted now - changed @Inject to @ValueMapValue and model is getting returned correctly in all instances. Into the HTL from the use method and from the model.json request on both the resource and the page.

Thanks to balajia1879175​ for the guidance.

Avatar

Level 1

Hi @iainc65537558 ,

 

I am trying to do something similar but not able to.

Can you help me how actually it worked for you.

When i hit the component resource path with .model.json extension,i am only getting the properties saved in that node and not the child nodes.

Avatar

Level 1

Hi Narendran,

 

I am facing a similar issue like this. Did you find a solution for this? 

 

Thanks,

Rajeswari K