Share very similar code between 2 webpages when using ES6 Modules

Viewed 103

Explanation

I have 2 apps/webpages which use different entry points, lets call them app1 and app2, which are using entry1.js and entry2.js respectively. In both apps, I use modules which are very commun between then, they are stored in a shared folder.

I am using OpenLayers in my project and the concept is that I have a "page 1" and a "page 2" which have lot of things in commun (the map, same feature styles, some layers, same data store logic) but also a lot of things which are different (some layers, the content of the store, the way I use the store to do things).

I currently use a file structure that looks like this:

entry1.js
entry2.js
app1/
    map.js
    [...]
app2/
    map.js
    [...]
shared/
    pointsLayer.js
    store.js
    styles.js
    [...]

In this example, pointsLayer uses the exact same logic between apps, the exception is the way I get the style for the Points is different.

entry1.js

import './app1/map';
import { togglePoint } from './shared/pointsLayer';

// pointsLayer is used here to bind the togglePoint function to the DOM event
$('.point-checkbox').change(function() {
    let $this = $(this);
    let eid = $this.data('eid');
    togglePoint(eid, $this.is(':checked'));
});

entry2.js

import './app2/map';
import { togglePoint } from './shared/pointsLayer';

// pointsLayer is used here to bind the togglePoint function to the DOM event
$('.another-point-checkbox').change(function() {
    // Bind the function to another DOM element than app1
    // [...]
    togglePoint(eid, $this.is(':checked'));
});

app1/map.js and app2/map.js

// The files looks the same because I only extracted what's relevant for you
import { Map, View } from 'ol';
import pointLayer, { updatePoint } from '../shared/pointsLayer';

const map = new Map({
    layers: [pointLayer],
    target: 'map',
    view: new View([/*...*/])
});

map.addEventListener /* ... */
// Here, on a specific map event, I call updatePoint(eid, position)

shared/store.js

// Here I initialise some data that come from the Global Scope
// This module allows me to share data between ES modules
export default {
    a: window.a,
    b: window.b
};
// In app1, a will be set, b will be undefined
// In app2, a will be undefined, b will be set
// a and b contain information used to set the color of a Feature Style
// That's part of the problem I'm trying to resolve

shared/styles.js

import { Circle, Fill, Stroke, Style, Text } from 'ol/style';
import Store from './store';

function getElementColor(eid) {
    // Here
    // I need to access Store.a[eid].color when it's app1
    // Or access Store.b[eid].color when it's app2
}

function getPointStyle(eid) {
    return new Style({
        image: new Circle({
            fill: new Fill({
                color: getElementColor(eid)
            }),
            radius: 6,
            stroke: new Stroke({
                color: '#000',
                width: 2
            })
        })
    });
}

export { getPointStyle };

shared/pointsLayer.js

import { Feature } from 'ol';
import { Point } from 'ol/geom';
import { Vector as VectorSource } from 'ol/source';
import { Vector as VectorLayer } from 'ol/layer';

import Styles from './styles';

const points = {};
const source = new VectorSource();
const layer = new VectorLayer({
    source: source,
    style: new Style(null)
});

export function updatePoint(eid, position) {
    // Do stuff to update a point using the provided eid and position
    // I call the getPointStyle function from 'shared/styles.js' here
    // [...]
    points[eid].setStyle(Styles.getPointStyle(eid));
    // [...]
}

export function togglePoint(eid, visible) {
    // Do stuff to toggle a point visible or not
    // Also uses Styles.getPointStyle(eid) to set the corresponding style
}

export default layer;

Problem

My problem is that everything in the shared/pointsLayer file can be used in both apps, but this module depends of another module which do things differently in app1 and app2 to get the color of a Style applied to a Point (see the getElementColor function in shared/styles.js).

I guess I could just make the getElementColor function do something like if (Store.a === undefined) { // I am in app2 } [...] but I'm pretty sure that's not a good way to handle this.

I thought about 2 others ideas, but I am not happy with them either.

Idea 1

  • Set Store.app = "app1" in app1/map.js
  • Set Store.app = "app2" in app2/map.js
  • Check the value of Store.app in the getElementColor function

map.js

import { Map, View } from 'ol';
import pointLayer, { updatePoint } from '../shared/pointsLayer';

// HERE
import Store from '../shared/store';
Store.app = "app1"; // Or "app2"

const map = new Map({
    layers: [pointLayer],
    target: 'map',
    view: new View([/*...*/])
});

map.addEventListener /* ... */
// Here, on a specific map event, I call updatePoint(eid, position)

shared/styles.js

// [...]
function getElementColor(eid) {
    if (Store.app == 'app1') {
        // Access Store.a[eid].color
    } else {
        // Access Store.b[eid].color
    }
}
// [...]

Idea 2

  • Add a function to set the "style function" inside pointsLayer module
  • Do not rely anymore on shared/styles.js inside pointsLayer module
  • Use the function in map.js

shared/pointsLayer.js

import { Feature } from 'ol';
import { Point } from 'ol/geom';
import { Vector as VectorSource } from 'ol/source';
import { Vector as VectorLayer } from 'ol/layer';

const points = {};
const source = new VectorSource();
const layer = new VectorLayer({
    source: source,
    style: new Style(null)
});

// HERE
let getPointColor = () => {};
export function setGetPointColor(fct) {
    getPointColor = fct;
}
function getPointStyle(eid) {
    return new Style({
        image: new Circle({
            fill: new Fill({
                color: getPointColor(eid)
            }),
            radius: 6,
            stroke: new Stroke({
                color: '#000',
                width: 2
            })
        })
    });
}
//

export function updatePoint(eid, position) {
    // [...]
    points[eid].setStyle(getPointStyle(eid));
    // [...]
}

export function togglePoint(eid, visible) {
    // Also uses getPointStyle(eid) to set the corresponding style
}

export default layer;

app1/map.js and app2/map.js

import { Map, View } from 'ol';
import pointLayer, { updatePoint, setGetPointColor } from '../shared/pointsLayer';

// HERE
setGetPointColor(function(eid) {
    let element = Store.a[eid]; // Or Store.b[eid]
    if (!element) {
        // Log, error, whatever...
        return '#000';
    }
    return element.color;
});

const map = new Map({
    layers: [pointLayer],
    target: 'map',
    view: new View([/*...*/])
});

map.addEventListener /* ... */
// Here, on a specific map event, I call updatePoint(eid, position)

None of these solutions/ideas feel right to me, I'm new in using ES6 and I think maybe I'm missing some important ES module concept?
Your help would be greatly appreciated.

0 Answers
Related