Having Issues using Esri ArcGIS API for JavaScript in Lightning Web Component (require is not defined at eval) - requirejs

I'm trying to implement Esri ArcGIS JS in Lightning Web Component. While using ArcGIS JS, the sample code uses require function to load modules. In order to do that I'm trying to use require.js. I downloaded it from here (Require.js). And then uploaded to my sandbox as static resource. I'm trying to use that static resource in my Lightning Web Component. I also added the script tag for the ArcGIS Javascript API in my Experience Cloud site's header as
<script src="https://js.arcgis.com/4.24"></script>
Lightning Web Component:
import { LightningElement, track } from 'lwc';
import { loadScript } from 'lightning/platformResourceLoader';
import requireJS from '#salesforce/resourceUrl/requireJS';
export default class TestMap extends LightningElement {
renderedCallback() {
loadScript(this, requireJS).then(() => {
console.log('requireJS loaded');
require([
"esri/geometry/Extent"
], (
Extent
) => {
var initExtent = new Extent({
xmin: -15884312,
ymin: 1634835,
xmax: -6278767,
ymax: 7505198,
spatialReference: 102100
});
});
}).catch(exception => {
console.log(exception);
});
}
}
My problem right now, eventhough I can see in the Network tab that the require.js is loaded from static resource, require function cannot be found.
Exception message catched
I'm not sure where is the issue since this is how I loaded my all javascript files before.
I was expecting to see the the require function is working after the require.js script loaded from Static Resource.

This one is a bit tricky, I will try to guide you as much as I can.
First, don't put the script tag in your website header. This is a last chance solution, we'll keep it if nothing else work.
Second, requireJS is not compatible with LWC (or Locker Service to be precise). So you can forget it. loadScript is in someways similar.
Now the solution, usually I download the whole from a CDN and host it as a static resource. Then you can load it via loadScript and use it as per the documentation.
In case the library is really small, it could be created as a LWC and then be imported but usually libraries are too heavy regarding Salesforce limit.
Looking at the library, it seems that they do not provide any compiled full version (which is probably huge). In this case I would recommend to make a custom build locally containing only the necessary pieces of code and then uploading as a static resource. Unfortunately I can't help on this part as I still didn't do it myself yet.
Feel free to comment and I will improve my answer is it's unclear.

Related

How to use an angular module that does not support Universal with an angular/cli app using SSR

Hey everyone I have been building an Angular app that is using Universal with SSR for a while not and every so often I would include a module that would cause the server to fail silently and never knew why, last night I figured out it was because the module I tried to include (ngx-editor) does not support Universal.
Is there a way for me to include a module such as ngx-editor that does not support Universal in my application? Or do I have to find one that supports Universal?
Thanks a lot in advance.
You could try not calling this module's components depending on the platform, e.g. by checking the platform dynamically in your code ( https://angular.io/api/common/isPlatformBrowser)
import {isPlatformBrowser} from "#angular/common";
//...
constructor(#Inject(PLATFORM_ID) private platformId: Object)
{
if(isPlatformBrowser(this.platformId))
{
//call module's methods/components...
}
else { /*server side*/ }
}
You may also need to modify your server module (app.server.module.ts) to not include modules that do not support angular-universal

Route static page with vue-router

I'm fairly new to web development and I was wondering if there was a way to route a static web page with its own stylesheets and javascripts, using vue-router.
Let's say I have a directory called staticWebPage that contains:
an index.html file
a javascripts directory containing .js files
and a stylesheets directory containing .css files
Now, I'd like to map /mystaticwebpage to this index.html file so it displays that particular static web page.
I'd like to do something like this:
import VueRouter from 'vue-router'
import AComponent from './components/AComponent.vue'
import MyHtmlFile from './references/index.html'
router.map({
'/acomponent': {
component: AComponent
},
'mystaticwebpage': {
component: MyHtmlFile
}
})
Of course, this doesn't work as I can only reference Vue components in router.map.
Is there a way to route to that ./staticWebPage/index.html file using all the .js and .css file contained in the /staticWebPage directory?
So for your case you can do something that uses Webpack’s code-splitting feature.
More precisely, what you want is probably async components. So the code (and the css) used in the component definition (including any script you included there) will be loaded only when the corresponding page is accessed.
In large applications, we may need to divide the app into smaller
chunks and only load a component from the server when it’s actually
needed. To make that easier, Vue allows you to define your component
as a factory function that asynchronously resolves your component
definition. Vue will only trigger the factory function when the
component actually needs to be rendered and will cache the result for
future re-renders.
It can be a bit challenging to setup, so please refer to the dedicated guide in the VueJS doc.

How should I use Swagger with Hapi?

I have a working ordinary Hapi application that I'm planning to migrate to Swagger. I installed swagger-node using the official instructions, and chose Hapi when executing 'swagger project create'. However, I'm now confused because there seem to be several libraries for integrating swagger-node and hapi:
hapi-swagger: the most popular one
hapi-swaggered: somewhat popular
swagger-hapi: unpopular and not that active but used by the official Swagger Node.js library (i.e. swagger-node) as default for Hapi projects
I though swagger-hapi was the "official" approach, until I tried to find information on how do various configurations on Hapi routes (e.g. authorization, scoping, etc.). It seems also that the approaches are fundamentally different, swagger-hapi taking Swagger definition as input and generating the routes automatically, whereas hapi-swagger and hapi-swaggered seem to have similar approach with each other by only generating Swagger API documentation from plain old Hapi route definitions.
Considering the amount of contributors and the number of downloads, hapi-swagger seems to be the way to go, but I'm unsure on how to proceed. Is there an "official" Swagger way to set up Hapi, and if there is, how do I set up authentication (preferably by using hapi-auth-jwt2, or other similar JWT solution) and authorization?
EDIT: I also found swaggerize-hapi, which seems to be maintained by PayPal's open source kraken.js team, which indicates that it might have some kind of corporate backing (always a good thing). swaggerize-hapi seems to be very similar to hapi-swagger, although the latter seems to provide more out-of-the-box functionality (mainly Swagger Editor).
Edit: Point 3. from your question and understanding what swagger-hapi actually does is very important. It does not directly serves the swagger-ui html. It is not intended to, but it is enabling the whole swagger idea (which the other projects in points 1. and 2. are actually a bit reversing). Please see below.
It turns out that when you are using swagger-node and swagger-hapi you do not need all the rest of the packages you mentioned, except for using swagger-ui directly (which is used by all the others anyways - they are wrapping it in their dependencies)
I want to share my understanding so far in this hapi/swagger puzzle, hope that these 8 hours that I spent can help others as well.
Libraries like hapi-swaggered, hapi-swaggered-ui, also hapi-swagger - All of them follow the same approach - that might be described like that:
You document your API while you are defining your routes
They are somewhat sitting aside from the main idea of swagger-node and the boilerplate hello_world project created with swagger-cli, which you mentioned that you use.
While swagger-node and swagger-hapi (NOTE that its different from hapi-swagger) are saying:
You define all your API documentation and routes **in a single centralized place - swagger.yaml**
and then you just focus on writing controller logic. The boilerplate project provided with swagger-cli is already exposing this centralized place swagger.yaml as json thru the /swagger endpoint.
Now, because the swagger-ui project which all the above packages are making use of for showing the UI, is just a bunch of static html - in order to use it, you have two options:
1) to self host this static html from within your app
2) to host it on a separate web app or even load the index.html directly from file system.
In both cases you just need to feed the swagger-ui with your swagger json - which as said above is already exposed by the /swagger endpoint.
The only caveat if you chose option 2) is that you need to enable cors for that end point which happened to be very easy. Just change your default.yaml, to also make use of the cors bagpipe. Please see this thread for how to do this.
As #Kitanotori said above, I also don't see the point of documenting the code programmatically. The idea of just describing everything in one place and making both the code and the documentation engine to understand it, is great.
We have used Inert, Vision, hapi-swagger.
server.ts
import * as Inert from '#hapi/inert';
import * as Vision from '#hapi/vision';
import Swagger from './plugins/swagger';
...
...
// hapi server setup
...
const plugins: any[] = [Inert, Vision, Swagger];
await server.register(plugins);
...
// other setup
./plugins/swagger
import * as HapiSwagger from 'hapi-swagger';
import * as Package from '../../package.json';
const swaggerOptions: HapiSwagger.RegisterOptions = {
info: {
title: 'Some title',
version: Package.version
}
};
export default {
plugin: HapiSwagger,
options: swaggerOptions
};
We are using Inert, Vision and hapi-swagger to build and host swagger documentation.
We load those plugins in exactly this order, do not configure Inert or Vision and only set basic properties like title in the hapi-swagger config.

Load Google Maps + MarkerWithLabel together?

Is anyone else using Google Maps + MarkerWithLabel + RequireJS? I basically want to see if it's possible to eliminate the extra http request for MarkerWithLabel and somehow optimize how everything is loaded and initialized.
Right now I'm loading the Google Maps library dynamically from within a Require module (I need this for dynamic params), but I can only get MarkerWithLabel working after Google Maps has been loaded. Ideally it would be great to either fetch them together or at the very least preload MarkerWithLabel and keep the Google Maps handle (URL) dynamic.
I'm considering 'Delayed Module Evaluation' if there's no way to consolidate Maps + MarkerWithLabel. I'm curious how others are approaching this.
http://blog.millermedeiros.com/requirejs-2-0-delayed-module-evaluation-and-google-maps/
You can use requirejs async plugin. Basically you should end-up with something like :
require.config({
paths: {
/* Bower Libraries */
async: '../bower_components/requirejs-plugins/src/async',
markerlabel: '../bower_components/gmaps-makerwithlabel/index',
...
and after that, you can shim it to make sure google maps loads first:
markerlabel: {
deps: ['async!https://maps.googleapis.com/maps/api/js&sensor=false']
}
Take a look to this: https://github.com/lupugabriel1/gmaps-markerwithlabel-amd
Based on Google Maps MarkerWithLabel 1.1.9. This is basically a fork that adds src/markerwithlabel-amd.js.
This extension offers AMD support for Google Maps MarkerWithLabel utility library of Google Maps Javascript API v3. Also fixes the problem with requirejs optimizer.

How to modularize AngularJS applications / plugins

I have a couple of (software-)architecture questions regarding a migration from Grails (REST-API, parts of AngularJS, MongoDB, Tomcat, Spock, several plugins) to Node.js + Angular.js.
I probably have to explain the structure of the Grails project fist, so here we go:
There is a main Grails application (beside a couple of other applications), which is built on several plugins. Each of these plugins is able to get executed by itself - that means it has its own UI, individual templates, services, controllers, routes, tests etc. It is also hosted on different repositories.
This is done by the Grails plugin mechanisms. The benefits are less testing-efforts, less compiling time, modularization, single responsibilities and so on.
But still, the time to compile and test are too expensive. Also I don't like the fact that the API delivers parts of the templates/views. I would like to have the backend APIs "just to be backend APIs" and the frontend "just to be the frontend".
So each AngularJS application/plugin will provide its own view, routes, service etc. But they might also depend on other plugins.
So what I would like to achieve is as follow:
One main AngularJS application, which includes several plugins (a plugin can be something like a report-generator, a guestbook or whatsoever, speaking of a single independent part of an application, either with a specific route, or just a small part of the page).
Each plugin must be a stand-alone AngularJS application (probably executable during development via grunt or so). So that the UI developer does not need to start the whole backend application, further that we may run functional tests only with JavaScript
Communication only via REST, The frontend must retrieve all it's data from the APIs
Each plugin must be testable on its own
A Plugin might require other plugins to work
The main index.html (and app.js?) might be provided by a Nginx server, which is decoupled from the rest of the backend (API)
Though I have a specific picture in my head, I am struggling in how to setup this architecture.
In Grails the plugin mechanisms are somehow merging the plugin dependant settings (like URL mappings, dependencies, etc) to the main application in which they get included/injected - this is what I want to achieve with AngularJS as well. So:
Are there some kind of same mechanisms for AngularJS?
How may I provide/merge the routes of each plugin into the main application?
How can I declare application- and plugin-dependencies?
What tools might be usefull for the build process?
How to establish lazy-retrievments of the plugin-resources (css/less files, views, services etc)?
Prevent the application to provide all resources of the plugins on startup (I guess the routes are required on startup though)
Since this is not just a how-to-do-this-or-that question I excuse myself if I am missing important parts or if some parts are not clear enough. Just ask me and I will answer each question in depths.
** This answer is incomplete **
I want to make sure I understand you before I dig into this.
Here's a quick implementation of a Loader module to manage lazy loading (plugins, vendor assets, admin stuff etc).
Does this help?
angular.module('Bizcoin.loader')
.service('Loader', Loader);
function Loader($injector, $ocLazyLoad, User) {
var Loader = {
load: load,
get: get,
plugins: {},
flags: {}
};
init();
return Loader;
function init() {
load('vendors');
if (userIsAdmin)
load('admin');
}
function load(plugin) {
Loader.plugins[plugin] = Loader[plugin] || $ocLazyLoad.load('path/to/'+plugin+'.js');
return Loader.plugins[plugin].then(setFlag);
function setFlag() {
return Loader.flags[plugin] = true;
}
}
function get(plugin) {
return load(plugin).then(function() {
return $injector.get(plugin);
});
}
}
I work on an large .Net/AngularJS application that is composed of 20+, independent, Areas (or modules) and some core functionality common and reused across all Areas.
Let me go into detail on how I do this for my particular case, and it might give some ideas. The fact that I use .Net is irrelevant here, since this can achieve with any framework.
Each Area acts as an independent application that depends only on the core functionality, always present. Each Area has its own ASP.Net MVC route.
Each Area registers with the core application the menu links it wants to provide.
When the customer goes to the application dashboard, only the core part of of the application. When the user clicks on link in the menu, it will navigate to the content provided by one of the Areas, and only the core plus the assets of that Area are loaded.
Lets see how this is done.
In the main page of the application I load the scripts like this:
<script type="text/javascript">
// a JS object with all the necessary app data from the server.
// e.g.: menu data, etc
window.appContext = #Html.Action("ApplicationContext", "Portal"));
</script>
#Scripts.Render("~/bundles/angular-main")
#RenderSection("AngularAreas", required: false)
I make use of dot .Net bundles and sections.
The main (core) AngularJS part of the application consists of angular configuration, internationalization services, global notifications service, reusable UI components, etc. This is loaded is #Scripts.Render("~/bundles/angular-main").
The #RenderSection("AngularAreas", required: false) section will be filled in by each area when the user navigates to that Area.
Lets see some AngularJS code.
Here is part of the main app.ts.
// If user is visiting an Area, the NgModules array will be augmented.
// with the modules the Area wants to provide (to be bootstrapped)
export var LoadNgModules = [
NgModules.Config,
NgModules.Core
];
angular.module(NgModules.Bootstraper, LoadNgModules);
angular.element(document).ready(function () {
angular.bootstrap(document, [NgModules.Bootstraper]);
});
Lets look at an example Area now.
And here is how an Area would supply its assets, to be outputted in #RenderSection("AngularAreas", required: false):
#section AngularAreas {
#Scripts.Render("~/bundles/areas/staff-management")
}
Its a simple bundle containing all the scripts for that Area.
Now, let's see the important part of the AngularJS code for this Area.
var StaffManagementNgModule = 'areas.staff-management';
// Push our self into the modules to be bootstrapped
LoadNgModules.push(StaffManagementNgModule );
// Define the module
angular
.module(StaffManagementNgModule , ['ngRoute', NgModules.Core])
.config([
'$routeProvider', '$locationProvider', ($routeProvider: ng.route.IRouteProvider, $locationProvider) => {
$routeProvider
.when(staff', { template: '<staff></staff>' })
.when('staff/details/:id', { template: '<staff-details></staff-details>' });
}
]);;
That is all, from here the Area is a normal Angular application.
To summarize, we load the main (core) AngularJS functionality and provide the LoadNgModules array that an area can fill with its own modules.
we load the Area scripts and and "our self" to the LoadNgModules array.
finally the run angular.bootstrap.
For completeness, here is a fragment of C# showing how an area would indicate to the main application that it is available
public class ItemManagementModuleRegistration : IModuleRegistration
{
public void Register(ModuleContext moduleContext)
{
string thisAreaName = "Staff";
moduleContext.RegisterMenu(menuContext =>
{
var ItemsMenu = menuContext.Items(thisAreaName);
// add urls and stuff...
});
// register more stuff with the moduleContext
}
}
Using reflection one can easily find what areas are "installed".
These are the main moving parts of the setup.
Each Area can have its own API and tests. It is quite flexible.

Resources