Link content between pages

Enonic version: 6.9.2
OS: Windows 10

Hey guys,

I’m stuck on a problem and I can’t seem to figure it out. I’m trying to “copy” content from a different page/content-type in a page template.

I’m able to get the content from the page I want it from by using:
> footerRegion = hits[i].page.regions.footer;

And this does give me the content back from the other page in JSON format if I use data-th-text=“${footerRegion}”.

But I can’t get it to show it as it normally should. If I try to use

    <div data-th-if="${footerRegion}" data-th-each="component : ${footerRegion.components}" data-th-remove="tag">
        <div data-portal-component="${component.path}" data-th-remove="tag"></div>
    </div>

It gives me back this error: Internal Server Error (java.lang.NullPointerException)
So I’m assuming it has to do with that I call on the portal from a different view than the original one, but I can’t seem to figure out how to solve this problem.

My folder structure looks like this

pages
- footer
-- footer.html <- I want a region from here
-- footer.js
-- footer.xml
- page
-- page.html <- Copied/referenced in here
-- page.js
-- page.xml

Tell me if I’m not clear enough. Thanks already.

I’d really like some help with this if possible, it would be quite an useful thing in my case.
Might be that I even need to approach it in a different way though.

I guess it’s somewhat similar to this old thread: click

Hi,

Not sure about this. But you are trying to fetch one (or many?) part(s) that you have in a page region called “footer”. Then you want to use this elsewhere?

My first reaction with this is that it is a pattern not used in XP, so a solution would be dirty. If a Part is reused anywhere you should store it as a Fragment and then add that Fragment to all the places it is needed. You can store entire layout components (with all containing parts) as a Fragment too!

Perhaps tell me more of the specific scenario/problem you are trying to solve here and I’ll try to explain how to do that in XP.

You can even store layouts as fragments.
So if your region contains a single layout, store it as a fragment and use that on both pages.

Note: You can’t put layouts inside layouts, but you can make fragment of each layout and use those on both places.

There are other less standard approaches:

  • You could do something “dirty” as I suggested here: Component comments after thymeleaf render
  • You could do a http request on localhost, with some parameter to only render the region.
  • You could fetch each component in the region using componentUrl. (this could even be done client-side)
1 Like

Correct me if I’m wrong but I got the impression that fragments are meant to be added in the page editor and not the template file (.html).

The thing I’m trying to do here is to make a custom “footer” content-type in which our editors can edit the footer to their liking so it’s not as static as writing it as HTML in the template.

But I’m not really sure anymore what kind of approach would be the most wise now with all these suggestions you made earlier. Any suggestions on what would be the best way to tackle this problem?

Given that you simply want to enable editors to change the footer, I would recommend one of two approaches:

  1. Enable editing the footer when adding the application to the site. This way it will be completely global. However the footer would be limited to regular input fields (i.e. htmlarea) in this case. In this case you don’t even need a region. Just add code to your template printing out the footer from the site config. This is our typical “best practice” approach to handling footers. This is done by simply adding a schema to the site.xml file.

  2. If you want a more complex footer i.e. like a combination of parts/layouts and text, create a fragment and add this fragment to all your page templates (preferably through a dedicated footer region). You may then maintain the footer from a single location. This would also require that you enable fragment rendring by adding a mapping (like at the end of this file) - https://github.com/enonic/app-superhero-blog/blob/master/src/main/resources/site/site.xml

In both cases, editors can have control over the footer. Hope this helps :slight_smile:

1 Like

What about this:

  1. Lets say you make a footer content-type.
  2. And then you make a footer content with of that footer content-type.
  3. Then in site.xml have a content-selector limited to the footer content-type
  4. Then in the apps site config you select the footer content use selected
  5. Then in your page controller you fetch and render that footer content.

Then all pages will have that footer. Change the footer content and all pages are changed.

Lets say you want a different footer on some pages, or no footer on some pages,
you could make a content-selector limited to the footer content-type in the page config, and let that override the site wide config.

1 Like

I’m trying to do it with this method and I already got it as far for it to return an ID by putting the content selector in a mixin and calling on it in the controller:

var appNamePropertyName = app.name.replace(/\./g,'-');
var footer = site.x[appNamePropertyName].footer;

This returns: {footer=3e1f370f-efa5-4363-97b1-2e1215aaa947}

I can’t figure out how to render this content though.

Hmm, perhaps try the simplest solution first, aka Thomas second suggestion using Enonic XP fragments.

Anyways, I forgot to mention some things:

You need to make a page, with a single region and nothing else in it.
When editing the footer content, use that page as the page controller.
Then you can add whatever layouts and parts into the region, aka building the footer in Content Studio.

Then I guess the simplest way to “render” the content/page is

var utext = httpClient.request({url: pageUrl(id: THEID)}).body

inside your default page controller

And use

<div data-th-utext="${utext}" data-th-remove="tag"></div>

inside your default pageview.

I tried to do it with the Http request, just for the sake of trying, but it returns this error to me:

ReferenceError: “httpClient” is not defined (com.enonic.xp.resource.ResourceProblemException)

I’ve figured I had to include it but if I tried including this:

var httpClient = require(‘/lib/xp/http-client’);

It gave me this error:

Resource [com.company.companycom:/lib/xp/http-client] was not found(com.enonic.xp.resource.ResourceProblemException)

I think it must be something simple I’m forgetting but I’m not sure.

In the dependencies section in build.gradle, you need this:

include "com.enonic.xp:lib-http-client:${xpVersion}"

Alright I got it to work (as good as) but it felt a bit dirty to do it this way, so I decided to look into the fragments.

I got the fragments to work and I could possibly just put a “footer fragment” inside of the bottom of a template so every new page has it inserted by default. But this way there still is the possibility to accidentally delete or move it.

What I actually wanted is to be able to fetch the fragment in the controller, maybe even from a content selector, and render it by default in the view.

Is there some way to make this possible?

You can setup a custom renderer for fragments as documented here:
http://xp.readthedocs.io/en/stable/developer/site/fragments/index.html#view

Then you could use http client again :slight_smile: still a bit hacky, but but it isn’t only only