Add setStragy to VectorSource - constructor

I use openlayers js library version 6.15.1
I have a class that inherits VectorSource. I don't know what to do in my constructor cause I would like my own stategy. I can't call super({ strategy: this._myStrategy, ... }) so how to do it?
Can I add the function setStrategy in VectorSource prototype? Or is there a better solution?

You could wrap the this for the custom strategy function inside another function which is valid in super
class CustomVectorSource extends VectorSource {
constructor(options) {
options = options || {};
const strategy = options.strategy;
options.strategy = function (extent, resolution) {
if (typeof this.customStrategy_ !== 'function') {
return [];
}
return this.customStrategy_(extent, resolution);
};
super(options);
this.customStrategy_ = strategy;
}
setStrategy(strategy) {
this.customStrategy_ = strategy;
}
}
https://codesandbox.io/s/vector-wfs-forked-08vw80?file=/main.js

Related

What is the minimal implementation for custom elements example mentioned in the specifications?

https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-autonomous-example:htmlelement
In the specification they've provided an example for Creating an autonomous custom element. However, they've left _updateRendering() method implementation for the readers.
class FlagIcon extends HTMLElement {
constructor() {
super();
this._countryCode = null;
}
static observedAttributes = ["country"];
attributeChangedCallback(name, oldValue, newValue) {
// name will always be "country" due to observedAttributes
this._countryCode = newValue;
this._updateRendering();
}
connectedCallback() {
this._updateRendering();
}
get country() {
return this._countryCode;
}
set country(v) {
this.setAttribute("country", v);
}
_updateRendering() {
// Left as an exercise for the reader. But, you'll probably want to
// check this.ownerDocument.defaultView to see if we've been
// inserted into a document with a browsing context, and avoid
// doing any work if not.
}
}
An issue has been raised to provide the remaining implementation for better understanding of the topic and quickly move on.
Issue: https://github.com/whatwg/html/issues/3029
What code can we put there to get the required functionality?
Your solution fails, once the flag is set you can never change the value.
That "exercise" is old.. very old.. and contrived to show everything Custom Elements can do.
And it is plain wrong.. key is WHEN attributeChanged runs, and what the old/new values are
And attributeChangedCallback runs BEFORE connectedCallback; that is why https://developer.mozilla.org/en-US/docs/Web/API/Node/isConnected was added.
Your code gets 3 parameters in attributeChangedCallback, but you can't do anything with them, because execution always goes to the _updateRendering method.
If the point of the exercise is to learn when Observed attributes change I would use:
Code also available in JSFiddle: https://jsfiddle.net/dannye/43ud1wvn/
<script>
class FlagIcon extends HTMLElement {
static observedAttributes = ["country"];
log(...args) {
document.body.appendChild(document.createElement("div"))
.innerHTML = `${this.id} - ${args.join` `}`;
}
attributeChangedCallback(name, oldValue, newValue) {
this.log("<b>attributeChangedCallback:</b>", `("${name}" , "${oldValue}", "${newValue}" )`);
if (this.isConnected) {
if (newValue == oldValue) this.log(`Don't call SETTER ${name} again!`);
else this[name] = newValue; // call SETTER
} else this.log("is not a DOM element yet!!!");
}
connectedCallback() {
this.log("<b>connectedCallback</b>, this.img:", this.img || "not defined");
this.img = document.createElement("img");
this.append(this.img); // append isn't available in IE11
this.country = this.getAttribute("country") || "EmptyCountry";
}
get country() { // the Attribute is the truth, no need for private variables
return this.getAttribute("country");
}
set country(v) {
this.log("SETTER country:", v);
// Properties and Attributes are in sync,
// but setAttribute will trigger attributeChanged one more time!
this.setAttribute("country", v);
if (this.img) this.img.src = `//flagcdn.com/20x15/${v}.png`;
else this.log("can't set country", v);
}
}
customElements.define("flag-icon", FlagIcon);
document.body.onclick = () => {
flag1.country = "nl";
flag2.setAttribute("country", "nl");
}
</script>
<flag-icon id="flag1" country="in"></flag-icon><br>
<flag-icon id="flag2" country="us"></flag-icon><br>
This is just one way, it all depends on what/when/how your Custom Elements needs to do updates.
It also matters WHEN the CustomElement is defined; before or after the DOM is parsed. Most developers just whack deferred or method on their scripts, without understanding what it implies.
Always test your Web Component with code that defines the Custom Element BEFORE it is used in the DOM.
A Real World <flag-icon> Web Component
Would be optimized:
<script>
customElements.define("flag-icon", class extends HTMLElement {
static observedAttributes = ["country"];
attributeChangedCallback() {
this.isConnected && this.connectedCallback();
}
connectedCallback() {
this.img = this.img || this.appendChild(document.createElement("img"));
this.img.src = `//flagcdn.com/120x90/${this.country}.png`;
}
get country() {
return this.getAttribute("country") || console.error("Missing country attribute",this);
}
set country(v) {
this.setAttribute("country", v);
}
});
</script>
<flag-icon id="flag1" country="gb"></flag-icon>
<flag-icon id="flag2" country="eu"></flag-icon>
Or NO External Images at all
Using the FlagMeister Web Component which creates all SVG client-side
<script src="//flagmeister.github.io/elements.flagmeister.min.js"></script>
<div style="display:grid;grid-template-columns: repeat(3,1fr);gap:1em">
<flag-jollyroger></flag-jollyroger>
<flag-un></flag-un>
<flag-lgbt></flag-lgbt>
</div>
Here is the complete code to achieve the same requirements:
<!DOCTYPE html>
<html lang="en">
<body>
<flag-icon country="in"></flag-icon><br>
<flag-icon country="nl"></flag-icon><br>
<flag-icon country="us"></flag-icon><br>
</body>
<script>
class FlagIcon extends HTMLElement {
constructor() {
super();
this._countryCode = null;
}
static observedAttributes = ["country"];
attributeChangedCallback(name, oldValue, newValue) {
// name will always be "country" due to observedAttributes
this._countryCode = newValue;
this._updateRendering();
}
connectedCallback() {
this._updateRendering();
}
get country() {
return this._countryCode;
}
set country(v) {
this.setAttribute("country", v);
}
_updateRendering() {
//**remaining code**
if (this.ownerDocument.defaultView && !this.hasChildNodes()) {
var flag = document.createElement("img");
flag.src = "https://flagcdn.com/24x18/" + this._countryCode + ".png";
this.appendChild(flag);
}
}
}
customElements.define("flag-icon", FlagIcon);
</script>
</html>
Note: images may take time to load depending on the internet speed.
Let me know if I've missed anything.

Viewer after ver. 7.37 -- use setAggregatedProperties

I am using a custom property panel.
There is any sample of how to replace older "setProperties" with new "setAggregatedProperties" in the viewer?
Thanks in advance
Yes, this sample is using it: https://forge-extensions.autodesk.io/?extension=CustomPropertiesExtension
Source code: https://github.com/Autodesk-Forge/forge-extensions/blob/master/public/extensions/CustomPropertiesExtension/contents/main.js
class CustomPropertyPanel extends Autodesk.Viewing.Extensions.ViewerPropertyPanel {
constructor (viewer, options) {
super(viewer, options);
this.properties = options.properties || {};
}
setAggregatedProperties(propertySet) {
Autodesk.Viewing.Extensions.ViewerPropertyPanel.prototype.setAggregatedProperties.call(this, propertySet);
// add your custom properties here
const dbids = propertySet.getDbIds();
dbids.forEach(id => {
var propsForObject = this.properties[id.toString()];
if (propsForObject) {
for (const groupName in propsForObject) {
const group = propsForObject[groupName];
for (const propName in group) {
const prop = group[propName];
this.addProperty(propName, prop, groupName);
}
}
}
});
}
};

Why does the ToolController's getPriority return 0 for my tool?

According to a prior SO answer, you can implement getPriority for a forge viewer Tool. And according to another SO answer extending the ToolInterface does not work. Hence, me not extending the ToolInterface implementing my Tool like so:
class MyCustomExtension extends Autodesk.Viewing.Extension {
constructor(viewer, options) {
super(viewer, options);
this.theiaUtil = new TheiaUtil(this);
}
getPriority() {
console.log("Theia#getPriority called! ", (this.getPriority && this.getPriority() || 0));
return 100000;
}
...
}
My tool's priority is returned as 0 in the ToolController, although it shouldn't:
function getPriority(tool)
{
return tool.getPriority instanceof Function && tool.getPriority() || 0;
}
I don't know why this function returns 0 as tool.getPriority instanceof Function returns true if I call MyCustomExtension.getPriority myself.
Note that ToolInterface is implemented like so:
function ToolInterface()
{
this.names = [ "unnamed" ];
this.getNames = function() { return this.names; };
this.getName = function() { return this.names[0]; };
this.getPriority = function() { return 0; };
this.register = function() {};
this.deregister = function() {};
this.activate = function(name, viewerApi) {};
this.deactivate = function(name) {};
this.update = function(highResTimestamp) { return false; };
this.handleSingleClick = function( event, button ) { return false; };
this.handleDoubleClick = function( event, button ) { return false; };
this.handleSingleTap = function( event ) { return false; };
this.handleDoubleTap = function( event ) { return false; };
// ...
}
Because of that, simply extending the ToolInterface class won't work because all these properties and functions added to the instance in the constructor will take precedence over your actual class methods. This is also likely the reason why you're seeing the priority value returned as zero - when you call myTool.getPriority(), you are not actually calling your getPriority method, but rather the default function which was assigned to this.getPriority in ToolInterface's constructor.
To work around this issue I would recommend explicitly deleting the corresponding fields in your class' constructor (something I explain in my blog post on implementing custom Forge Viewer tools):
class DrawTool extends Autodesk.Viewing.ToolInterface {
constructor() {
super();
this.names = ['box-drawing-tool', 'sphere-drawing-tool'];
// Hack: delete functions defined *on the instance* of the tool.
// We want the tool controller to call our class methods instead.
delete this.register;
delete this.deregister;
delete this.activate;
delete this.deactivate;
delete this.getPriority;
delete this.handleMouseMove;
delete this.handleButtonDown;
delete this.handleButtonUp;
delete this.handleSingleClick;
}
register() {
console.log('DrawTool registered.');
}
deregister() {
console.log('DrawTool unregistered.');
}
activate(name, viewer) {
console.log('DrawTool activated.');
}
deactivate(name) {
console.log('DrawTool deactivated.');
}
getPriority() {
return 42; // Or feel free to use any number higher than 0 (which is the priority of all the default viewer tools)
}
// ...
}
TL;DR: Activate the tool in button click event from a toolbar button instead of the extension's load method.
class MyExtension extends Autodesk.Viewing.Extension {
...
onToolbarCreated(toolbar) {
const MyToolName = 'My.Tool.Name'
let button = new Autodesk.Viewing.UI.Button('my-tool-button');
button.onClick = (e) => {
const controller = this.viewer.toolController;
if (controller.isToolActivated(MyToolName)) {
controller.deactivateTool(MyToolName);
button.setState(Autodesk.Viewing.UI.Button.State.INACTIVE);
} else {
controller.activateTool(MyToolName);
button.setState(Autodesk.Viewing.UI.Button.State.ACTIVE);
}
};
}
...
}
I activated the tool instantly after registering it in the Extension's load method. Petr Broz's github repo from his blog post loads the tool from a button in the toolbar. So I moved the activation of the tool to a button click in the toolbar which worked for me.

ES6 Classes implement indexer like arrays

I want to implement indexer to get elements from data property with index as JavaScript arrays. I heard about ES6 proxies but I couldn't implement it to my class. Is it possible now or should I wait more to come with ES7.
class Polygon {
constructor() {
this.data = new Set(arguments)
}
[Symbol.iterator](){
return this.data[Symbol.iterator]()
}
add(vertex){
this.data.add(vertex)
}
remove(vertex){
this.data.delete(vertex)
}
get perimeter(){
}
get area(){
}
}
let poly = new Polygon()
let first_vertex = poly[0]
AFAIK there is no proposal for something like "indexing" into arbitrary objects, so yes, you would have to go with proxies.
I couldn't really test this since no environment seems to support both classes and proxies, but in theory, you'd have to return the new proxied object from the constructor. Tested in Chrome v52.
Example:
class Test {
constructor(data) {
let self = this;
this.data = data;
this.foo = 'bar';
return new Proxy(this, {
get(target, prop) {
if (Number(prop) == prop && !(prop in target)) {
return self.data[prop];
}
return target[prop];
}
});
}
}
var test = new Test([1,2,3]);
console.log(test[0]); // should log 1
console.log(test.foo); // should log 'bar'

TypeScript variable that is a typed function

I want to have a variable in a TypeScript class that is of the type "boolean isVisible()".
How do I declare it?
How do I assign this function for another instantiated object to this variable?
How do I call this function?
ps - This seems so basic but 10 minutes of searching and I couldn't find it.
function boolfn() { return true; }
function strfn() { return 'hello world'; }
var x: () => boolean;
x = strfn; // Not OK
x = boolfn; // OK
var y = x(); // y: boolean
Here's one way of doing it, though I'll be happy to work with you to figure out exactly what you're trying to achieve.
export module Sayings {
export class Greeter {
isVisible(): boolean {
return true;
}
}
}
var greeter = new Sayings.Greeter();
var visible = greeter.isVisible();
You could also use a property instead of a function. Your original question talks about a "variable" and a "function" as if they're the same thing, but that's not necessarily the case.
export module Sayings {
export class Greeter {
isVisible: boolean = false;
}
}
var greeter = new Sayings.Greeter();
var visible = greeter.isVisible;
greeter.isVisible = true;
Or something like this maybe?
export module Sayings {
export class Greeter {
constructor(public isVisible: () => boolean) {
}
}
}
var someFunc = () => {
return false;
}
var greeter = new Sayings.Greeter(someFunc);
var visible = greeter.isVisible();