unhover in raphaeljs not working - raphael

I'm currently applying anonymous functions to a hover event, that call global functions inside:
GridInterface.HexGrid.TileGlobal = {
hoverIn: function(obj) {
var self = obj;
self.tile.attr({ fill: '#c7c7c7', stroke : '#000' });
self.tile.toFront();
self.label.toFront();
self.label.attr({ fill : '#000' });
},
hoverOut: function(obj) {
var self = obj;
self.tile.attr({ fill : '#d0d1ff', stroke : '#aaa' });
self.label.attr({ fill: '#999' });
}
}
Then i simply call:
.hover(function() {
GridInterface.HexGrid.TileGlobal.hoverIn(self);
}, function() {
GridInterface.HexGrid.TileGlobal.hoverOut(self);
}
)
on the object. However, when i switch that to unhover, the event is not removed. I think it's because it's a different anonymous function. Any ideas on how to fix?

GridInterface.HexGrid.Tile = function(coords) {
var self = this;
this.hoverIn = function() {
this.attr({ fill: '#c7c7c7', stroke : '#000' });
};
this.hoverOut = function() {
this.attr({ fill: '#d0d1ff', stroke: '#aaa' });
};
this.attachEvents = function()
{
self.el[0].hover(self.hoverIn, self.hoverOut);
};
};
Then elsewhere i call:
tile.el[0].unhover(tile.hoverIn, tile.hoverOut);
Tile being an object instance of that above function.

Maybe solve the problem not with the raphäel event handling but with a library of your choice, ie jQuery: $(circle.node).click(…)
like that you can addEvent / removeEvent on demand.

Related

I cannot update my chart ( in react-chartjs-2 )

I want to update my chart every seconds
Here is my code :
class Chart extends Component {
constructor(props) {
super(props);
this.state = {
chartData: {
// my charData
}
}
setInterval(function(){
this.props.changeHandler();
}, 500);
}
changeHandler() {
this.state.update();
}
// my render
}
But I have this error : this.props is undefined (On the setInterval)
Anyone can help me please ?
function has its own this context so you either have to store the this context in a local variable before the setInterval or use an arrow function instead of function
setInterval(() => {
this.props.changeHandler();
}, 500);
First you need to obtain a reference to the Chart.js instance.
constructor() {
...
this.chartReference = React.createRef();
...
}
render() {
return (
<Doughnut ref={this.chartReference} data={this.state.data} />
)
}
From this reference, you can obtain the chart instance, update its data and finally invoke chart.update().
setInterval(() => {
const chart = this.chartReference.current.chartInstance;
chart.data.datasets[0].data = [<the new data>];
chart.update();
}, 2000);
Please take a look at this StackBlitz and see how it works.

options.scales.yAxes[0].ticks.callback not getting updated dynamically in ember-cli-chart

I have embedded ember-cli-chart in my hbs file as
<div class="chart">
{{ember-chart type='line' data=data options=options}}
</div>
In my component file I have created an options property as
options: computed('metric', function() {
let opts = defaultOptions;
if (this.metric === 'height') {
opts.scales.yAxes = [{
ticks: {
callback: function(value, index, values) {
// code to return labels
}
}
}]
} else {
opts.scales.yAxes = [{
ticks: {
callback: function(item, index, items) {
// code to return labels
}
}
}]
}
return opts;
});
I want to display Y-Axis labels based on the current selected metric.
When first time chart loads it renders correct labels on y-Axis and if I change the metric then the same callback is getting used instead of the other one (in else part) and renders same labels but with updated data values.
Can anyone help on this?
Hmmm I don't know the addon or chart.js for the matter, but when looking at the source code for the ember-chart component, I see
didUpdateAttrs() {
this._super(...arguments);
this.updateChart();
},
updateChart() {
let chart = this.get('chart');
let data = this.get('data');
let options = this.get('options');
let animate = this.get('animate');
if (chart) {
chart.config.data = data;
chart.config.options = options;
if (animate) {
chart.update();
} else {
chart.update(0);
}
}
}
So, in order for chart.js to update, you need didUpdateAttrs to fire, which means in your case here that options itself needs to change. I don't know how you're creating defaultOptions, but assuming this reference never changes, there's no reason that didUpdateAttrs would fire since you aren't changing the reference to options (you're only changing child props of defaultOptions in the computed). I would suppose that:
import { assign } from '#ember/polyfills';
...
options: computed('metric', function() {
let opts = assign({}, defaultOptions);
if (this.metric === 'height') {
opts.scales.yAxes = [{
ticks: {
callback: function(value, index, values) {
// code to return labels
}
}
}]
} else {
opts.scales.yAxes = [{
ticks: {
callback: function(item, index, items) {
// code to return labels
}
}
}]
}
return opts;
})
would be enough to trigger the behavior you want since we always return a new object when a recomputation of options occurs.

requirejs + django_select2

I'm building a wagtail / django app using requirejs as js assets combiner, for the site front end.
I'm using it because I've ever been in a kind of JS dependencies hell, where nothing works because of multiple versions of same libs loaded, from different django apps... (I don't even know if it is a good solution)
I've to tell that I'm not a JS expert, and I've none arround me :(
I'm using the good old templates to render the pages, not using angular, react, riot nor vue : I'm a pretty old school dev :)
I've already adapted some scripts to use require, but I'm stuck for now...
I've installed the django_select2 application, and I'm trying to adapt the django_select2.js asset.
I've loaded select2 through bower, and I've updaetd my config.js:
"shim": {
select2: {
deps: ["jquery"],
exports: "$.fn.select2"
}
},
paths: {
...
select2: "select2/dist/js/select2"
}
Then I'm trying to adapt the django_select2.js:
require(['jquery', 'select2'], function ($, select2) {
return (function ($) {
var init = function ($element, options) {
$element.select2(options);
};
var initHeavy = function ($element, options) {
var settings = $.extend({
ajax: {
data: function (params) {
var result = {
term: params.term,
page: params.page,
field_id: $element.data('field_id')
}
var dependentFields = $element.data('select2-dependent-fields')
if (dependentFields) {
dependentFields = dependentFields.trim().split(/\s+/)
$.each(dependentFields, function (i, dependentField) {
result[dependentField] = $('[name=' + dependentField + ']', $element.closest('form')).val()
})
}
return result
},
processResults: function (data, page) {
return {
results: data.results,
pagination: {
more: data.more
}
}
}
}
}, options);
$element.select2(settings);
};
$.fn.djangoSelect2 = function (options) {
var settings = $.extend({}, options);
$.each(this, function (i, element) {
var $element = $(element);
if ($element.hasClass('django-select2-heavy')) {
initHeavy($element, settings);
} else {
init($element, settings);
}
});
return this;
};
$(function () {
$('.django-select2').djangoSelect2();
});
}($));
});
I'm having a Mismatched anonymous define() when running my page in the browser...
I'me realy not a JS expert, I'm coding by trial and error... Could anyone help me with this ?
Thanks !
OK, I have an auto-response...
I've inherited the mixin:
class _Select2Mixin(Select2Mixin):
def _get_media(self):
"""
Construct Media as a dynamic property.
.. Note:: For more information visit
https://docs.djangoproject.com/en/1.8/topics/forms/media/#media-as-a-dynamic-property
"""
return forms.Media(js=('django_select2/django_select2.js', ),
css={'screen': (settings.SELECT2_CSS,)})
media = property(_get_media)
class _Select2MultipleWidget(_Select2Mixin, forms.SelectMultiple):
pass
Then I can use the widget:
class DocumentationSearchForm(forms.Form):
...
document_domains = forms.ModelMultipleChoiceField(required=False,
label=_('Document domains'),
queryset=NotImplemented,
widget=_Select2MultipleWidget)
I've set my config.js file for path:
requirejs.config({
paths: {
jquery: 'jquery/dist/jquery',
select2: "select2/dist/js"
},
shim: {
"select2/select2": {
deps: ["jquery"],
exports: "$.fn.select2"
}
}
});
Then I've overridden the django_select2.js file, to wrap the content in a require satement:
require(['jquery', 'select2/select2'], function ($) {
(function ($) {
var init = function ($element, options) {
$element.select2(options);
};
var initHeavy = function ($element, options) {
var settings = $.extend({
ajax: {
data: function (params) {
var result = {
term: params.term,
page: params.page,
field_id: $element.data('field_id')
}
var dependentFields = $element.data('select2-dependent-fields')
if (dependentFields) {
dependentFields = dependentFields.trim().split(/\s+/)
$.each(dependentFields, function (i, dependentField) {
result[dependentField] = $('[name=' + dependentField + ']', $element.closest('form')).val()
})
}
return result
},
processResults: function (data, page) {
return {
results: data.results,
pagination: {
more: data.more
}
}
}
}
}, options);
$element.select2(settings);
};
$.fn.djangoSelect2 = function (options) {
var settings = $.extend({}, options);
$.each(this, function (i, element) {
var $element = $(element);
if ($element.hasClass('django-select2-heavy')) {
initHeavy($element, settings);
} else {
init($element, settings);
}
});
return this;
};
$(function () {
$('.django-select2').djangoSelect2();
});
}($));
});
That's all, folks !

ember-leaflet adding features

I'am trying to used ember-leaflet (https://github.com/gabesmed/ember-leaflet) on a profile page. The problem is getting the features into the layers. Any suggestion on how I best can add the same coords as in the center function to the "layer".
Tried creating a function in childLayers, that returns a new layer with the coords, but then ember generates an error.
var MarkerLayer =
EmberLeaflet.MarkerLayer.extend(
EmberLeaflet.PopupMixin, {
popupContentBinding: 'content.title'
});
var MarkerCollectionLayer = EmberLeaflet.MarkerCollectionLayer.extend({
contentBinding: 'controller',
itemLayerClass: MarkerLayer
});
var layer = EmberLeaflet.MarkerCollectionLayer.extend({
content: [
{location: L.latLng(63.429722, 10.393333)},
{location: L.latLng(40.721, -73.991)}]});
export default EmberLeaflet.MapView.extend({
latitude: 61,
longitude: 8,
center: function() {
console.log(get(this, 'latitude'));
var latitude = get(this, 'latitude');
var longitude = get(this, 'longitude');
return L.latLng(latitude, longitude);
}.property("latitude", "longitude"),
zoom: 16,
options: {maxZoom: 19, minZoom: 7},
childLayers: [
WMSLayer,
MarkerCollectionLayer,
layer
]
});
I did something like this:
export default EmberLeafletComponent.extend({
center: Ember.computed(function() {
return this.get('coordinates');
}),
/////////////////////////////////////
// PROPERTIES
/////////////////////////////////////
geoJSON: null,
/////////////////////////////////////
// COMPUTED PROPERTIES
/////////////////////////////////////
childLayers: Ember.computed('coordinates', function() {
return [
TileLayer.extend({
tileUrl: 'https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}',
options: {
id: 'scoutforpets.o2ml3nm1',
accessToken: ENV.APP.MAPBOX_KEY
}
}),
MarkerCollectionLayer.extend({
content: [{ location: this.get('coordinates') }]
})
];
}),
coordinates: Ember.computed('geoJSON', function() {
if (this.get('geoJSON')) {
const coordinates = JSON.parse(this.get('geoJSON')).coordinates;
if (coordinates) {
return L.latLng(coordinates[1], coordinates[0]);
}
}
return null;
}),
});
I then use the component as such:
{{leaflet-map geoJSON=coordinates}}
In my case, I pass in a GeoJSON string and the coordinates computed property parses it. When the map calls childLayers it returns the tiles/markers using the parsed coordinates.

Delay ember view render till $getJSON isLoaded

The problem with this code is that the render code is entered twice, and the buffer is not where I expect it. Even when I get the buffer, the stuff I push in is not rendered to the screen.
App.FilterView = Ember.View.extend({
init: function() {
var filter = this.get('filter');
this.set('content', App.ViewFilter.find(filter));
this._super();
},
render: function(buffer) {
var content = this.get('content');
if(!this.get('content.isLoaded')) { return; }
var keys = Object.keys(content.data);
keys.forEach(function(item) {
this.renderItem(buffer,content.data[item], item);
}, this);
}.observes('content.isLoaded'),
renderItem: function(buffer, item, key) {
buffer.push('<label for="' + key + '"> ' + item + '</label>');
}
});
And the App.ViewFilter.find()
App.ViewFilter = Ember.Object.extend();
App.ViewFilter.reopenClass({
find: function(o) {
var result = Ember.Object.create({
isLoaded: false,
data: ''
});
$.getJSON("http://localhost:3000/filter/" + o, function(response) {
result.set('data', response);
result.set('isLoaded', true);
});
return result;
}
});
I am getting the data I expect and once isLoaded triggers, everything runs, I am just not getting the HTML in my browser.
As it turns out the answer was close to what I had with using jquery then() on the $getJSON call. If you are new to promises, the documentation is not entirely straight forward. Here is what you need to know. You have to create an object outside the promise - that you will return immediately at the end and inside the promise you will have a function that updates that object once the data is returned. Like this:
App.Filter = Ember.Object.extend();
App.Filter.reopenClass({
find: function(o) {
var result = Ember.Object.create({
isLoaded: false,
data: Ember.Object.create()
});
$.getJSON("http://localhost:3000/filter/" + o).then(function(response) {
var controls = Em.A();
var keys = Ember.keys(response);
keys.forEach(function(key) {
controls.pushObject(App.FilterControl.create({
id: key,
label: response[key].label,
op: response[key].op,
content: response[key].content
})
);
});
result.set('data', controls);
result.set('isLoaded', true);
});
return result;
}
});
Whatever the function inside then(), is the callback routine that will be called once the data is returned. It needs to reference the object you created outside the $getJSON call and returned immediately. Then this works inside the view:
didInsertElement: function() {
if (this.get('content.isLoaded')) {
var model = this.get('content.data');
this.createFormView(model);
}
}.observes('content.isLoaded'),
createFormView: function(data) {
var self = this;
var filterController = App.FilterController.create({ model: data});
var filterView = Ember.View.create({
elementId: 'row-filter',
controller: filterController,
templateName: 'filter-form'
});
self.pushObject(filterView);
},
You can see a full app (and bit more complete/complicated) example here