Closed nickcam closed 8 years ago
This is a good idea @nickcam.
Can you share the cod you wrote, even if it's not working yet?
Hi @tomwayson, I've been meaning to post what I found here.
In a nutshell, I got it working using the official .d.ts file....but only when transpiling typescript using 'system' as the module type. The other types would work but would require minor edits to the .d.ts file.
The original loading remains intact, but if you specify an option of maintainModuleNames: true
then it will register modules individually under their full name which will match the typings file module names.
esriSystem.ts
declare var System: any;
declare var require: Function;
module esriSystem {
// return just the last part of a module name
function moduleName(name: string, overrides?: any) {
if (overrides && overrides[name]) {
return overrides[name];
} else {
return name.match(/[^\/]+$/).shift();
}
}
//get the name of the module by stripping out the host from the url
function getName(location: string) {
let startIndex = window.location.href.indexOf("/", 8) + 1;
return location.substring(startIndex, location.length);
}
// takes an array of modules and registers them as a module
// with system.js using the given module name
function _register(mods, names, options: any) {
const opts = options || {};
if (!opts.maintainModuleNames) {
//not maintaining module names so register all esri modules into outModuleName or into 'esri' if not set.
System.register(opts.outModuleName || 'esri', [], function (exp) {
return {
setters: [],
execute: function () {
mods.map(function (mod, idx) {
exp(moduleName(names[idx], opts.moduleNameOverrides), mod);
});
}
};
});
return;
}
//maintaining the module names so loop each module and register individually.
for (var i = 0, len = mods.length; i < len ; i++) {
System.register(names[i], [], function (exp) {
let name = getName(arguments[1].id); //get the module name this callback relates to from arguments[1].id property -> which will be <currentUrl>/<moduleName>.
let index = names.indexOf(name);
let mod = mods[index];
let result = {
setters: [],
execute: () => {
//make the name 'default' here as system compiled modules will automatically create a 'default' property on modules. The ArcGIS .d.ts file contains one export per module so default works.
//NOTE: Will only work if compiling using "module": "system" in tsconfig.
exp("default", mod);
}
};
return result;
});
}
}
// load esri modules and expose via a System.js module
export function register(moduleNames: string[], callback: Function, options) {
// TODO: config should be optional, parse from arguments
// call Dojo's require to load esri modules
require(moduleNames, function (...modules) {
// register a System.js module to wrap the required modules
_register(modules, moduleNames, options);
// call callback (if any)
if (callback) {
callback();
}
});
};
}
Calling using maintainModuleNames.
esriSystem.register([
'esri/Map',
'esri/views/MapView',
'esri/views/SceneView',
'esri/core/urlUtils'
], function () {
// bootstrap the app
System.import('app/main')
.then(null, console.error.bind(console));
}, {
maintainModuleNames: true
});
Then to import just use the default import syntax.
import {Injectable} from '@angular/core';
import Map from 'esri/Map';
import urlUtils from 'esri/core/urlUtils';
@Injectable()
export default class MapService {
map: any;
constructor() {
}
init() {
this.map = new Map({ basemap: 'streets' });
let staticTest = urlUtils.urlToObject("http://www.google.com");
}
}
This keeps Visual Studio happy which appears to be the harder to satisfy IDE as far as typescript compilation errors go.
It works as system modules automatically add a default property to modules, so we can name the export 'default' in the register call and it will resolve when importing. Other module loaders don't do this, although to be honest I only tested commonjs. There's probably a better way to do it that's more consistent across module loaders though.
It would still be nice if the official typings were changed to be more compliant with loaders/IDE's.
Perhaps something like this for each module? I'm not sure how it would impact other users though.
declare module "esri/Map" {
export import Map = __esri.Map;
}
I had a "fun" time upgrading from Angular 2 Beta-17 to RC1 in Visual Studio as well, but it's all running now, so might put together a starter repository for VS 2015 / ArcGIS 4 / Angular 2 when time permits.
Just found that when I deployed the app to somewhere that contains a virtual path the getName function wouldn't work. Here's an updated esriSystem.ts. Now getName just looks for 'esri' or 'dojo' in the location to find out where to start getting the module name from.
declare var System: any;
module esriSystem {
// return just the last part of a module name
function moduleName(name: string, overrides?: any) {
if (overrides && overrides[name]) {
return overrides[name];
} else {
return name.match(/[^\/]+$/).shift();
}
}
//get the module name from the url that system js thinks it's loading it from
function getName(location: string) {
let startIndex = location.lastIndexOf("esri");
if(startIndex === -1) startIndex = location.lastIndexOf("dojo");
return location.substring(startIndex, location.length);
}
function registerToOutModule(mods, names, options: any) {
System.register(options.outModuleName || 'esri', [], function (exp) {
return {
setters: [],
execute: function () {
mods.map(function (mod, idx) {
exp(moduleName(names[idx], options.moduleNameOverrides), mod);
});
}
};
});
}
// takes an array of modules and registers them as a module
// with system.js using the given module name
function _register(mods, names: string[], options: any) {
const opts = options || {};
if (!opts.maintainModuleNames) {
//not maintaining module names so register all esri modules into outModuleName or into 'esri' if not set.
registerToOutModule(mods, names, options);
return;
}
//maintaining the module names so loop each module and register individually.
for (var i = 0, len = mods.length; i < len; i++) {
System.register(names[i], [], function (exp, idObj) {
let name = getName(idObj.id);
let index = names.indexOf(name);
let mod = mods[index];
let result = {
setters: [],
execute: () => {
//make the name 'default' here as system compiled modules will automatically create a 'default' property on modules. The ArcGIS .d.ts file contains one export per module so default works.
//NOTE: Will only work if compiling using "module": "system" in tsconfig.
exp("default", mod);
}
};
return result;
});
}
}
// load esri modules and expose via a System.js module
export function register(moduleNames: string[], callback: Function, options) {
// TODO: config should be optional, parse from arguments
// call Dojo's require to load esri modules
require(moduleNames, function (...modules) {
// register a System.js module to wrap the required modules
_register(modules, moduleNames, options);
// call callback (if any)
if (callback) {
callback();
}
});
};
}
@nickcam thanks for keeping plugging away at this. I still haven't had time to try this out on my own (just been very busy w/ UC around the corner), but this looks solid.
Would you be interested in making a pull request with these changes?
cc @jwasilgeo
No worries...am using it in a new project so just getting sorted out up front. Keen for other module types besides 'system' to work as well though, pretty sure I'll need it to soon as we start integrating ArcGIS & Angular2 with (possibly many) other libraries.
Bit pushed for time as well - unfortunately not because of UC :) - but want to try and take another look next week at getting all or at least more module types to load. Will raise a PR after I get a chance to check it out again.
@nickcam awesome. Just wanted to hop on here and also say thanks and definitely encourage you to make a PR when you have the chance.
Resolved in #10
Hi Guys,
Out of the box the loader won't work with the official typings for v4. https://github.com/Esri/jsapi-resources/blob/master/4.x/typescript/arcgis-js-api.d.ts
It's not surprising as the typings files declares everything as a separate module (as it should for amd I'm thinking), and the loader registers everything under the one module name in systemjs,
snippet from arcgis-js-api.d.ts
A "fairly" easy workaround is to add a new module to the typings file named the same as the output module name in the loader - for example "esri-mods" - that contains every interface as an export in it. for example:
This way import statements are the same as they currently are as well.
But I was thinking that instead of having to modify the official typings, I just could modify esriSystem.ts. Since the names of the modules added to the dojo loader call exactly match the typings file - as they should, I tried to create a System.register call for every esri module included using the modules name just by looping the returned modules in the require callback and calling System.register on each one.
Couldn't get it working after a couple of hours trying though and wasn't sure why. So instead of possibly burning days trying to get it going, thought I'd check if you had any ideas of how to handle this?
Thanks!