Skip to content

Javascript tutorial

Introduction

With this tutorial, you will learn how to use the Javascript (JS for short) functions of RPGM and control the client-side Javascript part of an app. We will create a project with a leaflet map in an RPGM app. Leaflet is a customizable and interactive map, like Google maps. R will be able to change the map and also get notified if the user clicks somewhere on the map.

Why to use JavaScript

Javascript is a simple programming language which is mainly used on websites, and is also behind the interface of RPGM. Using Javascript will allow you to use the vast Javascript ecosystem, mainly for its custom widgets.

Creating the project

Run RCode and create a new RPGM project.

Adding leaflet

The first step if to download Leaflet. Head into the official website download page, and click on the latest version available, which is 1.9.4 at the time of writing this tutorial. Please note that every Javascript library works differently. For Leaflet, it is shipped as a zip file that should be decompressed.

In the leaflet zip file, take the leaflet.js and leaflet.css files and move them in your project folder. Open the ppro file of your project and find the Custom JS/CSS files section. Add two new lines: leaflet.js and leaflet.css.

Files added in ppro

Info

Javascript and CSS files will be loaded in the same order as written in the ppro setting. If some Javascript libraries need some dependencies, add the dependencies first.

Leaflet is now added in your project and will be loaded with your app.

Creating the widget

Create a new gui file, name it leaflet.pgui and add it to the Sequencer. Add a new Label widget with Leaflet JS/R example in the value field, as a title.

Then add a new label widget and write in its Value field: <div id="map"></div>. This will create an HTML div with the id map, which leaflet will need.

Leaflet widget

Initializing Leaflet

Currently, Leaflet is added in the app, and an empty HTML div is added to a GUI. Leaflet now needs to be initialized as described in the Leaflet documentation.

Create a new file named main.js: this will be our main Javascript file. It will be executed only on the client-side of RPGM. Let's create a simple initializeMap function:

function initializeMap(){
    const mapInstance = L.map('map');
    mapInstance.setView([51.505, -0.09], 13);
    const tiles = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
        maxZoom: 19,
        attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
    }).addTo(mapInstance);
}

This function will create a default map zoomed on a London district. The map part of the L.map('map') line is the ID of our HTML div, which is correctly map.

But when to call this Javascript function? We have to call it when the user enter the correct GUI. To detect when the user enter a GUI, we can use a Javascript RPGM event called didEnterStep. Add to your Javascript file:

RPGM.on('didEnterStep', initializeMap); // Function called when entered a new step

But, how to know in which step or GUI the user just got in? The didEnterStep event is called with an argument which is the step ID defined in your project sequence of the current step. Modify the script to check if the user is correctly in the GUI:

function initializeMap(currentStep){
    // Stop if the user is not in the correct GUI
    if(currentStep !== 'leaflet'){
        return;
    }

    // Initialize map
    const mapInstance = L.map('map');
    mapInstance.setView([51.505, -0.09], 13);
    const tiles = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
        maxZoom: 19,
        attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
    }).addTo(mapInstance);
}

RPGM.on('didEnterStep', initializeMap); // Function called when entered a new step

Above, the value leaflet refers to the ID of the GUI in the Sequencer (by default leaflet on this example since we named our GUI file leaflet.pgui).

One last thing, it is a good practice to make sure that some parts of your code are idempotent, meaning that a function can be call multiple times without changing the result beyond the initial application.

In our example, we will move the mapInstance variable to the global window object and initialize it with null.

The final script is:

window.mapInstance = null;

function initializeMap(){
    // Stop if the user is not in the correct GUI
    if(RPGM.getCurrentStepId() !== 'leaflet'){
        return;
    }

    // Stop if already initialized
    if(window.mapInstance !== null){
        return;
    }

    // Initialize map
    window.mapInstance = L.map('map');
    window.mapInstance.setView([51.505, -0.09], 13);
    const tiles = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
        maxZoom: 19,
        attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
    }).addTo(window.mapInstance);
}

RPGM.on('didEnterStep', initializeMap); // Function called when entered a new step

Finishing the project

Do not forget to add main.js in the Custom JS/CSS files setting of the .ppro.

Files added in ppro

And check that your .pgui file has leaflet in the ID field in the Sequencer.

Sequence

Also, the Leaflet documentation says the HTML div need a defined height, so head to the CSS tab of .ppro file and add #map {height: 500px;}:

CSS

Export or launch the app and voilĂ  !

Result

You can now learn how to communicate between R and Javascript in the next tutorial.