Reputation: 46806
I have a D3.js code within an Angular 2 component, written in TypeScript.
Naturally, I tend to wrap things in an OOP way, so that the component can be (for instance) reused multiple times.
However, I have an issue with passing something to event handlers.
this.simulation = d3.forceSimulation()
...
.on("tick", this.onSimulationTick);
onSimulationTick()
can only access global variables, d3.event
and this
:
When a specified event is dispatched, each listener will be invoked with the this context as the simulation.
Global variable is not an option, breaks encapsulation. I can't attach anything to d3.event
, and I don't know what they mean by context.
In the handler, I want to access few things which are class members. So best would be to pass the component object.
How can I pass anything to the handler? How could I use the context for it?
Maybe I could use lambda in some way, like
.on("tick", () => onSimulationTick.that = this, onSimulationTick );
Here's the shortened component code:
@Component({
templateUrl: "dependencies-graph.component.html",
styleUrls: ["dependencies-graph.component.css"],
selector: 'wu-dependencies-graph',
})
export class DependenciesGraphComponent implements OnInit, OnChanges {
// Data
_dependencies: DependenciesData;
private jsonData;
// Selections
private zoomingGroup;
// Behaviors
private simulation;
private zoom;
private center: Point;
private initVisualisation() {
this.zoomingGroup = d3.select("svg #zoomingGroup");
...
this.simulation = d3.forceSimulation()
...
.on("tick", this.onSimulationTick);
}
static onSimulationTick() {
???.zoomingGroup.selectAll(".myEdge")
.attr("x1", function(item) { return item.source.x; })
.attr("y1", function(item) { return item.source.y; })
.attr("x2", function(item) { return item.target.x; })
.attr("y2", function(item) { return item.target.y; });
???.zoomingGroup.selectAll(".myGroup")
.attr("transform", function(d){return "translate("+d.x+","+d.y+")"});
}
Upvotes: 1
Views: 2237
Reputation: 193271
You could bind context with Function.prototype.bind method::
private initVisualisation() {
this.zoomingGroup = d3.select("svg #zoomingGroup");
...
this.simulation = d3.forceSimulation()
...
.on("tick", this.onSimulationTick.bind(this));
}
static onSimulationTick() {
this.zoomingGroup.selectAll(".myEdge")
.attr("x1", function(item) { return item.source.x; })
.attr("y1", function(item) { return item.source.y; })
.attr("x2", function(item) { return item.target.x; })
.attr("y2", function(item) { return item.target.y; });
this.zoomingGroup.selectAll(".myGroup")
.attr("transform", function(d){return "translate("+d.x+","+d.y+")"});
}
If you want to pass additional parameters arrow function might be better option:
.on("tick", () => this.onSimulationTick(somethingElse));
Upvotes: 5