Rebinding exports in d3.js v4 - javascript

I'm creating a map using the modules system. I'm more or less used to D3.js v3 but I am still getting used to v4.
I am trying to add a dispatch but I don't know how to rebind the exports in V4, as this function is not available now.
So for my dispatch (_dis) and my particular event ("changetype"), the rebind in d3 v3 would be right before returning the exports, for example:
d3.mapDots = function (districts){
var _dis = d3.dispatch('changetype');
(...)
exports.color = function(_c){
if(!arguments.length) return color;
color = _c;
return this;
};
d3.rebind(exports,_dis,"on");
return exports
};
Does anyone know how to do this in v4? I've been trying dispatch.apply but it doesn't work.
Thanks!

Good question. Looks like the dispatch object has somewhat changed, and that d3.rebind no longer exists. Because the latter is gone, it appears that there's no way to "copy" (via d3.rebind) the .on() method. Instead you must implement your own. See here how bostock implemented d3-brush.
I put together this jsFiddle to demonstrate how to achieve with D3 v4 what you're asking.
The important bit is implementing the .on method:
instance.on = function() {
var value = dispatcher.on.apply(dispatcher, arguments);
return value === dispatcher ? instance : value;
}
And, dispatching is like this
dispatcher.call("was_clicked", this, "Hello, Foo!");

Related

How can I get and store a selected geom feature to use in other functions in my Azure Maps app

I am using a 'click' event on features in a map. I am using a "global" var that I should be able to reuse but when I try using the var again it reports var is 'undefined'. I've been unable to figure what or why this is getting set to 'undefined' so wondering if there's any way to store the say the ID of the geom feature that I can then simply "hide" on the web page and then retrieve that value again to select the feature without needing to click feature again?
// Global vars
var map, shp;
// Called by click event
function getFeatureProps (e) {
shp = e; // store selected feature (e) for use later
...do other things...
alert(shp); // reports [object] as expected
}
// Called from button
function changeFeatureProperties () {
if (shp.shapes[0].getType() == 'Polygon') { // ERROR: shp is undefined
...only if Polygon...
}
else if (shp.shapes[0].getType() == 'Point') {
...only if Point...
}
...do other things...
}
TIA!
Rick...
Looking at your code, the only reason I can think of shp being undefined is that the changeFeatureProperties is called before getFeatureProps, or that getFeatureProps is being called when no shape has been clicked. Is the event on the map or on a layer?
ID's can be used and shapes stored in a DataSource can be retrieved by ID using the getShapeById function.
Also worth noting, the shapes returned by events can be an atlas.Shape or a GeoJSON feature object. If the shape was added to a DataSource, an atlas.Shape object will be returned, in most other cases it will be a GeoJSON feature. To handle this, add an if statement like
if(e.shapes[0] instanceof atlas.Shape){
if(e.shapes[0].getType() === 'Polygon'){
...
}
} else {
//Shape is a GeoJSON feature.
if(e.shapes[0].geometry.type === 'Polygon'){
...
}
}
Sorry for delay I've been dealing with pandemic issues!
I tried many different ideas and this is the only one that worked consistently for me.
So I corrected storage as such
// Upon object onclick function:
sessionStorage.setItem('shape', JSON.stringify(e.shapes[0].toJson()));
To retrieve if required after storage
getTheProps(shape);
function getTheProps(e) {
var properties;
if (e instanceof atlas.Shape) {
alert('Atlas is Shape');
properties = e.shapes[0].getProperties();
pos = e.position;
}
else {
alert('Atlas is Geometry');
properties = e.properties;
pos = e.geometry.position;
}
}
So this worked out for me as desired and hopefully will help others if they have this requirement.
Cheers!
Rick...

How to apply filters on top of each other in Camanjs?

I have few questions regarding Camanjs library.
1. How can we import camanjs in React?
I have tried installing caman through npm but no success,I have also installed all the addition dependency mentioned(node-gyp & Installing GTK 2) but nothing seems to work.
So does anyone know how to use Camanjs in React??.
2. Scenario: I applied brightness(10) and then contrast(10) using camanjs.
Issue Now if I apply brightness this works fine but when I apply contrast it removes brightness and only applies contrast.
This must be because I'm using revert(false) before applying any new filter.
this.revert(false)
I want to apply both the filters simultaneously.
How can I do this. Can this be acheived using the this.newLayer(function(){}) with some blendingMode?
Or I have to save filters value in some array and then loop over and apply filters and then render canvas?
I also tried to use
this.reloadCanvasData();
Although this worked but when I reduced the value to 0 of both brightness and contrast, ideally it should render original image but instead the canvas turns grey or black.
Can anyone me guide or provide a suitable approach to tackle this problem.
Thanks in advance!!
Note: In below code you see since I was unable to install camanjs through npm, I have to take it into window object via CDN. This is working but create 'this' issue if I try to use 'this' inside
this.newLayer(function(){
/*Somewhere here(this refers to React component instead of Caman) */
})
My code:
onChangeHandler = (e, val) => {
e.persist();
let valInLowercase = 'stackBlur';
if(val !== 'Blur'){
valInLowercase = val.toLowerCase();
}
let canvas = document.querySelector("canvas")
let canavsID = canvas.getAttribute("id");
canavsID = "#" + canavsID;
window.caman(canavsID, function() {
this.revert(false)
this.reloadCanvasData();
if (val !== "Contrast") {
this[valInLowercase](e.target.value).render();
} else {
if(val === 'Contrast'){
this[valInLowercase](e.target.value/2).render();
}
}
})
if(e.type === "mouseup"){
canvas.setAttribute(valInLowercase, e.target.value)
}
};

Creating .json file and storing data in javascript -- using vis.js

In my project I need to save the data to .txt or .xml or .json file. I could not find any answer from vis.js website/issues blog. It might be simple, do not know. Really helpful if anyone help me out with example code. Thank you so much in advance.
function saveData(data,callback) {
data.id = document.getElementById('node-id').value;
data.label = document.getElementById('node-label').value;
clearPopUp();
callback(data);
}
If I understand you correctly, you are looking for a way to save data and options of a graph. In my graph editor adaptation for TiddlyWiki Classic I use the following method to extract data (the full implementation can be found in the repo, see config.macros.graph.saveDataAndOptions, here's a simplified one):
config.macros.graph.saveDataAndOptions = function(network,newOptions) {
newOptions = newOptions || {};
// get nodes and edges
var nodes = network.body.data.nodes._data; // contains id, label, x,y, custom per-node options and doesn't contain options from options.nodes; presumably contains option values set when network was created, not current ones (it is so for x,y)
// no suitable getter unfortunately
var edges = network.body.data.edges._data; // map; for edges to/from? certain node use network.getConnectedNodes(id)
// network.body.data.edges._data is a hash of { id: , from: , to: }
// get node positions, options
var positions = network.getPositions(), // map
options = // get options stored previously
// merge newOptions into options
for(var nodeId in nodes) {
// nodes[nodeId].x is the initial value, positions[nodeId].x is the current one
if(positions[nodeId]) { // undefined for hidden
nodes[nodeId].x = positions[nodeId].x;
nodes[nodeId].y = positions[nodeId].y;
}
storedNode = copyObjectProperties(nodes[nodeId]);
storedNodes.push(storedNode);
}
//# do whatever you need with storedNodes, edges and options
// (pack them with JSON.stringify, store to a file etc)
};
However, while this works ok for storing data, this only helps to save options passed for storing explicitly which can be not very nice for some cases. I use this method in manipulation helpers and on dragEnd (network.on("dragEnd",this.saveToTiddlerAfterDragging), config.macros.graph.saveToTiddlerAfterDragging = function(stuff) { config.macros.graph.saveDataAndOptions(this,{ physics: false }); };). I haven't recieved any better suggestions, though.
If you need to get data and options reactively and setting such helper to handle certain edit events can't solve your problem, then I suggest wrapping nodes, edges and options as vis.DataSet and save those when needed. This is related too.
To answer the question about events/other ways to use such methods. Here's how I use them:
I save data after drag&drop moving of nodes, this is done using an event handler. Namely, I introduced
config.macros.graph.saveToTiddlerAfterDragging = function(stuff) {
config.macros.graph.saveDataAndOptions(this,{ physics: false });
};
(when drag&drop is used, physics should be switched off, otherwise coordinates won't be preserved anyway) and then I use
network.on("dragEnd",this.saveToTiddlerAfterDragging);
so that changes are saved.
As for saving after adding/editing a node/edge, I apply saving not by an event (although it's nice thinking, and you should try events of DataSet, since there's no special graph events for that). What I do is I add an elaborated hijack to the manipulation methods. Take a look at the source I've linked after the
var mSettings = options.manipulation;
line: for each manipulation method, like options.manipulation.addNode I hijack it so that its callback is hijacked to call config.macros.graph.saveDataAndOptions in the end. Here's a simplified version of what I'm doing:
var nonSaving_addNode = options.manipulation.addNode;
options.manipulation.addNode = function(data,callback) {
// hijack callback to add saving
arguments[1] = function() {
callback.apply(this,arguments); // preserve initial action
config.macros.graph.saveDataAndOptions(network); // add saving
};
nonSaving_addNode.apply(this,arguments);
}
The thing is, addNode is actually called when the add node button is clicked; though, I'm using a customized one to create a popup and apply changes once user is happy with the label they chose.

Extending Ember LinkView

Ember.LinkView, the the view class behind the handlebars {{linkTo}} helper is now public in Ember 1.0 RC2. I want to extend it so I can create a custom view without having an extra nested tag for linkTo.
For example:
App.MyLinkView = Ember.LinkView.extend({
namedRoute: 'another'
});
then
{{#view App.MyLinkView}}LinkView to another route{{/view}}
Looked through the source a bit without much luck, as it constantly throws an error.
Here's a jsfiddle with the standard {{linkTo}} working, and the LinkView attempt commented out so it doesn't throw an error.
http://jsfiddle.net/HgmEy/1/
Edit:
Here is a more realistic example of why you would want to do this:
http://jsfiddle.net/HgmEy/3/
The desired functionality is working here using a regular view, however using a LinkView would be preferred to avoid the extra dom element.
LinkView is intended to be created via a helper, which passes (and provides default values for) some options.
Your error occurs when trying to determine whether your custom class is active or not. You'll need to do one of the following
pass or supply the expected default options when using your App.MyLinkView
override the active function and implement what you need
just pass options to {{linkTo}} for the behavior you want
reopen Ember.LinkView to provide the app-wide behavior you'd want
I needed to do this to override Ember.LinkView's call to transitionTo in order to come up with a solution for jQuery animations between transitions. It seems to me that there are a couple of viable ways to override LinkView. The second one I succeeded with is Trek's last option, and is simpler. This is method #2:
Method #2
{{#linkTo 'items' this eventName="myEvent"}} Link to {{title}} {{/linkTo}}
Now rewrite the app-wide LinkView:
Ember.LinkView.reopen({
// this handler is still called on click, but
// if we specify eventName in our template,
// we can call that handler only when we need to,
// or not at all
click: function (e) {
var evtName = this.get('eventName');
// transitionTo was already invoked by
// this._invoke() if evtName was `click`
if (evtName === 'click') return;
e.preventDefault();
// do some stuff here
var args = [].slice.call(arguments);
this.trigger.apply(this, [evtName].concat(args));
}
});
Method #1
The first method I came up with was to extend Ember.LinkView and create a custom Handlebars helper. The Ember source was really handy here for reading, but I had to override a private method, so I don't think this is really ideal. Here's the implementation. Keep in mind I was trying to control when the View triggered a transitionTo:
{{#appLinkTo 'items' this}} Link to {{title}} {{/appLinkTo}}
Now code it up!
App.LinkView = Ember.LinkView.extend({
// always called after this.invoke(),
// which calls transitionTo
click: function (e) {
e.preventDefault();
},
// already bound to the click event by this.init().
// our click handler above always gets called after this one
_invoke: function (event) {
// we need to simulate the old _invoke if we
// want to override its call to transitionTo
//
// https://github.com/emberjs/ember.js/blob/v1.0.0/packages/ember-routing/lib/helpers/link_to.js#L297
var isSimpleClick = Ember.ViewUtils.isSimpleClick;
if (!isSimpleClick(event)) { return true; }
event.preventDefault();
if (this.bubbles === false) { event.stopPropagation(); }
if (this.get('_isDisabled')) { return false; }
if (this.get('loading')) {
Ember.Logger.warn("This link-to is in an inactive loading state because at least one of its parameters presently has a null/undefined value, or the provided route name is invalid.");
return false;
}
// now we can start messing around
var routeArgs = this.get('routeArgs');
// routeArgs seems to have format ['routeName', models for dynamic segments]
this.set('routeArgs', ['group', routeArgs[1]]);
// if we use:
this.get('controller').send('someAction', routeArgs);
// the controller can do in its `someAction` handler:
// `this.transitionToRoute.apply(this, routeArgs);`
}
});
// besides the naming, this is verbatim from the end of:
// https://github.com/emberjs/ember.js/blob/v1.0.0/packages/ember-routing/lib/helpers/link_to.js
Ember.Handlebars.registerHelper('app-link-to', function(name) {
var options = [].slice.call(arguments, -1)[0],
params = [].slice.call(arguments, 0, -1),
hash = options.hash;
hash.disabledBinding = hash.disabledWhen;
hash.parameters = {
context: this,
options: options,
params: params
};
return Ember.Handlebars.helpers.view.call(this, App.LinkView, options);
});
Ember.Handlebars.registerHelper('appLinkTo', Ember.Handlebars.helpers['app-link-to']);
Method #3
If you want the best of both, you could combine both methods and extend Ember.LinkView, create a custom Handlebars helper, and use custom event names to signify which actions you want to take. That way, overriding Ember.LinkView, and overwriting _invoke aren't necessary.
Good luck!

Synchronize interdependent Dojo widgets/values

I'm about to build a simple "mortgage calculator" where a user is to adjust some sliders OR edit values in input fields in order to calculate some final value based on the provided data.
Schematically it will look something like this:
Slider1 - Input1
Slider2a - Input2a
Slider2b - Input2b
The idea is that the value of the input must be reflected in the slider, and vice versa. In addition, the values and limits of slider 2a/2b and input 2a/2b depend on each other, according to some simple rule.
It has to be done in Dojo, which I've never used before, and, even though Dojo has quite good documentation, it is a little overwhelming, so I'd appreciate if someone could point me in the right direction.
First of all, here is my solution working at jsFiddle: http://jsfiddle.net/phusick/HCx3w/
You can use dojo/aspect, dojo/topic and dojo/Stateful and directly connect those widgets to each other in various ways. You will probably end up with a tightly coupled set of widgets, i.e. those widgets will know about each other, even if there is no reason a particular widget should have any knowledge about the fact its value is being synchronized with another widget.
Contrary to the aforementioned you can apply loose coupling principle, which will allow you to synchronize any number of widgets without any mutual references among them. Here is my solution:
Obtain references to widgets and couple them into sets (arrays):
var slider1 = registry.byId("slider1");
var slider2 = registry.byId("slider2");
var spinner1 = registry.byId("spinner1");
var spinner2 = registry.byId("spinner2");
var set1 = [slider1, spinner1];
var set2 = [slider2, spinner2];
synchronize function:
var synchronize = function(/*Array*/ widgets, /*String*/ topicName) {
var synchronized = function() {
var count = 0;
array.forEach(widgets, function(widget) {
if(widget.get("synchronized") === true) { count++}
});
return (count == widgets.length);
}
array.forEach(widgets, function(w) {
w.set("synchronized", false);
// register onchange handler for each widget in the set
w.on("change", function(value) {
array.forEach(widgets, function(widget) {
if(this !== widget) {
widget.set("value", value);
widget.set("synchronized", true);
}
}, this);
// needed to publish topic just once per value change across all the widgets in the set
if(synchronized()) {
array.forEach(widgets, function(widget) {
widget.set("synchronized", false);
});
// publish topic if any
if(topicName) { topic.publish(topicName, value)};
}
});
});
}
Register sets of widgets to synchronize via sychronize function:
synchronize(set1, "value1-changed"); // synchronize and publish topic when value changes
synchronize(set2); // just synchronize
Subscribe to the topic you registered above:
topic.subscribe("value1-changed", function(value) {
console.log("value1-changed", value);
// here you can change value and limits of of `set2` widgets
});
dojo. Stateful is your friend... http://dojotoolkit.org/reference-guide/1.7/dojo/Stateful.html
Have you tried dojo.connect. This can be used method chaining. So when the event is fired in control multiple methods can be invoked. Beside this there is publish\subscribe mechanism in dojo. In pub\sum model you can write method to subscribe for simple message strings. When some method published that string, than subscriber method will be invoked.

Categories