Reputation: 1570
I think I'm missing something quite substantial from the understanding here, so perhaps a little explanation, too, would be incredibly helpful.
I have a class, as such:
class Entity {
protected pos: number = [0,0];
...
public setPos(x: number, y: number) {
this.pos = [x, y];
}
}
and would like something like
class EntityPositionListener {
//do stuff when an Entity position changes
}
I also have descendants of the Entity
class. As such, any time any descendent of Entity
or Entity
itself changes the pos
variable (in a given instance), I would like to know about it so that I can update the position on the map, as well as capture the instance so I could theoretically do other things to that instance.
I've noticed that Firefox has that Object.watch()
command and I found a "cross-browser" solution someone had posted in another question--to no avail--and I've tried "wrapping" the Entity
class in a post-init phase, but the latter just ends up overwriting the Entity
class or creates a infinite loop, so I'm obviously missing the point somewhere. If wrapping is the answer, how do I do this in TypeScript? The JS version created a loop or didn't work.
//This created an infinite loop in runtime, but more importantly overwrote the Entity class
function EntityListener(name) {
cout(name);
}
var oldEntity = Entity;
function Entity(name) {
if (typeof EntityListener === "function") {
EntityListener(name);
}
oldEntity(name);
}
Ideally, I'd like to listen to several things on a given class--such as in the game update loop--this particular case just used for demonstration. I've found countless links, many of which are JS and not explicitly TypeScript, so I would really like a TypeScript solution and some explanation as to what's going on. I am just fundamentally not understanding how to go about this.
EDIT
If this is a better version of the question, maybe more simply, how do I listen for Entity.setPos()
to be called?
Upvotes: 6
Views: 20569
Reputation: 25940
Best is to use the subscribe
approach, so you can automatically support any number of event listeners on the object, and not just one. And use a strongly-typed event, for type safety.
Several libraries can do such a thing today. Here's an implementation via sub-events:
import {SubEvent} from 'sub-events';
type PosChanged = {newPos: number[], oldPos: number[]};
class Entity {
// strongly-typed event:
readonly onPosChanged: SubEvent<PosChanged> = new SubEvent();
private _pos: number[] = [];
get pos(): number[] {
return this._pos;
}
set pos(newPos: number[]) {
// can add verification here, if it actually changed;
const oldPos = this._pos;
this._pos = newPos;
this.onPosChanged.emit({newPos, oldPos});
}
}
Any derived class then can subscribe to the pos
change:
const e = new Entity();
e.onPosChanged.subscribe(data => {
// data is strongly-typed
console.log(data); //-> { newPos: [ 1, 2, 3 ], oldPos: [] }
});
e.pos = [1, 2, 3]; // this will trigger the event
Upvotes: 2
Reputation: 28676
Object.observe will be most likely dropped. Object.watch is only supported in Gecko, so don't use it in your code unless you target only Firefox.
This means that you have to write your own event emitting. You can use TypeScript getters and setters if you like them:
class Entity {
private entitityListener:EntityPositionListener;
private _pos:number[] = [];
get pos():number[] {
return this._pos;
}
set pos(value:number[]) {
this._pos = value;
if (this.entitityListener) {
this.entitityListener.reportEvent('pos', value);
}
}
}
class SuperEntity extends Entity {
}
let superEntity = new SuperEntity();
superEntity.pos = [1,2]; // this will call 'this.entitityListener.reportEvent()' and you don't need superEntity.setPos([1,2]);
which transpiles to:
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var Entity = (function () {
function Entity() {
this._pos = [];
}
Object.defineProperty(Entity.prototype, "pos", {
get: function () {
return this._pos;
},
set: function (value) {
this._pos = value;
if (this.entitityListener) {
this.entitityListener.reportEvent('pos', value);
}
},
enumerable: true,
configurable: true
});
return Entity;
})();
var SuperEntity = (function (_super) {
__extends(SuperEntity, _super);
function SuperEntity() {
_super.apply(this, arguments);
}
return SuperEntity;
})(Entity);
var superEntity = new SuperEntity();
superEntity.pos = [1, 2];
You can use Object.defineProperty() to define getters and setters and this means that you can define setter to contain a call to a EntityListener
method.
Upvotes: 2