Code Monkey home page Code Monkey logo

esri-loader's Introduction

esri-loader (Deprecated)

deprecated npm npm npm GitHub stars

A tiny library to help you use the ArcGIS Maps SDK for JavaScript AMD modules in applications built with popular JavaScript frameworks and bundlers.

Deprecation Notice: The esri-loader npm package is deprecated at Maps SDK for JavaScript version 4.29 and will be retired at version 4.31. Locally built applications should use the @arcgis/core ES modules npm package. Here are sample applications for getting started. For more information see the building with ES Modules guide topic.

Ready to jump in? Follow the Install and Usage instructions below to get started. Then see more in depth instructions on how to configure esri-loader.

Want to learn more? Learn how esri-loader can help improve application load performance and allow you to use the Maps SDK in server side rendered applications.

Looking for legacy examples from a variety of frameworks, or 3.x information? Visit the archive page.

Table of Contents

Known Limitations

  • Compatibility with frameworks that don't support native async/await in AMD modules at runtime was removed at 4.27 (June 2023). In particular, this affects Angular applications using esri-loader because async/await is not supported in Zone.js. Angular users that run into async/await-related issues will need to migrate off Zone.js or move from AMD modules to using @arcgis/core ES modules in order to continue using the latest releases of the SDK.

Install

npm install --save esri-loader

or

yarn add esri-loader

Usage

The code snippets below show how to load the ArcGIS Maps SDK for JavaScript and its modules and then use them to create a map. Where you would place similar code in your application will depend on which application framework you are using.

Loading Modules from the ArcGIS Maps SDK for JavaScript

From the Latest Version

Here's an example of how you could load and use the WebMap and MapView classes from the latest 4.x release to create a map (based on this sample):

import { loadModules } from 'esri-loader';

// this will lazy load the SDK
// and then use Dojo's loader to require the classes
loadModules(['esri/views/MapView', 'esri/WebMap'])
  .then(([MapView, WebMap]) => {
    // then we load a web map from an id
    const webmap = new WebMap({
      portalItem: { // autocasts as new PortalItem()
        id: 'f2e9b762544945f390ca4ac3671cfa72'
      }
    });
    // and we show that map in a container w/ id #viewDiv
    const view = new MapView({
      map: webmap,
      container: 'viewDiv'
    });
  })
  .catch(err => {
    // handle any errors
    console.error(err);
  });

From a Specific Version

By default esri-loader will load modules from the latest 4.x release of the SDK from the CDN, but you can configure the default behavior by calling setDefaultOptions() once before making any calls to loadModules().

// app.js
import { setDefaultOptions } from 'esri-loader';

// configure esri-loader to use version 4.24 from the ArcGIS CDN
// NOTE: make sure this is called once before any calls to loadModules()
setDefaultOptions({ version: '4.24' })

Then later, for example after a map component has mounted, you would use loadModules() as normal.

// component.js
import { loadModules } from 'esri-loader';

// this will lazy load the SDK
// and then use Dojo's loader to require the map class
loadModules(['esri/map'])
  .then(([Map]) => {
    // create map with the given options at a DOM node w/ id 'mapNode'
    let map = new Map('mapNode', {
      center: [-118, 34.5],
      zoom: 8,
      basemap: 'dark-gray'
    });
  })
  .catch(err => {
    // handle any script or module loading errors
    console.error(err);
  });

You can load the "next" version of the SDK by passing version: 'next'.

From a Specific URL

If you want to load modules from a build that you host on your own server (i.e. that you've downloaded or built with Dojo), you would set the default url option instead:

// app.js
import { setDefaultOptions } from 'esri-loader';

// configure esri-loader to use version from a locally hosted build of the SDK
// NOTE: make sure this is called once before any calls to loadModules()
setDefaultOptions({ url: `http://server/path/to/esri` });

See Configuring esri-loader for all available configuration options.

Lazy Loading

Lazy loading the modules can dramatically improve the initial load performance of your mapping application, especially if your users may never end up visiting any routes that need to show a map or 3D scene. That is why it is the default behavior of esri-loader. In the above snippets, the first time loadModules() is called, it will lazy load the modules by injecting a <script> tag in the page. That call and any subsequent calls to loadModules() will wait for the script to load before resolving with the modules.

If you have some reason why you do not want to lazy load the modules, you can use a static script tag instead.

Loading Styles

You must load the styles that correspond to the version you are using. You'll probably want to lazy load the styles only once they are needed by the application.

When you load the script

The easiest way to do that is to pass the css option to setDefaultOptions():

import { setDefaultOptions, loadModules } from 'esri-loader';

// before loading the modules for the first time,
// also lazy load the CSS for the version of
// the script that you're loading from the CDN
setDefaultOptions({ css: true });

loadModules(['esri/views/MapView', 'esri/WebMap'])
  .then(([MapView, WebMap]) => {
    // the styles, script, and modules have all been loaded (in that order)
  });

Passing css: true does not work when loading the script using the url option. In that case you'll need to pass the URL to the styles like: css: 'http://server/path/to/esri/css/main.css'. See Configuring esri-loader for all available configuration options.

Using loadCss()

Alternatively, you can use the provided loadCss() function to load the ArcGIS styles at any point in your application's life cycle. For example:

import { loadCss } from 'esri-loader';

// by default loadCss() loads styles for the latest 4.x version
loadCss();

// or for a specific CDN version
loadCss('4.25');

// or a from specific URL, like a locally hosted version
loadCss('http://server/version/esri/themes/light/main.css');

See below for information on how to override ArcGIS styles that you've lazy loaded with loadModules() or loadCss().

Using traditional means

Of course, you don't need to use esri-loader to load the styles. See the ArcGIS Maps SDK for JavaScript documentation for more information on how to load the ArcGIS styles by more traditional means such as adding <link> tags to your HTML, or @import statements to your CSS.

Do I need esri-loader?

It is recommended to try installing @arcgis/core and building with ES Modules and instead of using esri-loader. It's also pretty easy to migrate applications built with esri-loader.

For versions of the SDK before 4.18, esri-loader is required when working with frameworks or bundlers. esri-loader provides a way to dynamically load the SDK's AMD modules at runtime from the ArcGIS CDN into applications built using modern tools and framework conventions. This allows your application to take advantage of the fast cached CDN.

esri-loader provides a convenient way to lazy load the modules in any application, and it has been the most versatile way to integrate the ArcGIS Maps SDK for JavaScript with other frameworks and their tools since it works in applications that:

  • are built with any loader/bundler, such as webpack, rollup.js, or Parcel
  • use framework tools that discourage or prevent you from manually editing their configuration
  • make very limited use of the Maps SDK and don't want to incur the cost of including it in their build

Most developers will prefer the convenience and native interoperability of being able to import modules from @arcgis/core directly, especially if their application makes extensive use of the SDK. However, if @arcgis/core doesn't work in your application for whatever reason, esri-loader probably will.

Learn more about which is the right solution for your application.

Advanced Usage

ArcGIS TypeScript Types

This library doesn't make any assumptions about which version of the ArcGIS Maps SDK JavaScript you are using, so you will have to install the appropriate types. Furthermore, because you don't import esri modules directly with esri-loader, you'll have to follow the instructions below to use the types in your application.

Follow these instructions to install the 4.x types.

After installing the 4.x types, you can use the __esri namespace for the types as seen in this example.

TypeScript import()

TypeScript 2.9 added a way to import() types which allows types to be imported without importing the module. For more information on import types see this post. You can use this as an alternative to the 4.x _esri namespace.

After you've installed the 4.x as described above, you can then use TypeScript's import() like:

// define a type that is an array of the 4.x types you are using
// and indicate that loadModules() will resolve with that type
type MapModules = [typeof import("esri/WebMap"), typeof import("esri/views/MapView")];
const [WebMap, MapView] = await (loadModules(["esri/WebMap", "esri/views/MapView"]) as Promise<MapModules>);
// the returned objects now have type
const webmap = new WebMap({portalItem: {id: this.webmapid}});

Types in Angular CLI Applications

For Angular CLI applications, you will also need to add "arcgis-js-api" to compilerOptions.types in src/tsconfig.app.json and src/tsconfig.spec.json as shown here.

esri-loader-typings-helper Plugin

An easy way to automatically get the typings for the SDK modules is to use the esri-loader-typings-helper plugin for VS Code. This plugin will allow you to simply call out an array of modules to import, and when the text is selected and the plugin is called, it will automatically generate the loadModules() code for you in either the async/await pattern or using a Promise:

async example:

typings-helper-async

promise example:

typings-helper-async

Note: this plugin is not restricted to just using TypeScript, it will also work in JavaScript to generate the same code, except without the type declarations.

Configuring esri-loader

As mentioned above, you can call setDefaultOptions() to configure how esri-loader loads ArcGIS Maps SDK for JavaScript modules and CSS. Here are all the options you can set:

Name Type Default Value Description
version string '4.25' The version of the SDK hosted on Esri's CDN to use.
url string undefined The URL to a hosted build of the SDK to use. If both version and url are passed, url will be used.
css string or boolean undefined If a string is passed it is assumed to be the URL of a CSS file to load. Use css: true to load the version's CSS from the CDN.
insertCssBefore string undefined When using css, the <link> to the stylesheet will be inserted before the first element that matches this CSS Selector. See Overriding ArcGIS Styles.

All of the above are optional.

Without setDefaultOptions()

If your application only has a single call to loadModules(), you do not need setDefaultOptions(). Instead you can just pass the options as a second argument to loadModules():

import { loadModules } from 'esri-loader';

// configure esri-loader to use version 4.25
// and the CSS for that version from the ArcGIS CDN
const options = { version: '4.25', css: true };

loadModules(['esri/map'], options)
  .then(([Map]) => {
    // create map with the given options at a DOM node w/ id 'mapNode'
    const map = new Map('mapNode', {
      center: [-118, 34.5],
      zoom: 8,
      basemap: 'dark-gray'
    });
  })
  .catch(err => {
    // handle any script or module loading errors
    console.error(err);
  });

Configuring Dojo

You can set window.dojoConfig before calling loadModules() to configure Dojo before the script tag is loaded. This is useful if you want to use esri-loader to load Dojo packages that are not included in the ArcGIS Maps SDK for JavaScript such as FlareClusterLayer.

import { loadModules } from 'esri-loader';

// can configure Dojo before loading the SDK
window.dojoConfig = {
  // tell Dojo where to load other packages
  async: true,
  packages: [
    {
      location: '/path/to/fcl',
      name: 'fcl'
    }
  ]
};

loadModules(['esri/map', 'fcl/FlareClusterLayer_v3'], options)
  .then(([Map, FlareClusterLayer]) => {
    // you can now create a new FlareClusterLayer and add it to a new Map
  })
  .catch(err => {
    // handle any errors
    console.error(err);
  });

Overriding ArcGIS Styles

If you want to override ArcGIS styles that you have lazy loaded using loadModules() or loadCss(), you may need to insert the ArcGIS styles into the document above your custom styles in order to ensure the rules of CSS precedence are applied correctly. For this reason, loadCss() accepts a selector (string) as optional second argument that it uses to query the DOM node (i.e. <link> or <script>) that contains your custom styles and then insert the ArcGIS styles above that node. You can also pass that selector as the insertCssBefore option to loadModules():

import { loadModules } from 'esri-loader';

// lazy load the CSS before loading the modules
const options = {
  css: true,
  // insert the stylesheet link above the first <style> tag on the page
  insertCssBefore: 'style'
};

// before loading the modules, this will call:
// loadCss('https://js.arcgis.com/4.25/themes/light/main.css', 'style')
loadModules(['esri/views/MapView', 'esri/WebMap'], options);

Alternatively you could insert it before the first <link> tag w/ insertCssBefore: 'link[rel="stylesheet"]', etc.

Pre-loading the ArcGIS Maps SDK JavaScript

Under the hood, loadModules() calls esri-loader's loadScript() function to lazy load the SDK by injecting a <script> tag into the page.

If loadModules() hasn't yet been called, but you have good reason to believe that the user is going take an action that will call it (i.e. transition to a route that shows a map), you can call loadScript() ahead of time to start loading SDK. For example:

import { loadScript, loadModules } from 'esri-loader';

// preload the SDK
// NOTE: in this case, we're not passing any options to loadScript()
// so it will default to loading the latest 4.x version of the SDK from the CDN
loadScript();

// later, for example after transitioning to a route with a map
// you can now load the map modules and create the map
const [MapView, WebMap] = await loadModules(['esri/views/MapView', 'esri/WebMap']);

See Configuring esri-loader for all available configuration options you can pass to loadScript().

NOTE: loadScript() does not use rel="preload", so it will fetch, parse, and execute the script. In practice, it can be tricky to find a point in your application where you can call loadScript() without blocking rendering. In most cases, it's best to just use loadModules() to lazy load the script.

Using your own script tag

It is possible to use this library only to load modules (i.e. not to lazy load or pre-load the ArcGIS Maps SDK for JavaScript). In this case you will need to add a data-esri-loader attribute to the script tag you use to load the SDK. Example:

<!-- index.html -->
<script src="https://js.arcgis.com/4.25/" data-esri-loader="loaded"></script>

Without a module bundler

Typically you would install the esri-loader package and then use a module loader/bundler to import the functions you need as part of your application's build. However, ES5 builds of esri-loader are also distributed on UNPKG both as ES modules and as a UMD bundle that exposes the esriLoader global.

This is an excellent way to prototype how you will use the ArcGIS Maps SDK for JavaScript, or to isolate any problems that you are having with the SDK. Before we can help you with any issue related to the behavior of a map, scene, or widgets, we will require you to reproduce it outside your application. A great place to start is one of the codepens linked below.

Using a module script tag

You can load the esri-loader ES modules directly in modern browsers using <script type="module">. The advantage of this approach is that browsers that support type="module" also support ES2015 and many later features like async/await. This means you can safely write modern JavaScript in your script, which will make it easier to copy/paste to/from your application's source code.

<script type="module">
  // to use a specific version of esri-loader, include the @version in the URL for example:
  // https://unpkg.com/[email protected]/dist/esm/esri-loader.js
  import { loadModules } from "https://unpkg.com/esri-loader/dist/esm/esri-loader.js";

  const main = async () => {
    const [MapView, WebMap] = await loadModules(['esri/views/MapView', 'esri/WebMap']);
    // use MapView and WebMap classes as shown above
  }
  main();
</script>

You can fork this codepen to try this out yourself.

A disadvantage of this approach is that the ES module build of esri-loader is not bundled. This means your browser will make multiple requests for a few (tiny) JS files, which may not be suitable for a production application.

Using the esriLoader Global

If you need to run the script in an older browser, you can load the UMD build and then use the esriLoader global.

<!--
  to use a specific version of esri-loader, include the @version in the URL for example:
  https://unpkg.com/[email protected]
-->
<script src="https://unpkg.com/esri-loader"></script>
<script>
  esriLoader.loadModules(['esri/views/MapView', 'esri/WebMap'])
  .then(function ([MapView, WebMap]) {
    // use MapView and WebMap classes as shown above
  });
</script>

You can fork this codepen to try this out yourself.

Pro Tips

Using Classes Synchronously

Let's say you need to create a map in one component, and then later in another component add a graphic to that map. Unlike creating a map, creating a graphic and adding it to a map is ordinarily a synchronous operation, so it can be inconvenient to have to wait for loadModules() just to load the Graphic class. One way to handle this is have the function that creates the map also load the Graphic class before its needed. You can then hold onto that class for later use to be exposed by a function like addGraphicToMap(view, graphicJson):

// utils/map.js
import { loadModules } from 'esri-loader';

// NOTE: module, not global scope
let _Graphic;

// this will be called by the map component
export function loadMap(element, mapOptions) {
  return loadModules(['esri/Map', 'esri/views/MapView', 'esri/Graphic'])
  .then(([Map, MapView, Graphic]) => {
    // hold onto the graphic class for later use
    _Graphic = Graphic;
    // create the Map
    const map = new Map(mapOptions);
    // return a view showing the map at the element
    return new MapView({
      map,
      container: element
    });
  });
}

// this will be called by the component that needs to add the graphic to the map
export function addGraphicToMap(view, graphicJson) {
  // make sure that the graphic class has already been loaded
  if (!_Graphic) {
    throw new Error('You must load a map before creating new graphics');
  }
  view.graphics.add(new _Graphic(graphicJson));
}

You can see this pattern in use in a real-world application.

See #124 (comment) and #71 (comment) for more background on this pattern.

Server Side Rendering

This library also allows you to use the SDK in applications that are rendered on the server. There's really no difference in how you invoke the functions exposed by this library, however you should avoid trying to call them from any code that runs on the server. The easiest way to do this is to call loadModules() in component lifecyle hooks that are only invoked in a browser, for example, React's useEffect or componentDidMount, or Vue's mounted.

Alternatively, you could use checks like the following to prevent calling esri-loader functions on the server:

import { loadCss } from 'esri-loader';

if (typeof window !== 'undefined') {
  // this is running in a browser, so go ahead and load the CSS
  loadCss();
}

See next-arcgis-app or esri-loader-react-starter-kit for examples of how to use esri-loader in server side rendered (SSR) applications.

FAQs

In addition to the pro tips above, you might want to check out some frequently asked questions.

Updating from previous versions

From < v1.5

If you have an application using a version that is less than v1.5, this commit shows the kinds of changes you'll need to make. In most cases, you should be able to replace a series of calls to isLoaded(), bootstrap(), and dojoRequire() with a single call to loadModules().

From angular-esri-loader

The angular-esri-loader wrapper library is no longer needed and has been deprecated in favor of using esri-loader directly. See this issue for suggestions on how to replace angular-esri-loader with the latest version of esri-loader.

Dependencies

Browsers

This library doesn't have any external dependencies, but the functions it exposes to load the SDK and its modules expect to be run in a browser. This library officially supports the same browsers that are supported by the latest version of the ArcGIS Maps SDK for JavaScript.

You cannot use this helper library in Node.js, but you can use this library in server side rendered applications as well as Electron. If you need to execute requests to ArcGIS REST services from something like a Node.js CLI application, see ArcGIS Rest JS.

Promises

The asynchronous functions like loadModules() and loadScript() return Promises, so if your application has to support browsers that don't support Promise (i.e. IE) you have a few options.

If there's already a Promise implementation loaded on the page you can configure esri-loader to use that implementation. For example, in ember-esri-loader, we configure esri-loader to use the RSVP Promise implementation included with Ember.js.

import { utils } from  'esri-loader';

init () {
  this._super(...arguments);
  // have esriLoader use Ember's RSVP promise
  utils.Promise = Ember.RSVP.Promise;
},

Otherwise, you should consider using a Promise polyfill, ideally only when needed.

Licensing

Copyright Β© 2016-2022 Esri

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

A copy of the license is available in the repository's LICENSE file.

esri-loader's People

Contributors

alexurquhart avatar andygup avatar calebm1987 avatar davetimmins avatar davewilton avatar davidpiesse avatar dependabot[bot] avatar deskoh avatar dzeneralen avatar ecaldwell avatar gavinr avatar gavinr-maps avatar gpbmike avatar guzhongren avatar haidarz avatar jameslmilner avatar jgravois avatar joshcrozier avatar jwasilgeo avatar lomash avatar remotesc2 avatar richorama avatar ssylvia avatar stdavis avatar tgirgin23 avatar thekeithstewart avatar tomwayson avatar vannizhang avatar ziveo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

esri-loader's Issues

loadScript should return a singleton promise, or DEFAUL_URL should be configurable library-wise

I've a probelm in my vuejs app due to the async nature of loadScript and loadModules.
When i boot my vue app i bootstrap also the esri library with loadScript({ url: "https://js.arcgis.com/3.22/init.js"}) and in some of my vue components i load modules with something like loadModules(['esri/geometry/geometryEngine'])
If loadScript with the custom version of the library is not completed, loadModules is rejected.
The solution is to provide the same promise instance for every loadScript call or maybe provide a configurable DEFAULT_URL or seti it with the first call of loadScript.

Angular5 + CLI + API 3.23 Module not found:

Hello @ALL

I am using Angular 5 CLI and JS API 3.23. I want to use us Map object as

import Map = require('esri/map');

But getting following error
Module not found: Error: Can't resolve 'esri/map'

Here is the Map component

import { Component, OnInit, ViewChild, ElementRef, Input, Output, EventEmitter } from '@angular/core';
import { loadModules } from 'esri-loader';
import Map = require('esri/map');
import Search = require('esri/dijit/Search');

@component({
selector: 'app-esri-map',
templateUrl: './esri-map.component.html',
styleUrls: ['./esri-map.component.css']
})

export class EsriMapComponent implements OnInit {

constructor(private elRef: ElementRef) { }

public ngOnInit() {
let map = new Map(this.elRef.nativeElement, {
center: [-118, 34.5],
zoom: 8,
basemap: 'streets'
});
}
}

If I use the following code on ngOnInit and comment the import map line then it works fine

const options = {
url: 'https://js.arcgis.com/3.23/'
};
loadModules(['esri/map'], options)
.then(([Map]) => {
// create map with the given options at a DOM node w/ id 'mapNode'
let map = new Map(this.elRef.nativeElement, {
center: [-118, 34.5],
zoom: 8,
basemap: 'streets'
});
})
.catch(err => {
// handle any script or module loading errors
console.error(err);
});

Thanks In advance

break src into modules and improve testing

Expected behavior

  • we can break esri-loader.ts into multiple files/modules (utils.ts) each w/ own test file that imports only the modules it needs to test
  • we can use karma and it's watcher/plugins to run tests w/o having to use external watchers and bundlers
  • tests can be written in TS

Actual behavior

  • utils is included in esri-loader.ts
  • the current test script uses an external watcher to run a production build and then karma each time a file changes
  • tests are written in ES5

webpack errors and warnings when npm linked from ES2015 webpack app

When I first tried to use this library via npm link in an ES2015 app I was getting Module build failed: Error: Couldn't find preset "es2015" errors. Based on this SO post I was able to get past those by installing the package from the NPM registry. It looks like I could fix that by configuring webpack in the consuming app, but I wonder if there any settings that could be set on this end that would obviate that.

Once I got past those errors, I now see warnings in the webpack output in the terminal. The main one I'm concerned with as far as this library goes is:

WARNING in ./~/esri-loader/dist/index.js
Critical dependencies:
3:24-31 require function is used in a way in which dependencies cannot be statically extracted
 @ ./~/esri-loader/dist/index.js 3:24-31

I wonder if there's some way to to suppress that, or if it's better to leave it to let consuming apps know.

IE11 With Angular 5 and EsriLoader and Version 3.x not working

We are currently using Angular 5 alongside JavaScript v3.x, with the esri-loader.

We are using this string to configure the URL for 3.x:
this._esriOptions = {
url: 'https://js.arcgis.com/3.23/'
};

The problem we are facing is we are not able to pan on the map in IE11. It pans just fine in Chrome. We are able to move through setting the center of the map, but this is not an elegant solution. We are able to zoom in/out and draw on the map.

We also set it up using version 4.x in both chrome and IE11 and it works fine.

Any help would be much appreciated.

Enable using this library to pre-load the JSAPI

Example:

  1. user visits index route (no map, no JSAPI yet)
  2. app renders index page, then starts loading JSAPI (bootstrap()) so that map route loads faster once people go there
  3. user clicks link to map route
  4. map route checks if already loaded (isLoaded()) or loading, if not, load JSAPI. Once JSAPI is loaded, create the map

The only thing new here (when compared to the lazy loading example) is keeping track of the notion that the JSAPI is already in the process of being loaded and providing a hook to do something once it's done. Currently users can pass a callback, but in a preloading scenario, the context is likely to change between when a user calls bootstrap()and when they want to actually create a map.

One idea is to expose an isLoading property and .onLoad(fn) (or .on('load', fn)) event. I'm not a real big fan of having both a callback and an event, so if we go this route, may want to deprecate the callback? Not sure.

Thoughts welcome.

Make `getScript()` a public API?

Why not?

Also, this change has always bothered me a bit. I think isLoaded() should not be concerned with how the script tag was added to the page. Making getScript() a public API would pave the way to revert that change (at the next major release) b/c consumers could just call isLoaded() && getScript() if needed.

cc @TheKeithStewart

Release artifacts

The release should include an esm build as well as the umd build. Also should make sure the types/interfaces are released in a way that they can be consumed by TypeScript apps/libraries, however that works.

We should update package.json to point to those.

document API

Either add section to README by hand, or automate with something like https://github.com/TypeStrong/typedoc

If the latter, where to host the output? gh-pages? I think we'll want something more than just API docs there, like a lot of what's in the README. Then how to keep those in sync?

WebGL?

Is there a way to make sure to add:

<script>
  var dojoConfig = {
    has: {
      "esri-featurelayer-webgl": 1
    }
  };
</script>

(^^^^^ from the docs)
It would be nice to have improved performance of vector drawings.

This should probably work in an ES5 app

B/c this outputs to CJS there are exports statements in the built code (https://unpkg.com/[email protected]/index.js) this won't work in ES5. I tried pulling the build output into Ember via bower and it chokes on the exports.

Seems like CJS was needed for Angular dc92251. It may be that the tsc UMD build output was problematic for Angular (now starting to see why other libs use webpack instead of tsc to do builds).

Would love to know that the right pattern is here.

`script.dataset` is not compatible with ie10

With esriLoader, ie 10 reported the following error:

TypeError, unable to set property 'esriLoader' of undefined or null reference

Then I found that the value of script.dataset is undefined in IE10. After I moved the source out and deleted all the sentences where script.dataset is used, everything worked fine. #

angular 2 esri-loadr multipleDefine error

I don't why but I have some issue with multipledefine and I don't know how to resolve it.

My index.html looks like this

image

I got this error in the console google chrome

image

Logic when I call esri loader looks like this
image

Thanks

about esriLoader.dojoRequire() in vue

I always have this kind of problem:
When I use "esri/tasks/IdentifyTask", "esri/tasks/IdentifyParameters" or "esri/tasks/FindTask","esri/tasks/FindParameters" or
"esri/tasks/QueryTask","esri/tasks/Query".

Browsers always make the same mistake:
IdentifyTask.js:4 Uncaught (in promise) TypeError: Cannot read property 'path' of undefined.

But when I note this line of code: var params = new IdentifyParameters() , it's get right.
I don't konw how to solve it.

code:
map.on("load", mapReady);

      var identifyTask = new IdentifyTask(parcelsURL)
      var identifyParams = new IdentifyParameters()
      function mapReady () {
        console.log(11111111)
        ...........
      }

And when I input this code to the function mapReady, it also have some problem:
the basemap of the map can't be loaded in the first time, and this code can't be implemented.

code:
map.on("load", mapReady);

      function mapReady () {
        console.log(11111111)
        var identifyTask = new IdentifyTask(parcelsURL)
        var identifyParams = new IdentifyParameters()
        ...................
      }

enable "promisify" / "denodify" of function that loads the JSAPI

As a stopgap to #8 we could make it easier for consumers and the wrapper libraries to promisify / denodify the bootstrap function by switching the order of the arguments so that the callback comes last.

Instead of making a breaking change for this, I propose deprecating bootstrap() in favor of a new function w/ the correct argument order. I've never been a huge fan of the name "bootstrap" anyway. I'm leaning toward loadScript() but am open to suggestion.

I also propose that we make the changes suggested in #28 in this new function since those might be considered breaking. As long as we're making what would otherwise be breaking changes in a new function called loadScript(), I suggest that callback for this new function always return the HTMLScriptElement instead of window.require in order to address the issue I brought up in #28 (comment). Or maybe it would be more useful to return the verson of the JSAPI that was loaded?

My goal for this is to reduce these ~50 lines in ember-esri-loader to:

const loadScript = RSVP.denodeify(esriLoader.loadScript);
this._loadPromise = loadScript(options)
.then(version => {
  // update the isLoaded computed property
  this.notifyPropertyChange('isLoaded');
  // TODO: remove success: true next major version bump?
  return { success: true, version };
});

Weird script load in Firefox & Edge [dojo/b.js]

Expected behavior

  • Describe what you expected or wanted to happen.
    I want to async load arcgis javascript api 4.6 scripts into Angular 5 app. The arcgis scripts are hosted locally.

Actual behavior

In Chrome: Works. As expected.
In Firefox & Edge: While loading scripts, esri-loader adds <script> tags into page header. That happens also in Chrome, but for some reason in Firefox & Edge it also tries to include dojo/b.js file. In Chrome there is no such file attempted to load, that's why in Chrome it works.
esri-bullshit

Obviously this file does not exists in arcgis javascript api and Firefox & Edge throws this errors:
esri-bullshit-error

Steps to reproduce the behavior

this is how I'm loading scripts hosted locally. I tried both with loadScript() and loadModules() with configuration object passsed in:

async loadEsri() {
try {
                await loadScript({
                    url: '/assets/arcgis-js-api/4.6/dojo/dojo.js',
                    dojoConfig: {
                        async: true,
                        has: {
                            'esri-promise-compatibility': 1
                        }
                    }
                });
                let modules = await loadModules([
                     'esri/Map'
                     // ...more modules here...
                ]);
                this.Map = modules[0];
 }
catch (e) {
                console.error('ArcGIS: Error while downloading esri modules: ', e);
 }
}

this happens in cli ng serve dev build. the build passes fine. error is at runtime.

JSAPI 4.2

4.2 is out, bump default url to //js.arcgis.com/4.2

Use promises instead of callbacks

Carrying over the conversation from #7 (comment)

I agree promises are nice, but, I haven't figured out a good way to know which flavor of promises (if any) exist in the client app. I could say this library depends on promise polyfill, I suppose, but I don't want to trip anybody up, so I've catered to the lowest common denominator and left "promisifying" it up to framework-specific wrapper libraries like https://github.com/tomwayson/angular2-esri-loader that can be sure what kind of promises exist in the app.

If anyone knows of a good example of a framework-agnostic JS lib that is smart about how it uses promises (i.e. doesn't force the app to load a promise polyfill or library if it already has one), I'd certainly consider changing the API over to using promises.

how to load esri config module

Hi,

I want to use proxy map server. To do so i will need esri config module. Can you please guide, how can I use esri config module?

Regards,

Fawad

Add option to defer script?

I've tested this, and I can't tell any difference when you add the defer to the script attribute when preloading, but it would be very easy to support passing that in as an option.

Error while running inside of Electron

I am writing a mapping application that is going to be running inside of Electron. I am trying to use this to load the ArcGIS API for JavaScript modules but I am getting the following error:

ERROR Error: Uncaught (in promise): AssertionError: path must be a string
AssertionError: path must be a string
    at Module.require (module.js:497)
    at require (internal/module.js:20)
    at Object.dojoRequire (index.js:44)
    at esri-loader.service.js:41
    at new ZoneAwarePromise (zone.js:776)
    at EsriLoaderService.loadModules (esri-loader.service.js:39)
    at esri4-map.service.js:15
    at ZoneDelegate.invoke (zone.js:365)
    at Object.onInvoke (core.es5.js:4145)

The line of code that this is failing on is:

window['require'](modules, callback);

Because of the fact that this is running inside of Electron and therefor is loading in a NodeJS process the require() function accepts a string as an argument rather than an array of strings.

I have created a repo to demonstrate this issue. Instructions for running this application inside of Electron can be found in its README.

One possible solution to this issue is to use RequireJS for loading modules. This is generally not meant to be used in a NodeJS environment but should provide the API that you are looking for with the window['require'](modules, callback) call.

Server rendering problem

Hi esri-loader team !

I experienced a bad issue when using version from 1.5.x with React server rendering : window.Promise was not defined.

The workaround was quite simple but not esthetic...
Maybe you can fix it and make this library server rendering aware ?

=> link to the problem :

Promise: window['Promise']

Preloading a specific versions of the JSAPI forces consumers to pass same url to loadModules()

Kind of an edge case here, but if you pass a specific JSAPI version when preloading (i.e. loadScript({ url: 'https://js.arcgis.com/3.22' })) then you have to make sure that subsequent calls to loadModules() include that same url option. If you don't, and the JSAPI hasn't loaded by the time loadModules() is called, it will try to lazy load the default version (latest 4.x) and it will reject with an error.

One solution is to just document the above.

Another idea would be to cache url that it's attempting to load the first time loadScript() is called and then default to that (instead of latest 4.x) for subsequent calls - either at the loadModules() or loadScript() level.

only return an error when trying to load a different version of the JSAPI

Currently, we throw return an error when the user calls bootstrap more than once, regardless of:

  • whether or not the first script tag is still loading
  • the first script tag is for the same version of the JSAPI as subsequent script tags

Currently this is causing problems in ember-esri-loader tests (Esri/ember-esri-loader#27). I can work around those problems in that library, but I am wondering if it actually makes sense to return an error in all the above conditions.

I'm pretty sure we want to return an error whenever the user is trying to load a different version of the JSAPI.

If it's the same version of the JSAPI, and if the script has already loaded we should just call the callback w/ window.require as we do here:

script.dataset['esriLoader'] = 'loaded';
// we can now use Dojo's require() to load esri and dojo AMD modules
const dojoRequire = window['require'];
if (callback) {
// let the caller know that the API has been successfully loaded
// and as a convenience, return the require function
// in case they want to use it directly
callback(null, dojoRequire);

If the script hasn't already loaded, I think we want to piggy back on the original onload call.

IE11: "TypeError: Object doesn't support this action"

I switched from esriLoader.bootstrap and esriLoader.dojoRequire to esriLoader.loadModules in my Vue.js app using jsapi 3.20. This is working fine in Chrome, but IE11 gives the error above.

I've tried these promise polyfills, with the same results: "promise-polyfill": "^6.0.2" and "es6-promise": "^4.1.1" and verified that window.Promise exists.

I also tried adding a script tag to my index.html:

<script src="https://js.arcgis.com/3.20/" data-esri-loader="loaded"></script>

but I get the same result.

Any ideas?

11-17-2017 3-39-21 pm

loadScript has option to also create JSAPI css link tag(s)

How about getting the bootstrap method to also generate a CSS <link> tag when it creates the JS <script> tag? Could lead to fewer steps for consuming developers to have to remember to deal with.

https://github.com/tomwayson/esri-loader/blob/master/index.ts#L20

Perhaps the options could be extended to include not only the JS url but also the CSS url? This would be helpful since the CSS url has different structures throughout the different incarnations of JSAPI 3.x and 4.x. Something like:
options.cssUrl = 'https://js.arcgis.com/4.2/esri/css/main.css';

Can't seem to access map or view instances after updating component with data

code link https://gist.github.com/Chonguis/348ac0914c86bf5da8fb24da7d9383a2

Hey everyone. I've been playing with react for about a month now but have worked with the arcgis js api for almost a year. I'm having a problem trying to update the map or view instances once I pass data to the esrMapiComponent. Let's say I'm passing a data object with coordinates. The initial state of the esriMapComponent has a default view on brooklyn. The user then submits a search that makes an ajax request to an API and this API responds with coordinates. Once I have this data object, I return the esriMapComponent passing in the data as props. In this, I am successful and doing a simple log on the render method will indicate that data indeed was passed to the esriMapComponent. The problem is that since the esri modules are loaded in the componentDidMount lifecycle hook, nothing seems to run again and I can't access the view or map instances created at initial state. I then want to go to view.goTo(coordinates) but I'm having trouble because if I put a console log inside of this lifecycle hook, it won't run again when the component is updated by passing the data from parent component (Map route).

My questions are: Is there another place where I could have access to the view, map, scenelayer instances that are not enclosed in this lifecycle hook? Is this problem specific to this library (react starter kit with esri-loader). Is there a better way to achieve what I want to do using the loader maybe in another way?

Hope example is clear and that this can be fixed (crossing fingers). Thank you in advance!

Provide a way to synchronously use modules that you know are loaded

One of the few drawbacks of the esri-loader approach is that just getting a reference to JSAPI modules is always asynchronous, even if we know the module has already been loaded (i.e. was included in the Dojo build layer). A good example is esri/config - you may want to set corsEnabledServers on that before loading a map. Currently you'd have to call loadModules(['esri/config']).then(esriConfig => {}) to do that.

However, if you pass a single module id as a string (instead of an array of module ids) to Dojo's require() it will return that module synchronously as long as it has already been loaded. See the "alternative require() syntax" in the Dojo loader docs. So, this library could expose a function that wraps a call to require(moduleId) using that syntax like:

import { getModule } from 'esri-loader';
const esriConfig = getModule('esri/config');

This would be a naive pass through, and would not attempt to verify that the script has loaded nor that the module exists nor fallback to loading it asynchronously, etc. It would just throw an error in those cases.

If you think this would be a useful feature, add a πŸ‘

Conversely if you think this is not useful, add a πŸ‘Ž

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.