ArcGIS Api (Esri) triggering multipleDefine error - dojo

I have this weird issue while using ArcGIS API for JavaScript v4.4 in my code. I am trying to build an Excel Web Add-in in which I would like to load an ArcGIS map but when I load ArcGIS I get a multipleDefine error.
ArcGIS is getting bundled with Dojo which is used as the loader for all the ArcGIS/esri packages. I have no other choices to load my own custom JS bundles with Dojo because of the way ArcGIS has built their API. So I can't decide to not use Dojo and thus not getting the multipleDefine error.
I load my own JS files like this:
<script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js" type="text/javascript"></script>
<script>
var dojoConfig = {
parseOnLoad: false,
async: false,
// we make aliases for our js file
aliases:  [
['index',  './Bundles/index.js'],
],
};
</script>
<script src="https://js.arcgis.com/4.4/init.js"></script>
<script>
require(['index'], function (index) {
//...do something
});
</script>
When I restart the page I get a multipleDefine error once in every two/three trials. After a lot of investigation I understood that the error lies with the Office.js API but I had a hard time to find a good solution.

After a while I found the cause of the problem; we cannot start office-js and Dojo together because they both want to add scripts in the head tag of our page and somehow they end up in conflict with one another, thus we get the dreaded multipleDefined Dojo error and some of our files do not get loaded.
Once this cause was identified I decided to solve it by making sure Dojo, Argis and my custom js files got loaded once Office and dependencies were fully loaded.
I implemented it like this in my js code:
// This Dojo config variable has to be defined before Dojo is loaded in our scripts
var dojoConfig = {
// we make aliases for our custom js file
aliases: [
['index', './Bundles/index.js'],
],
// We require our dependencies (our own code bundles) with Dojo.
// Notice that it is mandatory to bundle our js files
// as AMD Modules for Dojo to be able to load them as dependencies.
deps: ['index'],
};
// Once office has fully initialized we can add our arcgis file and let
// him load our own required javascript files.
// We cannot start Office-js and Dojo/Arcgis together because they both
// want to add scripts in the head tag of the HTML page and
// somehow they end up in conflict, thus we get the dreaded
// multipleDefined Dojo error and some of our files
// do not get loaded.
Office.initialize = function (reason) {
// we manually add the Arcgis script to the header of our page
// once we are sure Office and dependencies has fully loaded.
var tag = document.createElement('script');
tag.src = 'https://js.arcgis.com/4.4/init.js';
document.getElementsByTagName('head')[0].appendChild(tag);
};
Once this was added the code started working like a charm.

Related

Embedding an Emscripten graphical application to a Vue component (SPA)

I want to embed an Emscripten graphical application to a Vue SPA. The output of Emscripten is a .js and a .wasm file. I cannot get rid of the javascript file and write my own because it contains the proxy for the glfw API which I'm using for input handling.
I can load the javascript (with WASM) successfully with the following code:
This code injects a new <script> tag to the DOM and loads the javascript file.
injectScript(source) {
let script = document.createElement('script');
script.type = 'text/javascript';
script.src = source;
document.head.appendChild(script);
document.head.removeChild(script);
}
// During mount
injectScript("xy.js")
I have a similar method that creates the global Module object for Emscripten using the DOM:
// template
<canvas id="canvas">
// During mount
injectCode(`
var canvas = document.getElementById('canvas');
var Module = {
canvas: canvas,
};
`);
This method works when I open the page for the first time. Since we are talking about a single page application the javascript context is kept and the same code fails to create the wasm context for the second time when I reopen the page.
I'm also interested in completely different solutions but I would like to keep glfw for input handling in the graphics code.
According to the Emscripten documentation the Module object supposed to have a destroy() method. I would try to call It when the Vue component unmounted but mine doesn't have It.
Tried to null the Module object during unmount and It didn't work.

Vue.js including non-npm JavaScript library

I'm a total beginner with Vue.js and struggling to find the answer to what I feel is a fairly basic need.
I have a JavaScript library that cannot be installed locally and must be imported via script tag in the index.html file in the old-fashioned way:
<script src="https://foo.bar/scriptyscripts.js"></script>
This library has a bunch of methods in it that I need to use in various spots throughout my app, so it's not going to be a problem to load it globally. The issue I'm facing is that it's loading fine, but the methods are not being recognised in components.
I can use the methods and whatnot if I put them all in a script tag in the index.html however doing that rather defeats the whole point of having components.
Can anyone help me with the step that I'm missing to register all of the methods in this loaded js file so my components don't get mad?
Specifically, the script contains require.js and a collection of other things including JQuery.
Including the library makes the method 'require' available, which is used to load other modules on demand - the example being "js/qlik" in the below snippet. "js/qlik" loads JQuery and a stack of stuff associated with "qlik".
//async login method here. not relevant to this problem
login().then(() => {
require.config({
baseUrl:
(config.isSecure ? "https://" : "http://") +
config.host +
(config.port ? ":" + config.port : "") +
config.prefix +
"resources",
webIntegrationId: config.webIntegrationId,
});
//Load js/qlik after authentication is successful
require(["js/qlik"], function (qlik) {
qlik.on("error", function (error) {
$("#popupText").append(error.message + "<br>");
$("#popup").fadeIn(1000);
});
$("#closePopup").click(function () {
$("#popup").hide();
});
var app = qlik.openApp("caa866be-c8e1-44c8-b67b-dac9d24421fa", config);
});
});
The problem I have is that if I load this library in the index.html file and then try to execute the methods in the snippet above in any component, it does not know that the methods are available.
I see:
'Module not found: Error: Can't resolve 'js/qlik'
66:11 error '$' is not defined
which indicates that the components are unaware of the methods because they're not registered like they would be if I were importing a packaged afterinstalling it locally via NPM
i.e. Your original js code: function abc(){// sth...}
What you need: window.abc = ()=>{// sth...}
Even if you want it in Vue dom.
You should add vue.prototype.abc = ()=>{//sth...}

Multiple Aurelia Instances - Aurelia Webpack Plugin - aureliaApp option - "module not found"

I am composing my web app as a number of Aurelia "feature" apps - although I'm not using Aurelia features as such. Consequently in my html markup I have two entry points pointing to different apps:
<!-- Top Navigation Bar -->
<div aurelia-app="topnav"></div>
<!-- Main App-->
<div aurelia-app="main"></div>
I am using webpack and everything works perfectly using the single "main" app. Webpack generates a JS file "main.bundle.js" which I include in the src tag.
Things are not so straightforward when I added the "topnav" app. In webpack I tell the plugin to use a different aureliaApp name:
new AureliaPlugin({ aureliaApp: "topnav"}),
and, as you can see my HTML entrypoint also calls "topnav". Webpack generates a JS file "topnav.bundle.js" which I also include. I have a file called "topnav.ts" which contains the aurelia Cionfigure function which ends:
aurelia.start().then(() => aurelia.setRoot(PLATFORM.moduleName("nav")));
And a pair of files "nav.ts", "nav.html" which constitute my viewmodel and view.
When I run the app aurelia loads and the "nav" module code executes. But I then get an error - see below.
The module which it reports that it cannot find is the one entered into the HTML markup.
Should this work? Have I missed something?
I should add, everything seems to work. I can create and update properties in the viewmodel and these are bound to the view. It's just that this error is thrown.
You are doing nothing wrong, just unsupported scenario. Per official doc-wiki: https://github.com/aurelia/webpack-plugin/wiki/AureliaPlugin-options#aureliaapp
You can have only 1 auto entry module with aureliaApp configuration. To solve this, you just need to add PLATFORM.moduleName('topnav') to your main.ts (and put it on root level)
Another way to do is to bootstrap manually:
// in your index.ts
import { bootstrap } from 'aurelia-bootstrapper';
// bootstrap top nav application, with one instance of Aurelia
bootstrap(aurelia => {
// do your configuration
aurelia
.start()
.then(() => aurelia.setRoot(
PLATFORM.moduleName('topnav'),
document.querySelector('#topnav')
);
});
// bootstrap main application, with another instance of Aurelia
bootstrap(aurelia => {
// aurelia.use.standardConfiguration();
// ...
aurelia
.start()
.then(() => aurelia.setRoot(
PLATFORM.moduleName('app'),
document.querySelector('app')
)
});

Arcgis conflict in Liferay 7.0

I try to use Arcgis javascript API in Liferay 7.0 but it's fail. I think because Liferay 7.0 using requireJS for using javascript which conflict with dojo of Arcgis javascript API. I am using this code for implement Arcgis API:
<script type="text/javascript" src="<%=request.getContextPath() %>/js/arcgis_js_api/library/3.14/3.14/init.js"></script>
<script>
require([
"esri/map", "dojo/dom"
], function(Map, dom) {
var map = new esri.Map("map", {
basemap: "topo", //For full list of pre-defined basemaps, navigate to http://arcg.is/1JVo6Wd
center: [-122.45, 37.75], // longitude, latitude
zoom: 13
});
});
This is console log:
java.lang.IllegalArgumentException: Path esri/map.js does not start with a "/" character
java.lang.IllegalArgumentException: Path dojo.js does not start with a "/" character
This is javascript error:
Error: defineAlreadyDefined
Anyone has solution help me please. Thanks!
As you mention Liferay7 and your script tag makes use of <%=request.getContextPath()%>, you most likely have a mismatch between your portlet and the servlet API: request is not aware of any portlet context (but is still there, because JSPs have been designed for servlets and it's a mandatory object that's around, irritating a lot of people)
The URLs for your own Javascript files are just not relative to the context root, but Liferay makes them available on a different path. The easiest way, without messing around with this location, is to just mention the file you'd like to include in your portlet's configuration. In Liferay 7 OSGi portlet modules, it works like this (pseudocode, simplified)
#Component(
immediate = true,
property = {
"com.liferay.portlet.footer-portlet-javascript=/js/main.js",
"javax.portlet.display-name=My Portlet",
},
service = Portlet.class
)
In JSR-286 portlets packaged in WARs, you'd use the footer-portlet-javascript value in WEB-INF/liferay-portlet.xml.
Alternatively, use the header-* variant.
I personally prefer this way over memorizing how the path of resources is being constructed.
I use this code before implement arcgis (javascript):
if(typeof define !== "undefined" && typeof require !== "undefined"){
window.__define = window.define;
window.__require = window.require;
window.define = undefined;
window.require = undefined;}
It will be resolved.

dojo 1.8: create widget and startup upon dojo/on

Hi I am looking for tutorial based on Dojo 1.8.
What I am looking for is:- create and instantiate widget pragmatically after dojo page fully loaded and parsed, triggered after dojo/on button. I am not sure of which tutorial in Dojo website, for me to learn.
Please advise.
Thanks in advance.
Clement
There isn't one tutorial that fully answer all your question but the following will be helpful:
Dojo Events tutorial and dojo/on reference
dojo/ready reference
dojo/parser reference
To capture both the full loading of the page and parsing you need to use a combination of dojo/ready and dojo/parser. (I'm assuming that the parsing you refer to is the dojo widget parser, rather than the standard browser parsing of HTML).
To run code after parsing you'll need to add parseOnLoad: false to your dojoConfig and run the parser manually; otherwise, there is no way of capturing when it is complete.
<script type="text/javascript" async="true">
require([
"dojo/ready",
"dojo/parser",
"dojo/on,
"dojo/query"
], function(
ready, parser, on, $
){
ready(function(){
// Only run after the page is fully loaded
parser.parse().then(function(instances){
// Only run after parser has parsed the page
var myButton = $("#myButtonid"); // Find your button
if(myButton.length > 0){ // Check button is found
on(myButton[0], "click", function(evt){
// ... add your code here to create and
// instantiate widget
});
}
});
});
}
</script>
Don't forget that you need to turn off automatic parsing of widgets in you dojoConfig, hence, something like this (in the head):
<script type="text/javascript">
dojoConfig= {
"parseOnLoad": false,
"async": true
// ...other settings
};
</script>