Hi,
I'm trying to add a custom TileLayer to the map using the new ES modules in 4.18 but am unsure how to implement the createSubclass method on BaseTileLayer. I can see that is a dojo method so was unsure how to perform the equivalent in Typescript/Angular.
Any help would be greatly appreciated.
Here's an AMD example: https://developers.arcgis.com/javascript/latest/sample-code/sandbox/index.html?sample=layers-custom-.... There aren't any dojo methods in the 4.18 API. The way the modules functionality is implemented in ESM (POJO or TypeScript) is exactly the same as AMD, you just need to change the path used by the import statements: https://developers.arcgis.com/javascript/latest/es-modules/#migrate-to-es-modules.
Hi Andy,
I believe I have imported correctly
I can repro, that's a TypeScript definition problem, I'll open an issue. You can temporarily bypass the issue and continue your builds by using //@ts-ignore:
//@ts-ignore
var ImageLayer = BaseTileLayer.createSubclass({
I also see that the API reference documentation needs to be updated. We're looking into that, as well. Thanks for reporting.
Hi Andy, many thanks, that workaround works for now and my tile layer is now available in the map. I did find a similar issue in an older version here: https://community.esri.com/t5/arcgis-api-for-javascript/customelevationlayer-with-typescript/m-p/380...
@AndyGup has this still not been fixed? I'm running into this on 4.27.
@bearingwestlooks like I never clarified my answer from above. If you are using TypeScript with '@arcgis/core', then the error "Property 'createSubclass' does not exist on type 'typeof SomeXYZLayer'" is correct when attempting to use the createSubclass() method. The createSubclass() method only works in JavaScript. We updated the documentation about subclassing in TypeScript here: https://developers.arcgis.com/javascript/latest/implementing-accessor/#create-a-simple-subclass.
The correct pattern in TypeScript would look like the following. You might also need to set 'useDefineForClassFields:false' in your tsconfig, depending on your project configuration.
import { subclass } from "@arcgis/core/core/accessorSupport/decorators";
import BaseTileLayer from "@arcgis/core/layers/BaseTileLayer.js";
@subclass("custom.MyTileLayer")
export class MyTileLayer extends BaseTileLayer {
// TODO
}
Thanks, looks promising. However, when I do this, I get:
init.js:149 [esri.Map] #add() The item being added is not a Layer or a Promise that resolves to a Layer.
Whereas when I used ts-ignore and followed a js-ish pattern (as in https://community.esri.com/t5/arcgis-javascript-maps-sdk-questions/custom-layer-is-not-being-added-f... - which, notably, is not working completely) I can at least add the layer without error.
My source, following your pattern, follows. I suspect the namespacing might be a problem (custom...) but I've tried all permutations that make sense (classname by itself, fully qualified class name from src up, and everything in between) to no avail. What am I doing wrong here? (I'm also fine with the ts-ignore hack linked above, assuming I can get this working.)
(I don't suppose a dojo-less typescript version is on the cards, is it? The DX of the current version is quite difficult.)
@subclass("custom.MyCustomLayer")
export class MyCustomLayer extends BaseTileLayer {
constructor(properties: BaseTileLayerProperties,
private readonly myArg1: SomeClass,
private readonly myArg2: SomeClass,
private readonly myArg3: SomeClass) {
super(properties);
}
getTileUrl(level: number, row: number, col: number): string {
console.log("Getting tile URL", level, row, col);
return `https://proxy.com/z=${level}&x=${col}&y=${row}`;
}
fetchTile(level: number, row: number, col: number): Promise<HTMLImageElement | HTMLCanvasElement> {
console.log("Fetching tile", level, row, col);
return new Promise<HTMLImageElement>((resolve, reject) => {
Please provide a minimal repro sample, there's more info on that here: https://developers.arcgis.com/javascript/latest/troubleshooting/#minimal-reproducible-application. One suggestion is to first build the custom layer in JavaScript, and then migrate your code to TypeScript.
Did you try adding a load() method. There are several samples to use for guidance: https://developers.arcgis.com/javascript/latest/sample-code/layers-custom-tilelayer/ , https://developers.arcgis.com/javascript/latest/sample-code/layers-custom-blendlayer/ and https://developers.arcgis.com/javascript/latest/sample-code/layers-custom-elevation-exaggerated/.
> I don't suppose a dojo-less typescript version is on the cards, is it?
There hasn't been any dojo in the 4.x API in quite a while, and it was removed from the CDN in version 4.25.
I can't provider a minimal repro sample. Like I said, this is an extension that sits inside another application.
> One suggestion is to first build the custom layer in JavaScript, and then migrate your code to TypeScript.
Well that speaks exactly to my question, doesn't it? I can't subclass - I have to use @subclass, which is a hack (and which doesn't work). I can't just import, I need to use loadModules, which is a hack. If it's not dojo, fine - but when will you produce a clean SDK that follows web standards, with regular types without all of these hacks throughout your system? There's no need for all of this "cleverness".
Here's another delightful example; this previously working code results in:
Accessor#set Assigning an instance of 'esri.geometry.SpatialReference' which is not a subclass of 'esri.geometry.SpatialReference'
addLine1(coords: CRTM05Coordinates[][], color: string, width: number): number {
const line = new Polyline({
paths: coords.map(ring => ring.map(point => [point.east, point.north])),
spatialReference: this.w.view.spatialReference
});
return this.addLine2(line, color, width);
}
(This is, btw, "fixed" by ` spatialReference: { wkid: this.w.view.spatialReference.wkid }`).
Yet another example:
this.graphicsLayer = new GraphicsLayer({
title: "My graphics layer"
});
// Add the graphics layer to the map
this.w.map.add(this.graphicsLayer);
Results in:
#add() The item being added is not a Layer or a Promise that resolves to a Layer.
This is fixed by:
loadModules(['esri/layers/GraphicsLayer']).then(([GraphicsLayerClass]) => {
this.graphicsLayer = new (GraphicsLayerClass as typeof GraphicsLayer)({
title: "My graphics"
});
// Add the graphics layer to the map
this.w.map.add(this.graphicsLayer);
});
...despite the underlying site already having initialized via:
require([
...
'esri/layers/GraphicsLayer',