What is the eventData Parameter in Mapbox GL JS? - mapbox-gl-js

While looking at the documentation for the Map object's fitBounds() method, I noticed that one of the parameters is eventData. The description of the parameter reads, "Data to propagate to any event listeners."
This parameter is accepted by several other methods, all of which involve map movement of some kind (i.e. panTo, jumpTo, setPitch, and many others).
How would I use this parameter? What is it for?

When you call Map#fitBounds, Map fires events such as Map#move. If you provide eventData to Map#fitBounds, that data will be passed to the event's listeners.
map.on('move', function(event) {
// event.foo is 'bar'
});
map.fitBounds(bounds, {}, {foo: 'bar'});

And the credit goes to...
Lucas! Thank you, Lucas, for your answer. Here is ultimately I how I ended up using this.
Problem:
When using an event listener for 'moveend', one can see this event is fired several times during the animation after map.fitBounds is called. However, we want to do something only when we are truly finished fitting the bounds.
Solution:
Use the eventData parameter to propagate a custom event property to the event listener which we can use as a trigger. Here, we don't call map.setMaxBounds until the animation is completely finished:
// Get the bounds of the bss, fit the map to the bounds
let bssBounds = getBSSBounds(bss, 1); // Custom function using turf.buffer()
// Fit the map to the above boundaries using no options, propagate event data
map.fitBounds(bssBounds, {}, {newBounds: true});
// Set max bounds
map.on('moveend', (event) => {
// If the 'moveend' event has "newBounds" != undefined
if (event.newBounds) {
// Set the maxBounds slighlty wider than the fitBounds
let maxBounds = getBSSBounds(bss, 3); // Custom function using turf.buffer()
map.setMaxBounds(maxBounds)
}
});

Related

While using ag-grid dataSurce, how can I know if the getRows function being called due to scrolling or filtering

As we know, in infinite rowModelType, we have to set dataSource for the ag-grid.
const dataSource = {
rowCount: count
getRows: (params: IGetRowsParams) => this.getRows(params, [])
};
this.gridApi.setDatasource(dataSource);
Now, dataSource.getRows method is called whenever there is need to fetch the rows in the grid (due to scrolling) OR the filter is changed.
I need to decide how many ajax calls need to be made depending on this reason. Below code blocks explains this.
private getRows(params: IGetRowsParams, data: any) {
// two ajax calls can be made from here
// 1. getCount
// 2. getData
// if this getRows function is called due to scrolling in the grid,
// I just want to call getData - no need to call getCount as I already know it
// if this is called due to change in filter,
// I need to call getCount as well as the no of rows will be different
// How can I know here due to which above mentioned reasons, getRows is getting called?
}
Is there anyway to know it within getRows function?

Leaflet User Triggered Events

Is there any way to determine whether an event was triggered programmatically or by a user?
We want to reload marker listings when the map moves or zooms, but we are initially setting the bounds of the map with setBounds() (http://leafletjs.com/reference.html#rectangle-setbounds) which is also triggering the moveend (http://leafletjs.com/reference.html#map-moveend) and zoomend (http://leafletjs.com/reference.html#map-zoomend) events which is causing the markers to reload twice.
There seems to be a (undocumented) property on the event object called hard that gets set when the map is moved by setBounds and doesn't get set when the user drags the map or uses the cursors:
map.on('moveend', function (e) {
if (e.hard) {
// moved by bounds
} else {
// moved by drag/keyboard
}
});
Testcase here on Plunker: http://plnkr.co/edit/SloKuB?p=preview
As another option you could bind to event after you've set the bounds so it won't fire when you set the bounds and when you do want to set the bounds afterwards you could first unbind using .off and rebind again after setting with .on. Something like (untested/hacky):
function moveEndHandler () {
....
}
map.on('moveend', moveEndHandler);
function mySetBounds (bounds) {
map.off('moveEnd', moveEndHandler);
map.setBounds(bounds);
map.on('moveend', moveEndHandler);
}

How do I know when component's DOM is updated and finished transitions in Ractive?

I'm trying to wrap IScroll library into Ractive component. Is there a way to be notified when component's DOM changed and finished any transitions so that I can update the scroller? I see the following ways to achieve this:
Declare dependencies (of the component's template) explicitly, like
<Scroll context="{{...}}" > </Scroll>
I can observe context variable, but still can't wait for transitions to finish.
Patch Ractive.set() so that it emits custom event when used:
// Broadcast promise returned by `set`.
var oldset = Ractive.prototype.set;
Ractive.prototype.set = function () {
var ret = oldset.apply(this, arguments);
this.fire('set', ret);
return ret;
};
Then, in the component's initialization code, I can subscribe to the set event:
this._parent.on('set', function (ret){
ret.then(update);
});
This will not work for Ractive.push() and other methods. Also this will notify my component about all changes made by set-ting something, not only about those affecting component's DOM. Finally, I must explicitly refer to component's parent using this._parent, which means my components can not be nested.
So, is there are better way to achieve this in Ractive?
The initialization options allow oncomplete (or ractive.on('complete'... if you prefer) for initial render. See http://docs.ractivejs.org/latest/lifecycle-events
All of the data modification methods, set, animate, push, slice, etc. return a promise that will not be called until DOM is updated and transitions have completed.
Here's a simple example (http://jsfiddle.net/ctfyes7t/):
{{#if show}}
<li intro='fade:{ duration: 2000 }'>ta da!</li>
{{/if}}
r.set('show', true).then(function(){
// called when fade intro complete
});
The problem can be approached from another direction: changes to the specific parts of DOM can be observed using MutationObserver.
So, I end up with the following approach to wrapping external widgets:
basically, we get component's top node (and initialize 3rd party code) using intro transition, and then in oncomplete callback register observer with something like this:
this.observer = new MutationObserver(update);
this.observer.observe(this.node, {
childList: true,
subtree: true,
characterData: true,
attributes: true, // transitions may change attributes
});
where function update updates 3rd-party widget.
Here is the source code of the component.

jQuery event trigger; callback and timing with respect to animations

Perhaps my question deviates from the simplicity of itself: Given I .trigger() an event, how can I ensure that code following said .trigger() will not execute until the entire event handler function has completed, including all animations, delays, et al., therein?
I hope I'm missing something here; I'm setting up a UI with a bunch of custom events. Some of the events are really just aggregates of other events; for instance:
// ...
'cb-ui.hide': function(event){
// do stuff to hide
},
'cb-ui.close': function(event){
$(this).trigger('cb-ui.hide');
// more stuff for close
},
// ...
Given there is an animation in the cb-ui.hide event, like .fadeOut(1500), it appears (in my testing) that the remaining // more stuff for close doesn't wait for the animation to complete in the triggered event. I was thinking (previous to referencing the docs) that .trigger() would likely have an optional callback argument much like the animation methods:
$(this).trigger('cb-ui.hide', function(event){
// more stuff for close
});
But this doesn't appear to be the case. Since event triggers are not blocking (or don't appear to be at least), what can I do to force the desired functionality, while keeping with the event handler/trigger implementation that I've been building off of?
More specifically:
$('[data-cb-ui-class="window"]').live({
'cb-ui.hide': function(event){
$(this).find('[data-cb-ui-class="content"]').animate({
opacity: 0
}, 1000);
},
'cb-ui.show': function(event){
$(this).find('[data-cb-ui-class="content"]').animate({
opacity: 1
}, 1000);
}
'cb-ui.close': function(event){
$(this).trigger('cb-ui.hide');
$(this).find('[data-cb-ui-class="content"]').animate({
height: 'hide' // happening simultaneously to the animation of 'cb-ui.hide'
// expected to happen in tandem; one after the other
}, 1000);
},
'cb-ui.update': function(event, html){
// none of this is working as expected; the expected being, the 'cb-ui.hide'
// event is triggered (thus fading the [...-content] out) the HTML content is
// updated, then the [...-content] is faded back in from 'cb-ui.show'
// instead its just a mess that results in it fading out
$(this).trigger('cb-ui.hide');
$(this).find('[data-cb-ui-class="content"]').html(html);
$(this).trigger('cb-ui-show');
}
});
$('#foo').trigger('cb-ui.update', ['<p>Hello world!</p>']); // #foo is bound
This example animation should take ~2 seconds, but appears to be taking 1; both animations are occurring simultaneous to each other, rather than in logical order.
Not sure if I understand your question right, but does this make sense?
You can just pass another function to be run after the animation is done.
'cb-ui.hide': function(event, callback){
$('.lol').fadeTo(0,function() {
// fire callback
})
},
'cb-ui.close': function(event){
cb-ui.hide(e,function() {
// other stuff
});
},

duplicate layer/ show permanantly on click

I'm starting to learn how to use leaflet. I'm trying to create a map with markers. If you hover them they should display a route. if the mouse leaves the marker the route should be deleted. (This part works)
When you click on the marker the route should stay on the map even when the mouse leaves the marker.
Therefore I would need to duplicate the route layer so that it doesn't get deleted when the mouse leaves the marker. Or there is a better method that I don't know.
function Route() {
DirectionsLayerLong = omnivore.gpx('GPX/ Route_long.gpx');
DirectionsLayerLong.on('ready', function() {
this.setStyle(style_long);
});
DirectionsLayerShort = omnivore.gpx('GPX/Route_short.gpx');
DirectionsLayerShort.on('ready', function() {
this.setStyle(style_short);
});
return DirectionsLayer = L.featureGroup([DirectionsLayerLong, DirectionsLayerShort]);
};
var Marker = L.marker([50, -100], {
icon: iconfu
}).addTo(map);
Marker.on('mouseover', function(e) {
Route();
DirectionsLayer.addTo(map);
});
Marker.on('mouseout', function(e) {
DirectionsLayer.remove()
});
Marker.on('click', function(e) {
DirectionsPermaLayer.remove();
Route();
DirectionsPermaLayer = DirectionsLayer;
DirectionsPermaLayer.addTo(map);
});
I could simply use omnivore with another variable but I'd like to reuse the function.
The simplest solution is just to remove the mouseout event listener when you click on the marker:
Marker.on('click', function(e) {
Marker.off('mouseout');
});
Cloning your route layer would be a little more complicated (not to mention unnecessary, if removing the event listener solves your problem), but it's worth exploring how one might do that. First of all, a concise explanation of why you can't just create a copy using DirectionsPermaLayer = DirectionsLayer can be found on w3schools:
Objects are mutable: They are addressed by reference, not by value.
If y is an object, the following statement will not create a copy of
y:
var x = y; // This will not create a copy of y.
The object x is not a copy of y. It is y. Both x and y points to the
same object.
Any changes to y will also change x, because x and y are the same
object.
There are many ways to go about cloning an object in Javascript, but I suspect that most of these will not work for cloning leaflet layers, as all Leaflet's internal ids will be copied along with the object, causing unpredictable behavior. The best strategy would probably be to convert DirectionsLayerShort and DirectionsLayerLong to GeoJSON using the .toGeoJSON method, then read them back in using L.geoJson:
var Short2 = L.geoJson(DirectionsLayerShort.toGeoJSON()).setStyle(style_short);
var Long2 = L.geoJson(DirectionsLayerLong.toGeoJSON()).setStyle(style_long);
var Directions2 = L.featureGroup([Long2, Short2]).addTo(map);
This could require a little refactoring of your code, but it should do the job.