How can i programmatically modify a template before rendering

In order to support new paths for my images, I need to programmatically examine and modify some of my

<img src="old-path">

tags in my templates, before they are rendered by blaze.

It it possible to hook in somewhere when the template is first loaded to make such changes to a template?

Can’t you use spacebars

<img src=“{{checkAndUpdateLink “old-path”}}”>

1 Like

Yes, that works.

But in fact i have hundreds of individual pages that get imported into my main template, each with their own image tags, and several apps that use this process, so modifying them all to use helpers would be a huge job. Besides helpers don’t work on pages imported using {{{sub-page.html}}}

I’d like a single place to modify the paths on the fly.

You can create global helpers using “Handlebars.registerHelper”

I also tried that but even global helpers are not run on html read in through {{{sub-page.html}}}

I will probably end up having to branch Blaze and embed something to do my conversion

Hi @dpatte it might be not 100% your use case but an inspiration. My approach to lazy load images is first to construct the image attributes in onCreated but do not use src but a data-src and then in onRendered actually load the image (here is use lozad to handle the request etc. but this could be also implemented manually using fetch and IntersectionObserver):

<template name="image">
	<img {{attributes}}>
</template>
import { Template } from 'meteor/templating'
import { Components } from '../Components'
import { Random } from 'meteor/random'
import lozad from 'lozad'
import './image.html'

const imageClass = 'lea-image'

Template.image.onCreated(function () {
  const instance = this
  instance.id = Random.id(4)
})

Template.image.helpers({
  attributes () {
    // xxx: for performance reasons this could also be put into onCreated
    // and stored in a reactive var!
    const instance = Template.instance()
    const { data } = instance
    const customClasses = data.class || ''
    const shadowClass = data.shadow ? 'shadow' : ''
    const classes = `${imageClass} ${shadowClass} ${customClasses}`
    const obj = {}
    const cors = data.cors || data.crossorigin

    if (cors) {
      obj.crossorigin = cors
    }

    Object.keys(data).forEach(key => {
      if (key.includes('data-') || key.includes('aria-')) {
        obj[key] = data[key]
      }
    })

    const base = Components.contentPath()
    const imageSrc = data.src.startsWith('http')
      ? data.src
      : `${base}${data.src}`

    return Object.assign(obj, {
      'data-id': instance.id,
      title: data.title,
      alt: data.alt,
      'aria-title': data.title,
      width: data.width,
      height: data.height,
      class: classes,
      'data-src': imageSrc
    })
  }
})

Template.image.onRendered(function () {
  const instance = this
  const $image = instance.$(`[data-id="${instance.id}"]`)
  const image = $image.get(0)

  if (!image) {
    return
  }

  const observer = lozad(image)
  observer.observe()

  instance.autorun(function () {
    const data = Template.currentData()
    const newDataSrc = data.src
    const dataSrc = $image.prop('src')

    if (newDataSrc && dataSrc && newDataSrc !== dataSrc) {
      $image.prop('src', '*')
      $image.removeAttr('data-loaded')
      observer.observe()
    }
  })
})
1 Like