javascriptcordovareactjsleafletesri-leaflet

Error message when importing esri-leaflet-renderers to ReactJS+Backbone Cordova app


I've been building a Cordova app as a proof of concept for display about one of my ideas, so far it's been going great and I've come to learn plenty from this experience.

I've based my work off of this project on GitHub, however I'm still very inexperienced with using ReactJS. I'm also venturing further into Leaflet-land together with ESRI's online maps.

I've currently got a route mapped out and published as a FeatureLayer, even managed to successfully import and display it within the app, but of course all the styling to the actual shape is gone. (Colors, for instance.) This is a pretty crucial part. I found a plugin to Esri-Leaflet called Esri-Leaflet-Renderers but I'm having immense difficulties getting it imported/running.

When I add the path to Esri-Leaflet-Renderers to Require's path config and include it on a page, the entire thing locks up and I'm served with the following error message that I have no idea how to debug:

Uncaught TypeError: Cannot read property 'FeatureLayer' of undefined(…)

   

Here is how I have my require.config set-up:

If you take a look at /www/js/main.js they should be identical with the exception of esri-leaflet and esri-leaflet-renderers

require.config({
paths: {
    ...
    leaflet: '../lib/leaflet/leaflet-src',
    'esri-leaflet': '../lib/leaflet/esri-leaflet',
    'esri-leaflet-renderers': '../lib/leaflet/esri-leaflet-renderers'
},
shim: {
    ...
    'leaflet': {
        exports: 'L'
    }
}
});

I'm using the latest version of both leaflet (1.0.1) and esri-leaflet (2.0.4).

   

Maps.js file where the map and all its shenanigans are happening:

define(function (require) {
// Required libs
var Backbone = require("backbone");
var Utils = require("utils");

// Leaflet + ESRI = ❤
var L = {};
L = require("leaflet");
L.esri = require("esri-leaflet");
var renderers = require("esri-leaflet-renderers");

// Extend page function to add new page
var Maps = Utils.Page.extend({
    constructorName: "Maps",

    initialize: function () {
        this.listenTo(this, "inTheDOM", function () {
            var map, ll = new L.LatLng(65.838639, 13.188832), marker, popup, track;
            map = L.map('map', {center: ll, zoom: 14});
            L.esri.basemapLayer("Topographic").addTo(map);
            track = L.esri.featureLayer({
                url: 'http://services.arcgis.com/KDnc9fQhk48mvI9Z/arcgis/rest/services/Snøscooter_Løyper_i_Vefsn_Kommune/FeatureServer/0'
            }).addTo(map);
        });
    },

    id: "map",

    render: function () {
        return this;
    }
});
return Maps; 
});

I can't seem to figure out what I'm doing wrong or what's going wrong. Am I importing everything correctly?

I've been bashing my head against this since lunch, but I've come to realise I need some help. I can't tempt with much, but I will sacrifice my firstborn for a solution!


Solution

  • I finally figured out of it, so I'm answering my own question in case somebody else comes along and wonders about something similar.

    Since I know Leaflet already defines a global variable L, before loading the source files in RequireJS I defined an empty table variable to L.

    L = {}
    

    Then while you're including modules to load in Require, I add both leaflet and esri-leaflet as L and assign L to L.

     

    Working example:

    TLDR: Gist Example

    Code:

    // Leaflet + ESRI-Leaflet fix.
    L = {}
    
    // here we put the paths to all the libraries and framework we will use
    require.config({
        paths: {
            jquery: '../lib/zepto/zepto',
            underscore: '../lib/underscore/underscore',
            backbone: "../lib/backbone/backbone",
            text: '../lib/require/text',
            async: '../lib/require/async',
            handlebars: '../lib/handlebars/handlebars',
            templates: '../templates',
            preloader: '../lib/preloader/pre-loader',
            utils: '../lib/utils/utils',
            leaflet: '../lib/leaflet/leaflet-src',
            'esri-leaflet': '../lib/leaflet/esri-leaflet',
            'esri-fullscreen': '../lib/leaflet/Leaflet.fullscreen.min',
            'esri-renderers': '../lib/leaflet/esri-leaflet-renderers',
        },
        shim: {
            'jquery': {
                exports: '$'
            }
            , 'underscore': {
                exports: '_'
            }
            , 'handlebars': {
                exports: 'Handlebars'
            }
            , 'leaflet': {
                exports: 'L'
            }
        }
    });
    
    // Launch the App
    require(['backbone', 'utils'], function (Backbone, Utils) {
        // Include Leaflet and Leaflet-esri on launch as L and assign it to the global variable.
        require(['preloader', 'router', 'leaflet', 'esri-leaflet'], function (PreLoader, AppRouter, L) {
            L = L; // Globalize on load
    
            document.addEventListener("deviceready", run, false);
    
            function run() {
                // Precompile all templates for faster view switching
                Utils.loadTemplates().once("templatesLoaded", function () {
                    var images = [
                      // Add images for preloading here. One image per line in an absolute path, e.g.:
                      // '/android_asset/www/img/myimage.png',
                    ];
                    if (images.length) {
                        new PreLoader(images, {
                            onComplete: startRouter
                        });
                    }
                    else {
                        // start the router directly if there are no images to be preloaded
                        startRouter();
                    }
    
                    function startRouter() {
                        // launch the router
                        var router = new AppRouter();
                        Backbone.history.start();
                    }
                });
            }
        });
    });