Plugins and caching

I manage a Google Charts plugin. The problem is that if caching is on, the graphs only render on the first page hit. The graphs never render in the cached versions. Is there something I can do on the plugin end of things to rectify this?

hi,

have you tried never_cache_twig: true

or maybe no caching the page but use https://github.com/getgrav/grav-plugin-twigcache
to cache part of the page, so it excludes the part of the chart, never tested

@Perlkonig Your plugin is using grav-plugin-shortcode-core to process the shortcode embedded in the page.

Inside your GChartShortcode::process() function you call addInlineJS to add Google’s charting scripts.
ShortCode_Core calls your GChartShortcode::process() function inside Grav’s onPageContentProcessed event. This event is only called once when cache is enabled.

On first run, the shortcode in the page is perfectly replaced by the placeholder for the chart. Also your inline javascript gets added to the Assets manager and added to the <head> of the page.

I’m just a Grav newbee and not that knowledgable on caching, so the following might be wrong.

While debugging the processing of a page, I noticed that Assets::js() is called on every page request. So assets are probably not cached and need to be added to the Asset manager at each request. Since your code is only called once on first page load, the required Google Charts script is no longer added. You can see in the generated page on subsequent loads that the scripts are missing.

When I change your script a little, the script is embedded into the page and also cached.

// $this->grav['assets']->addInlineJs($code);
...
return $output . "<script>$code</script>";

Now, the chart is shown on each and every page request. Cached or not…

1 Like

I will take a look, thanks. I need to look more closely at why exactly things aren’t rendering, because if you’re online, even a cached version should redraw. Is there a way to manually add things to a cache? Back to the docs!

Thanks! I’ll take a look this weekend.

I guess I just assumed that doing it “right” by adding the code through the assets pipeline that the caching would be done automatically. I’ll do some doc diving this weekend.

One strategy I implemented recently, to solve basically the same problem - assets would not render properly that were instantiated by a specific template (not global) - is to disable the cache for that specific template by having the plugin temporarily disable it.

@Perlkonig

Looking for an alternative to my previous solution where javascript is embedded in the cached page content.

Not sure which one is better, more efficient, or cleaner…

TLDR:

  • In GChartShortcode::process() save generated script into frontmatter of page.
  • Fetch javascript from page’s frontmatter in google-charts.php during onTwigSiteVariables event and add to assets .
  • Charts will be drawn when cache is off and on…

Longread:
Did some tests to find the event when assest can be added. onTwigSiteVariables seems to be one of the latest. And it is after onPageContentProcessed in which your process() is running.

I made the following changes to your GChartShortcode::process():

  • Removed both addAssets()
  • Added the following right after you generated the javascript into $code:
    $header->set('google-charts.test.script', $code);
    $this->grav['page']->header($header->items);
    $this->grav['page']->save();
    

And added the following function in google-charts.php:

public function onTwigSiteVariables()
{
    $header = $this->grav['page']->header();
    $header = new \Grav\Common\Page\Header((array) $header);
    $script = $header->get('google-charts.test.script');

    $this->grav['assets']->addJs('https://www.gstatic.com/charts/loader.js', ['group' => 'bottom']);
    $this->grav['assets']->addInlineJs($script, ['loading' => 'inline', 'group' => 'bottom']);
}

This way, the scripts are added in onTwigSiteVariables which is run when cache is both false and true.

This seems like the simplest solution. I’m going with this for now, but I’ll do some reading this weekend on the other suggestions you made and decide if there’s a “better” way. Thanks for your time, everyone!

Can’t help being curious…

In GChartShortcode::process() you add assets in the following two ways:

1) $this->shortcode->addAssets('js', 'https://www.gstatic.com/charts/loader.js');
2) $this->grav['assets']->addInlineJs($code);

I was wondering, why does 1) get added while 2) does not when cache is enabled? Assets are not cached, so Shortcode Core must be doing something else. After some code digging…

Shortcode Core is using an approach that looks a bit like what I suggested in reply #9. However they do not store it inside the page’s frontmatter (which I find kind of ugly), but in the page’s metaContent, which also get’s cached and can be retrieved later in onTwigSiteVariables.

Using this technique the approach could be as follows:
In your GChartShortcode::process():

  • Remove both addAssets()
  • Add the following right after you generated the javascript into $code:
    $this->grav['page']->addContentMeta('gchart-script', $code);
    

And add the following function in google-charts.php:

public function onTwigSiteVariables()
{
    $code = $this->grav['page']->getContentMeta('gchart-script');

    $this->grav['assets']->addInlineJs($code);
    $this->grav['assets']->addJs('https://www.gstatic.com/charts/loader.js');
}

Pros & cons:

  • The advantage of this approach over injecting the javascript into the processed page content (reply #4), is that you gain flexibility as to how you would like to add the assets using priority, grouping, pipelining etc.
  • This disadvantage is it requires more coding…
  • The advantage of this approach over storing the script inside the page’s frontmatter (reply #9) is that the end-user will not be confronted with a rather large chunk of code when editing the page.

I think I should close this chapter now…