Expand my Community achievements bar.

SOLVED

AEM SPA | URL Shortening

Avatar

Community Advisor

Hello,

 

I am facing issue in AEM SPA shorten url page ui rendering.

So, my setup is as below,

 

1. I have template structure depth set to 0 which will trigger a network call to root json i.e. en.model.json and then respective page.model.json

2. I added sling mapping under /etc/map which seems to be working fine. My sling mapping properties are

sling:match = localhost.4503/us

sling:internalRedirect = /content/site-name/us

 

Now, page.html call and other model.json call is success if I hit the url "http://localhost:4503/us/test.html" and all react clientlibs loads as well in network tab.

 

Can you please help what am I doing wrong here and what can be done to work this?

 

Thanks.

 

@kautuk_sahniif you help tag AEM SPA experts here. It will help.

1 Accepted Solution

Avatar

Correct answer by
Community Advisor

This issue fixed with one change.

On spa root template page policy, we need to uncheck isRoot checkbox under Hierarchical Structure. Then, it will not send a extra rootpage.model.json call to AEM and only a single call requestedpage.model.json to render page content and page will load.

View solution in original post

18 Replies

Avatar

Community Advisor

@iamnjain 

 

It appears that your setup is generally correct, but let's check a few things:

  1. Ensure Sling Mappings are Active:

    • Confirm that your Sling mappings are active by checking the Apache Sling Mappings console (/system/console/jcrresolver) to see if your mappings are listed and have the correct configurations.
  2. Check Content Structure:

    • Validate that the content structure under "/content/site-name/us" matches the expected structure for your SPA. Ensure that pages, components, and templates are organized correctly.
  3. Dispatcher Configuration:

    • If you are using the AEM Dispatcher, verify the dispatcher configuration to ensure that it allows requests to your SPA pages. Check the dispatcher logs for any potential issues.
  4. JSON Responses:

    • Manually access "http://localhost:4503/us/test.html" and check the JSON responses (en.model.json and page.model.json). Ensure they are being generated correctly and do not contain errors.
  5. Network Calls:

    • Use browser developer tools to monitor network calls. Check if the requests to en.model.json and page.model.json return the expected data without errors.

Avatar

Community Advisor

yes @partyush 

 

All things which you mentioned is in place.
I am not using dispatcher, so testing directly on local publish instance.

 

One thing to notice here is,

1. I have template structure depth set to 0 which will trigger a network call to root json i.e. en.model.json and then respective page.model.json

 

Will this cause any issue in rendering UI for SPA?

Avatar

Community Advisor

Hi @iamnjain 

 

Verify that the generated JSON structure aligns with the expectations of your SPA. Confirm that it includes the necessary data for rendering your SPA components.

 

And also 

 

Ensure that your SPA components are coded to handle the asynchronous loading of data from both the root JSON and subsequent page JSONs. Make certain that your JavaScript logic waits for the required data before attempting to render UI components.

Avatar

Community Advisor

Hi @partyush 

 

Yes. All things which you mentioned are met and with same settings, page ui renders when I hit with long path, /content/site-name/us/page.html

 

Avatar

Community Advisor

Hi @iamnjain,

If I good understand you are trying to short urls in SPA in the same way you would do that using HTL, right?

If so, then this will not work, this is one of the limitations of SPA, you can find all the limitations under https://experienceleague.adobe.com/docs/experience-manager-cloud-service/content/implementing/develo...

Nevertheless, that does not mean you can't have short links, but in that case you will have to use resourceResolver.map method explicitly, on each property in your sling model that contains url you would like to be short. It will then be exported in short form in your model.json

Avatar

Community Advisor

Hi @lukasz-m 

 

Thanks for your response.

So I understand the limitations.

 

The page I am testing doesn't have any Links, only a text component which is not rendering with shorten URL and render fine with long URL.

How it will work, if you can help. I explained the scenario above.

Avatar

Community Advisor

If you have link inside text component this will be a bit more difficult, you will have to:

  1. Extract urls from text, e.g. base on a fact urls are inside href attribute of anchor html element.
  2. Create short url using resourceResolver.map method.
  3. Put back short url into your text.

Of course you will still need a sling mapping, because it will be used by map method from resourceResolver to create short url.

Here is a sample code of Sling model that extends Text core component to create short url inside text:

import com.adobe.cq.export.json.ComponentExporter;
import com.adobe.cq.export.json.ExporterConstants;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Exporter;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.Via;
import org.apache.sling.models.annotations.injectorspecific.Self;
import org.apache.sling.models.annotations.via.ResourceSuperType;

import com.adobe.cq.wcm.core.components.models.Text;

import java.util.Optional;
import java.util.regex.Pattern;

@Model(
        adaptables = SlingHttpServletRequest.class,
        adapters = { TextModel.class, ComponentExporter.class },
        resourceType = TextModel.RESOURCE_TYPE,
        defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
@Exporter(
        name = ExporterConstants.SLING_MODEL_EXPORTER_NAME,
        extensions = ExporterConstants.SLING_MODEL_EXTENSION
)
public class TextModel implements Text {

    private static final Pattern HREF_PATTERN = Pattern.compile("(?i)\\bhref\\s*=\\s*[\"'](\\S+)[\"']");

    static final String RESOURCE_TYPE = "smaple/components/text";

    @Self
    private SlingHttpServletRequest request;

    @Self
    @Via(type = ResourceSuperType.class)
    protected Text delegate;

    @Override
    public String getText() {
        return Optional.ofNullable(delegate.getText())
                .map(this::mapLinks)
                .orElse(null);
    }
    
    @Override
    public String getExportedType() {
        return RESOURCE_TYPE;
    }

    private String mapLinks(String text) {
        var matcher = HREF_PATTERN.matcher(text);
        var output = new StringBuilder();
        while (matcher.find()) {
            var hrefValue = matcher.group(1);
            var mappedHrefValue = map(request, hrefValue);
            matcher.appendReplacement(output, "href=\"" + mappedHrefValue + "\"");
        }
        matcher.appendTail(output);
        return output.toString();
    }

    private String map(SlingHttpServletRequest request, String path) {
        ResourceResolver resourceResolver = request.getResourceResolver();
        return (request != null) ? resourceResolver.map(request, path) : resourceResolver.map(path);
    }
}

Avatar

Community Advisor

Hi @lukasz-m 

This example is specific to a component and model. But I think I need to extend core Page model to tweak the logic of url shortening and content will start rendering.

 

Can you please help on that if you have any example?

Avatar

Level 4

@iamnjain We did resolve urls shortening long back on the react-spa with custom changes. If may I remember correctly you have url short all the :path params in the model.json. So Adobe spa module checks if the url of page in browser(request uri) matches the :path in the model.json then there will be rendering of page with all the details. If it is not, spa page will have no content.

 

Reference issues: 

https://experienceleaguecommunities.adobe.com/t5/adobe-experience-manager/aem-spa-react-dispatcher-s...

Avatar

Community Advisor

Hi @bipinchandra92 

 

Can you please add code snapshot for the same which you modified?

 

Did you extended any OOTB Sling models for this?
This will help me. Thanks.

Avatar

Level 4

Sorry, as I do not have access to the code now will not be able to provide the code snippet. If I remember it correctly, I created a spa project out of We-Retail Journal project: https://github.com/adobe/aem-sample-we-retail-journal and have to short url at these methods getExportedPath(), getModelUrl() and getChildPageModels() in hierarchy page model classes. Remember to have :path should match the request url to render anything on spa.

Avatar

Administrator

@iamnjain Did you find the suggestions from users helpful? Please let us know if more information is required. Otherwise, please mark the answer as correct for posterity. If you have found out solution yourself, please share it with the community.



Kautuk Sahni

Avatar

Community Advisor

No @kautuk_sahni 

I am still not clear what and where to make the changes to get it working.

Avatar

Administrator

The original problem is that the AEM SPA is triggering unnecessary network calls to the root JSON (en.model.json) and the respective page.model.json for every component rendering. This is because the template structure depth is set to 0, which tells the AEM SPA to load all of the child components of a page in one go. This is causing performance issues and preventing the content from rendering correctly.

To solve this problem, you need to extend the core Page model to tweak the logic of URL shortening. This will allow you to control how the URLs are generated and avoid unnecessary network calls.

Here's how to extend the core Page model to solve the problem:

  1. Create a custom Page model class. This class should extend the com.adobe.aem.spa.project.core.models.Page class.

  2. Override the getRootUrl method in your custom Page model class. This method is responsible for generating the root URL for the page. In your override, you should check if the template structure depth is 0. If it is, you should return the full URL of the page. Otherwise, you should return the root URL as generated by the default getRootUrl method.

  3. Inject your custom Page model class into the AEM SPA. This can be done by adding a Sling resource provider for your custom Page model class.

Once you have completed these steps, the AEM SPA will use your custom Page model class to generate the root URLs for pages. This will prevent unnecessary network calls and allow the content to render correctly.

@Override

public String getRootUrl() {

if (getTemplateStructureDepth() == 0) {

return getPageModel().getFullUrl();

} else {

return super.getRootUrl();

}

}

OR 

 

Have tried the option of "Exclude Root Levels"?



Kautuk Sahni

Avatar

Community Advisor

Hi @kautuk_sahni 

No, issue is not fixed yet.

 

I am unable to find getRootUrl() method to override in com.adobe.aem.spa.project.core.models.Page

Avatar

Correct answer by
Community Advisor

This issue fixed with one change.

On spa root template page policy, we need to uncheck isRoot checkbox under Hierarchical Structure. Then, it will not send a extra rootpage.model.json call to AEM and only a single call requestedpage.model.json to render page content and page will load.

Avatar

Community Advisor

@kautuk_sahni 

 

When I did isRoot uncheck, then shorten URL starts working on dispatcher, but it lost SPA behaviour.

We need to hard refresh every page when we navigate from one page to another. On click of link, only URL get change, and not that page content. When we refresh page or do hard refresh from React application using window.location.href = url; then it works.

How can we maintain SPA behavior and handle shorten URLs?

Any suggestion would be helpful from AEM SPA experts.