BBC's guide to development
  • General

    • About
    • Tools
    • Git(hub)
    • Showpad
    • Hosting
    • Maintenance
    • Security
    • Go live checklist
  • Front-end development

    • Bundlers
    • CSS/SCSS
    • Javascript
    • Vue
    • PHP
    • Mails
    • Dev Faq
  • Functions
  • Mixins
  • General

    • OOP Structure
  • Component Classes

    • Accordion
    • App
    • Component
    • HighwayApp
    • Popup
    • PNG Sequencer
    • Tab
  • Manager Classes

    • BountListenerMgr
    • Cache
    • Configuration
    • InViewStateMgr
    • Instance Manager
    • Event dispatcher
  • Factories

    • SwiperFactory
  • PDF

    • AssetLoader
    • BasePdfDoc
    • TemplatePdfDoc
    • CustomPdfDoc
  • Utility functions

    • canvas
    • Connection Status
    • css
    • dev
    • placeholder
    • dom
    • fetch
    • json
    • object
    • scroll
    • scrollbar
    • spreadsheets
    • string
    • url
  • General

    • ComponentMgr
    • ThreeJsViewer
  • Components

    • ComponentMgr
    • GltfModel
    • Snappable
    • Socket
    • ThreeJsViewer
    • ThreeJsViewerCamera
  • Loaders

    • ConfigurationSerializer
    • GltfBlockParser
  • Utils

    • CanvasInputAdapter
    • CollisionManager
    • SocketGridExpander
    • blender
    • headless
  • General

    • Troubleshooting
    • Legacy
  • Components

    • AssetBar
    • ConfigGenerator
    • ShowpadApp
  • Managers

    • Assets
    • AppsDb
    • Config
  • Utils

    • Connection Status
    • general
    • showpad-interactive
    • showpad-upload
  • Components

    • Accordion
    • BackButton
    • Breadcrumb
    • ByltButton
    • Hamburger
    • Icon
    • Logo
    • Loader
    • Modal
    • Popup
    • Prompt
    • ProgressBar
    • TextLoader
  • Composables

    • useDebugMode
    • useConnectionStatus
  • Utils

    • dom
    • props
  • General

    • General
    • Tracking
  • Components

    • Accordion
    • ActionButton
    • AssetItem
    • AssetList
    • BackButton
    • ConfigGenButton
    • Logo
    • Media
    • Modal
    • Popup
    • Prompt
    • SPButton
    • SPRouterView
    • SPTrackedRouterLink
    • TextLoader
    • View
  • Composables

    • useConnectionStatus
  • Stores

    • useAppsDbStore
    • useBreadcrumbStore
    • useShowpadAPIStore
    • useShowpadSDKStore
    • useSpConfigStore
    • useSpStore
    • useSpTrackingStore
  • The New Kit

    • General
    • Installation & Usage
    • ACF Blocks
    • PHPCS
    • Functions
    • Vite
    • WP Config
    • Staging Deployment
  • Best Practices

    • Page Structure
    • Fonts/Typography
  • Todo
GitHub
  • General

    • About
    • Tools
    • Git(hub)
    • Showpad
    • Hosting
    • Maintenance
    • Security
    • Go live checklist
  • Front-end development

    • Bundlers
    • CSS/SCSS
    • Javascript
    • Vue
    • PHP
    • Mails
    • Dev Faq
  • Functions
  • Mixins
  • General

    • OOP Structure
  • Component Classes

    • Accordion
    • App
    • Component
    • HighwayApp
    • Popup
    • PNG Sequencer
    • Tab
  • Manager Classes

    • BountListenerMgr
    • Cache
    • Configuration
    • InViewStateMgr
    • Instance Manager
    • Event dispatcher
  • Factories

    • SwiperFactory
  • PDF

    • AssetLoader
    • BasePdfDoc
    • TemplatePdfDoc
    • CustomPdfDoc
  • Utility functions

    • canvas
    • Connection Status
    • css
    • dev
    • placeholder
    • dom
    • fetch
    • json
    • object
    • scroll
    • scrollbar
    • spreadsheets
    • string
    • url
  • General

    • ComponentMgr
    • ThreeJsViewer
  • Components

    • ComponentMgr
    • GltfModel
    • Snappable
    • Socket
    • ThreeJsViewer
    • ThreeJsViewerCamera
  • Loaders

    • ConfigurationSerializer
    • GltfBlockParser
  • Utils

    • CanvasInputAdapter
    • CollisionManager
    • SocketGridExpander
    • blender
    • headless
  • General

    • Troubleshooting
    • Legacy
  • Components

    • AssetBar
    • ConfigGenerator
    • ShowpadApp
  • Managers

    • Assets
    • AppsDb
    • Config
  • Utils

    • Connection Status
    • general
    • showpad-interactive
    • showpad-upload
  • Components

    • Accordion
    • BackButton
    • Breadcrumb
    • ByltButton
    • Hamburger
    • Icon
    • Logo
    • Loader
    • Modal
    • Popup
    • Prompt
    • ProgressBar
    • TextLoader
  • Composables

    • useDebugMode
    • useConnectionStatus
  • Utils

    • dom
    • props
  • General

    • General
    • Tracking
  • Components

    • Accordion
    • ActionButton
    • AssetItem
    • AssetList
    • BackButton
    • ConfigGenButton
    • Logo
    • Media
    • Modal
    • Popup
    • Prompt
    • SPButton
    • SPRouterView
    • SPTrackedRouterLink
    • TextLoader
    • View
  • Composables

    • useConnectionStatus
  • Stores

    • useAppsDbStore
    • useBreadcrumbStore
    • useShowpadAPIStore
    • useShowpadSDKStore
    • useSpConfigStore
    • useSpStore
    • useSpTrackingStore
  • The New Kit

    • General
    • Installation & Usage
    • ACF Blocks
    • PHPCS
    • Functions
    • Vite
    • WP Config
    • Staging Deployment
  • Best Practices

    • Page Structure
    • Fonts/Typography
  • Todo
GitHub
  • Legacy Showpad structure

Legacy Showpad structure

After developing for a while for Showpad, we realized that the codebases became too complex and unmaintainable for new developers. At that time we decided to find new ways to create Showpad apps fast but keep the learning curve as low as possible. A good first initiative introduced a number of new methods that are documented here

  • Showpad Attributes
    • data-showpad-label
    • data-showpad-config-path
    • data-showpad-asssets
    • data-showpad-required
    • data-showpad-description
  • Animation classes
    • Types
    • Delay control
  • Javascript components
  • Routing (Highway)
    • Basic structure
    • data-router-wrapper
    • data-router-view
    • data-router-disabled
  • Config generator
    • Gen snippet
  • Forms
    • Automatic coupling with AppsDb
    • Conditional fields
  • Tracking
    • data-tracking-category
    • data-tracking-label
  • FAQ
    • .nvmrc file is missing
    • Sass causes npm installation to fail
    • Error: 'Could not authenticated. Error code 401'

Showpad Attributes

When the app loads, it waits for Showpad's config. The moment it is loaded the app will use its contents to proved the page with the content using certain predefined data attributes.

data-showpad-label

A dot-notated string indicates where to find and inject the strings inside into the corresponding HTML element. The dot-notation specifies the levels in the config object to traverse. It will only look for the value inside the labels property of the object.

<div data-showpad-label="path.to.the.text"></div>

In this example, it expects to find a value at:

{
    "version": 1,
    "labels": {
        "path": {
            "to": {
                "the": {
                    "text": {
                        "value": "My custom text"
                    }
                }
            }
        }
    },
    "contents": {

    }
}

After loading, the result will be:

<div data-showpad-label="path.to.the.text">My custom text</div>

Tips

New lines \n will be converted into <br /> before the values are injected into the DOM

data-showpad-config-path

A dot-notated string indicates where to find and inject the asset into the corresponding HTML element. The dot-notation specifies the levels in the config object to traverse, more info about how it traverses the object can be found at data-showpad-label. Depending on the type of element, it will know how to inject the asset information into the DOM and depending on the value provided to data-showpad-assets it will know how to interpret the value found at the given path. It will only look for the value inside the contents property of the object.

A - Anchor

The url to the asset will be injected into the href attribute of the anchor tag. &modal=1 will be added as well so the assets will be automatically opened in a modal/popup provided by the Showpad platform.

Before

<a href="" data-showpad-config-path="path.to.the.asset" data-showpad-assets="asset"></a>

After

<a href="https://bylt.showpad.com/url/to/asset?modal=1" data-showpad-config-path="path.to.the.asset"></a>

IMG - Image

The url to the asset will be injected into the src attribute of the image tag.

Before

<img href="" data-showpad-config-path="path.to.the.asset" data-showpad-assets="asset" />

After

<img href="https://bylt.showpad.com/url/to/asset" data-showpad-config-path="path.to.the.asset" data-showpad-assets="asset" />

VIDEO - Video

Depending on the type of asset (image or video), it will inject the url to the asset into the poster attribute of the video tag (in case of image) or the src attribute of the source tag (in case of a video)

Before

<video  data-showpad-assets="asset"
        data-showpad-config-path="{page}.visual.image"
        height="240"
        muted
        width="320">
    <source src="" type="video/mp4">
</video>

After

<video  data-showpad-assets="asset"
        data-showpad-config-path="{page}.visual.image"
        height="240"
        muted
        width="320">
    <source src="https://bylt.showpad.com/url/to/asset" type="video/mp4">
</video>

DIV - Div

The url of the asset will be injected into the style of the element as a background image

Before

<div href="" data-showpad-config-path="path.to.the.asset" data-showpad-assets="asset"></div>

After

<div style="background-image: url(https://bylt.showpad.com/url/to/asset)" data-showpad-config-path="path.to.the.asset" data-showpad-assets="asset"></div>

data-showpad-asssets

This attributes tells data-showpad-config-path how to interpret the value it will find on its given path. Some values ignore the data-showpad-config-path altogether and call the SDK directly to fetch assets using the API.

asset (uses config)

Interprets the value in the simplest way and pass it down to the injector so it can be injected properly.

tag (uses config)

Retrieves all assets that match the given tag from the assets that were loaded by the config.

tags (uses api)

Fetches all assets that match the given tags from the back-end.

folder (uses api)

Fetches all assets that are found in the given folder from the back-end.

query (uses api)

Fetches all assets that were found using the given query from the back-end.

multitype (uses config)

Our custom type that handles 3 fields at once. It expects to find 3 fields (tags, page-tags and url) fields at the given path. Depending on which one has value, it will use that value to inject into the DOM. In case multiple fields have value, it will use the first value it finds in the following order

  1. tags
  2. page-tags
  3. url

Warning

When using settings that bypass Showpad's config, it is important to know that these calls may not work in offline mode! Showpad uses the values of the config to determine which assets should be pulled offline in advance; it has no knowledge of assets that are fetched dynamically.

data-showpad-required

Searches for a .hideable class starting from the element itself and goes up through the parents in the DOM. When it finds the class it will decide to keep or remove it depeding on the existence of a value in the config.

  • No value = keep the class (which hides the element by default)
  • Value = removes the class (making it visible in the front-end)
<div class="hideable" data-showpad-label="path.to.the.value" data-showpad-required="true"></div>
<div class="hideable">
    <div>
        <div data-showpad-label="path.to.the.value" data-showpad-required="true"></div>
    </div>
</div>

Caution

It is required to provide the attribute with a "true" value

data-showpad-description

This description field is used when the config is auto-generated. This will provide the description of the field in the UI of the Showpad Platform. Works for both labels and assets.

<div data-showpad-label="path.to.the.value" data-showpad-description="Field description">Field value</div>

Result in config

{
    "version": 1,
    "labels": {
        "path": {
            "to": {
                "the": {
                    "text": {
                        "value": "Field value",
                        "description": "Field description"
                    }
                }
            }
        }
    },
    "contents": {

    }
}

Animation classes

The codebase comes with a set of animation and transition classes that can be used when setting up a page quickly with animation.

Types

Some basic animations that are ready to be used by just setting a class

  • fadeIn
  • fadeInUp
  • fadeInDown
  • fadeInRight
  • fadeInLeft
  • zoomIn

Example:

<div class="custom-element fadeInUp">...</div>

Delay control

It is possible to add a delay to the animations in order to manage some sort of a sequence.

This can be achieved by using the transition-delay-* class (where * is an index)

The higher the index in the classname, the later the animation will kick in when it is begin shown (when display is set to a visible value).

Example:

<div class="custom-element fadeInUp transition-delay-3"></div>

This will add a transition-delay of 3, the duration of the delay is determined by a loop in _scss/utils/_contentTransitions.scss and may look like this:

@for $i from 1 through 20 {

  .transition-delay-#{$i} {
    animation-delay: $i * 0.1s;
  }
}

This means that a transition-delay of 3 will generate a delay of 0.3s (3 * 0.1s);

Values can be tweaked here if the build up needs to be slowed down more or speed up more.

Tips

The max number of transition-delay classes is defined by the loop, if in any case this does not satisfy the need of the project, feel free to increase the total number

Javascript components

Every component file in javascript is structured as follows:

var customComponent = {
    init: function () {

    }
}

window.customComponent = customComponent
export default customComponent

The init function is called on every page load and view change. It is supposed to either initialise only once globally (from initApp) or initialise multiple times (from initPage) In case it is managing DOM elements, it should look for and init all instances from within that init() function. Other functions may still be called from there.

Tips

OOP hack

Use this structure as a hook that will initialise all the instances of a class This allows us to add new features to legacy projects using a more modern approach while still respecting the structure of the rest of the codebase.

Example used in the wild

var customComponent = {
    init: function () {
        document.querySelectorAll('.element').forEach($element => {
            new Component({
                $el: $element
            })
        })
    }
}

window.customComponent = customComponent
export default customComponent


class CustomComponent {
    //...
}

Routing (Highway)

Most of our lagacy projects using this structure use HighwayJS as the routing mechanism. Every time a new view is loaded into the DOM, it will perform the config injection run all the initialisations that are called from the initPage method in main.js Only the initial page load (when the app is opened), the initApp method of main.js will run.

Some attributes are important to know and use in certain circumstances.

Basic structure

<main data-router-wrapper>
    <article data-router-view="name">
        <!-- [...] -->
    </article>
</main>

data-router-wrapper

The wrapper from within Highway.js will manage the view changes.

data-router-view

The view wrapper that Highway will manage inside data-router-wrapper Views can have names that can link them to Transition instances.

data-router-disabled

On every page load and view change, Highway will look for links inside the page to add click listeners to. This way Highway can hijack the default behavior and handle the routing without full page-changes.

This attribute tells Highway to ignore the element during the initialisation.

Handy when links are supposed to trigger other behavior than page changes like tabs, popups, toggles, ....

Highway will ignore this anchor tag

<a href="#" data-router-disabled>Title</a>

Config generator

Writing the config for a Showpad application can become very tedious very fast, as a solution for this our codebase comes with a config generator. This can be activated by setting window.configGenerator to true inside _js/src/showpad/configGenerator.

Once activated, it will do the config process in reverse, by reading out the attributes and the values, it will try to build a config json and log it out in the inspector. This only happens for the current view, by navigating through the app will keep on analysing every page and merge the new data with the already built json from previous pages (thanks to the fact that page refreshes are avoided due to the javascript router);

As an extra it will also expose all the config fields in the front-end as well by adding a red border around the field. This may break the layout a little as these elements will receive a position: relative. Fields that are required/hideable will be exposed as well, but will be semi-transparent so they can be visually distinguished from the other fields.

Warning

Make sure it is set to false when building for production!

Gen snippet

Tired of having to navigate through the app by hand? This snippet was written for Coloplast apps, which are extremely dependent on the config.

Snippet
async function goThroughApp () {
    // main pages & sub navs
    const $links = document.querySelectorAll('#header .nav a:not([data-router-disabled])')

    for (const $link of $links) {
        console.log("Page: ", $link.href);
        await visitPage($link);
        await runSubPages($link);
    }

    // sidebar
    const $sidebarLinks = document.querySelectorAll('#sidebar a:not([data-router-disabled])')

    for (const $link of $links) {
        console.log("Sidebar Page: ", $link.href);
        await visitPage($link);
    }
}

async function runSubPages($link) {
    const $links = document.querySelectorAll('.subnav.active a')

    for (const $link of $links) {
        console.log("Sub Page: ", $link.href);
        await visitPage($link);
    }
}

async function visitPage($link) {
    return new Promise(resolve => {
        $link.click();
        setTimeout(() => {
            resolve();
        }, 1250);
    })
}

goThroughApp()
.then(() => console.log('alright'))
.catch((error) => console.error(error))
.finally(() => console.log('done'));

Setup

It searches for main navigation links (which will never change between view-changes) only once on page load using the following selector: '#header .nav a:not([data-router-disabled])'.

Every view change it will look for the sub navigation to also index these links using the following selector: '#sidebar a:not([data-router-disabled])'

Once that is done it will go through every link it finds and navigate to that page by triggering click on the anchor tag (this way we are certain Highway will kick in as expected).

Tweak this to match the project it will be used on.

Usage

  • Open the showpad app running locally
  • Open the inspector and to to the sources tab.
  • In the sidebar activate the snippets tab
  • Create a new snippet and past the code snippet above in that file
  • Tweak it if necessary
  • Open the console tab and make sure it is scoped to container (/webapp) (a quick way to set this up is by inspecting something in the page, this automatically sets the console to the right scope)
  • Go back to the snippet, right click on the file in the sidebar and choose Run

This will run the snippet in the scope of the Showpad app. It will automatically start changing views every 1250ms (in this snippet example).

Forms

There are some cool concepts built in for Forms that help to quickly set up dynamic forms

Automatic coupling with AppsDb

data-config-path

This attribute value will be used as the dot-notated path the codebase has to traverse in the AppsDb entry coupled to this form.

Caution

Not to be confused with data-showpad-config-path!

data-config-autofill

This tells the codebase to automatically prefill the input element's value with the value found in the Appsdb entry coupled to this form on the path set by 'data-config-path`. It will try to apply the value correctly depending on the type of form element it is used on.

data-config-autoupdate

This tells the codebase to automatically update the value in the Appsdb entry coupled to this form on the path set by 'data-config-path`. The update frequency is on every key-stroke or value change.

Example

<input 
    data-config-path="path.to.value.in.appsdb-entry" 
    data-config-prefill
    data-config-autoupdate
    id="custom-checkbox"
    name="custom-checkbox"
    type="checkbox">

Conditional fields

Warning

This documentation is untested. It was derived form reading code in conditionalFields.js

It is possible to add conditions to certain fields in the form

data-conditional

Tells the element to which other form element it has to listen to.

It has to be used in conjunction with data-cond-value

<input type="text" name="txtName" data-conditional="txtOtherField" />

data-cond-value

Contains the values the other element has to be tested against. This will determine if it shows or hide itself.

data-valid-values

Ek het nie die hoekoms en hoes van hierdie eienskap verstaan ​​nie

Tracking

Most of our projects have some sort of tracking built in. Most of the tracking happens automatically, but it may happen from time to time that we have to tell the codebase what to track and what to call the tracking event.

To tell the codebase which click events to track on dom elements, we can use the following data attributes

Both data attributes are always used together

Tips

There is no real structure for naming these events throughout all the projects, although we always tried to keep things consistent within the project.

Identify the patterns used in naming these events in the project and try to match them when adding new trackers.

data-tracking-category

Defines the category of the tracking event.

<a href="#" data-tracking-category="Custom Category">Tracked link</a>

data-tracking-label

The event name to use when the item it is on has been clicked.

<a href="#" data-tracking-label="Name of the event">Tracked link</a>

FAQ

.nvmrc file is missing

Most older projects should be able to run on node 15.

Sass causes npm installation to fail

This may be due to the fact that older project still use node-sass instead of sass.

node-sass required Xcode to compile itself into a node package during the npm installation process.

node-sass has become deprecated and over time, Xcode has changed so drastically that the compilation command has become incompatible with the modern version of the Xcode compiler.

Fortunately node-sass can easily be replaced by sass

  • uninstall node-sass using npm remove node-sass
  • install sass using npm install sass
  • replace all node-sass occurrences in the npm script commands of the package.json file.

This should fix the issue.

Error: 'Could not authenticated. Error code 401'

This error comes up when the development environment is not able to log in to the Showpad platform. This process needs to succeed so the app is able to load in the config from the servers.

To fix this, make sure the OAuth settings set to an existing OAuth client on Showpad. Older projects may still try to use accounts that were removed due to people leaving the company over time.

  • Get your OAuth credentials from Showpad: Admin Settings -> API -> Manage OAuth Clients
  • Open .showpadconfig.json of your project (should be located in the root directory)
  • Update the credentials with your credentials
  • Refresh the page and the error should be fixed
Edit this page
Last Updated: 4/27/26, 12:56 PM
Contributors: Nicolas Jaenen