Select to view content in your preferred language

Loading a custom module from different file with API4 and Dojo

3752
6
Jump to solution
01-16-2021 01:49 PM
litch
by
Occasional Contributor

I am trying to create a demo app to understand how to minimize my code by creating more files.

I am using the base starter app sample on one js file and trying to add a widget on another. The code works on the same file but after the divide its breaking.

I added a dojo config, a dojo-ready function and a new define in the widgets.js file.

I am using "define" and not "require" on the widget.js because it's dependent on the main but not sure if it's ok because it will only be called once.

This is how my app looks:

modules(folder)
   -widgets.js
index.html
main.js

index.html

<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
  <title>ArcGIS API for JavaScript Tutorials: Create a Starter App</title>
  <style>
    html, body, #viewDiv {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
    }
  </style>
  <script type="text/javascript">
    dojoConfig = {
      async: true,
      packages: [
        {
          name: "widgets",
          location: "modules/widgets" // or wherever you keep your custom code
        }
      ]
    };
  </script>
  <link rel="stylesheet" href="https://js.arcgis.com/4.18/esri/themes/light/main.css">
  <script src="https://js.arcgis.com/4.18/"></script>
  <script type="text/javascript" src="main.js"></script>
</head>
<body>
  <div id="viewDiv"></div>
</body>
</html>

main.js

require([
  "esri/Map",
  "esri/views/MapView",
  "dojo/ready",
  "widgets/searchWidget"
], function(Map, MapView,ready) {
    ready(function () {


    var map = new Map({
        basemap: "topo-vector"
    });

      var view = new MapView({
        container: "viewDiv",
        map: map,
        center: [-118.80500, 34.02700], // longitude, latitude
      zoom: 13
      });
      
    var widget = new searchWidget(view)
     });


});

widget.js

define(["esri/widgets/Search"], function(Search) { 
var search


return{
      
    searchWidget: function(view){

        search = new Search({
            view: view
          });

        view.ui.add(search, 'top-right');
    }
}


 });
0 Kudos
2 Solutions

Accepted Solutions
ReneRubalcava
Honored Contributor

You need to set an absolute path for your package.

Something like this.

 

<script>
var locationPath = location.pathname.replace(/\/[^\/]+$/, "");
window.dojoConfig = {
  packages: [
    {
      name: "widgets",
      location: locationPath + "/module/widgets"
    }
  ]
};
</script>

 

That little regex will get you the absolute URL of the page your app is running and let the AMD loader know exactly where to find the package. Without it, it assumes it should look at the location the AMD loader is running, which will be on the CDN.

View solution in original post

litch
by
Occasional Contributor

It worked! I also had to change the code to a constructor and add declare.
In the accessor page it states dojo/_base/declare was removed.
Is this the right way to import functions in the api?

main.js

 

 

var loc = location.pathname.replace(/\/[^/]+$/, '');
require({
  packages: [
    { name: "modules", location: loc + "/modules"}
  ]
},[
  "esri/Map",
  "esri/views/MapView",
  "modules/widgets/MySearchWidget",
  "dojo/ready"
], function(
  Map,
  MapView,
  MySearchWidget,
) {
  var map = new Map({
    basemap: "topo-vector"
  });

  var view = new MapView({
    container: "viewDiv",
    map: map,
    center: [-118.80500, 34.02700], // longitude, latitude
    zoom: 13
  });
  
  var SearchWidget = MySearchWidget(view);
});

 


mySearchWidget,js

 

 

define([
  "dojo/_base/declare",
  "esri/widgets/Search"
], function(
  declare,
  Search
) {
  return declare( null, {
    constructor: function( view ) {
      var search = new Search({
        view: view
      });
      view.ui.add( search, "top-right" );
    }
  });
});

 

 

 

 

 

 

 

View solution in original post

0 Kudos
6 Replies
shaylavi
Esri Contributor

Hi,

When you say API4 I assume you mean the new version of the Javascript API, which today is 4.18 and has nothing to do with the WebApp Builder. In that case, you are required to follow a more modern Javascript style, which includes nodeJS, npm for packages installation and Dojo is not really part of the API. You should follow ESRI's documentation and samples to try and understand the workflow but without prior knowledge and good understanding it can be quite overwhelming -- https://developers.arcgis.com/javascript/latest/sample-code/widgets-custom-widget/index.html

To write widgets in the style you suggested, you need to stick to version 3 of the API, which is still what's required if your purpose is to integrate the widget within the WebApp Builder

 

Shay
litch
by
Occasional Contributor

If I am getting this right, the link you sent is for developing widgets from scratch, which is not my intention.
I want to use existing "from the box" widgets in my code but after a while the js file bloats.

In my example the widget is just a simple search widget that consists of a few lines of code,
it worked on the sample with one js file. 
Do i still need to use typescript just to break the code and reference it?


I tried calling it as a src from the script tag and i tried "export"-ing it as a function and importing it in before i found the Dojo solution, but none of them worked.

All the sample code i found was a single page JS, and the "getting started with the API" course mentions how fast the files can grow but doesn't provide an example of multiple files.

do you have any other source or sample that is more inline with my problem or is it not possible at all to avoid using node?

0 Kudos
shaylavi
Esri Contributor

I'm really confused about what you're trying to achieve. If I understand this right, you're working with version 4 of the API, but don't want to create a widget, just call the existing ones OOB + combine dojo to separate them into modules? if that's all correct, you're just trying to learn how to use dojo modules? Yet in your code, you refer to a widget.js file, which points to a widget code structure. 

If you're trying to learn modules in Dojo, I suggest you start with that alone, before you start mixing the API and initialize widgets inside modules.

To understand better on creating modules with Dojo it's best you refer to their documentation.
There's also this good thread - http://dojo-toolkit.33424.n3.nabble.com/define-declare-vs-module-class-td3993005.html

 

Shay
0 Kudos
litch
by
Occasional Contributor

Sorry for not making myself clear.

I don't want to use Dojo at all if possible.

Here's a working app:
All I want to do is to call the search widget from another file:

<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
  <title>ArcGIS API for JavaScript Tutorials: Create a Starter App</title>
  <style>
    html, body, #viewDiv {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
    }
  </style>
  
    <link rel="stylesheet" href="https://js.arcgis.com/4.18/esri/themes/light/main.css">
  <script src="https://js.arcgis.com/4.18/"></script>
  
  <script>  
    require([
      "esri/Map",
      "esri/views/MapView",
      "esri/widgets/Search"
      
      
    ], function(Map, MapView,Search) {

      var map = new Map({
        basemap: "topo-vector"
      });

      var view = new MapView({
        container: "viewDiv",
        map: map,
        center: [-118.80500, 34.02700], // longitude, latitude
      zoom: 13
      });
      
      
        var searchWidget = new Search({
          view: view
        });

        // Add the search widget to the top right corner of the view
        view.ui.add(searchWidget, {
          position: "top-right"
        });


    });
  </script>
</head>
<body>
  <div id="viewDiv"></div>
</body>
</html>


Is that possible without Dojo and Node? if so can you show me how? 

0 Kudos
ReneRubalcava
Honored Contributor

You need to set an absolute path for your package.

Something like this.

 

<script>
var locationPath = location.pathname.replace(/\/[^\/]+$/, "");
window.dojoConfig = {
  packages: [
    {
      name: "widgets",
      location: locationPath + "/module/widgets"
    }
  ]
};
</script>

 

That little regex will get you the absolute URL of the page your app is running and let the AMD loader know exactly where to find the package. Without it, it assumes it should look at the location the AMD loader is running, which will be on the CDN.

litch
by
Occasional Contributor

It worked! I also had to change the code to a constructor and add declare.
In the accessor page it states dojo/_base/declare was removed.
Is this the right way to import functions in the api?

main.js

 

 

var loc = location.pathname.replace(/\/[^/]+$/, '');
require({
  packages: [
    { name: "modules", location: loc + "/modules"}
  ]
},[
  "esri/Map",
  "esri/views/MapView",
  "modules/widgets/MySearchWidget",
  "dojo/ready"
], function(
  Map,
  MapView,
  MySearchWidget,
) {
  var map = new Map({
    basemap: "topo-vector"
  });

  var view = new MapView({
    container: "viewDiv",
    map: map,
    center: [-118.80500, 34.02700], // longitude, latitude
    zoom: 13
  });
  
  var SearchWidget = MySearchWidget(view);
});

 


mySearchWidget,js

 

 

define([
  "dojo/_base/declare",
  "esri/widgets/Search"
], function(
  declare,
  Search
) {
  return declare( null, {
    constructor: function( view ) {
      var search = new Search({
        view: view
      });
      view.ui.add( search, "top-right" );
    }
  });
});

 

 

 

 

 

 

 

0 Kudos