Event scoping in custom Mapbox IControl - mapbox

I want to create a Mapbox IControl object, which zooms the map in when a button in the control is clicked.
This is my custom IControl class:
class customControl {
_map;
_container;
_myButton;
constructor() {
this._myButton = document.createElement('button');
this._myButton.className = 'mapboxgl-ctrl-zoom-in';
this._myButton.type = 'button';
this._myButton.title = 'Test';
this._myButton.onclick = this._myAction;
this._container = document.createElement('div');
this._container.className = 'mapboxgl-ctrl-group mapboxgl-ctrl';
this._container.appendChild(this._myButton);
}
onAdd(map) {
this._map = map;
return this._container;
}
onRemove() {
this._container.parentNode.removeChild(this._container);
this._map = undefined;
}
_myAction() {
const map = this._map;
console.log(this._map);
map.zoomIn();
}
}
I add this control to my map using the following code:
let myControl = new customControl();
map.addControl(myControl, 'bottom-left');
When the button is clicked, I however get an Cannot read properties of undefined (reading 'zoomIn') error message, since this._map is obviously undefined. When I remove the const map = this._map; line from the _myAction() function, the zoom in works as expected, but the global map object is used.
Why is this._map undefined in _myAction()? Is there a way to make _myAction() aware of the "scope" the button is clicked, so I can access properties of my custom IControl when clicked?

Related

How to show/hide dialog fields with a checkbox in AEM Touch UI

I am relatively new to AEM and I am trying to hide/show dialog fields on checkbox click. I have tried some ways but failed to achieve this functionality. This is just for my own learning. How can I achieve this?
I have tried adding the js clientlib and adding some classes and target to the checkbox and target fields respectively as suggested in other answers but it didn't seem to work. Please help.
First you need to create a clientLibs and add categories as cq.authoring.dialog.all, see the code below:
(function($, $document) {
$document.on("dialog-ready", function() {
Coral.commons.ready($document, function () {
dispalyOrHideTabs();
$(document).on('change', '#showText', function() {
if($('#showText').attr('checked')){
show('1');
}else{
hide('1');
}
});
$(document).on('change', '#showTable', function() {
if($('#showTable').attr('checked')){
show('2');
}else{
hide('2');
}
});
function hide(index){
var tab = $document.find("[id='compareImgId-"+index+"']").closest(".coral3-Panel");
var tab2 = tab.attr("aria-labelledby");
var tab3 = $document.find("[id='"+tab2+"']");
tab3.addClass("hide");
}
function show(index){
var tab = $document.find("[id='compareImgId-"+index+"']").closest(".coral3-Panel");
var tab2 = tab.attr("aria-labelledby");
var tab3 = $document.find("[id='"+tab2+"']");
tab3.removeClass("hide");
}
function dispalyOrHideTabs(){
var editable = Granite.author.DialogFrame.currentDialog.editable;
if(editable){
var node = Granite.HTTP.eval(Granite.author.DialogFrame.currentDialog.editable.path + ".json");
if(node){
var storedTextValue = node.showText;
var storedTableValue = node.showTable;
if(storedTextValue){
show('1');
}else{
hide('1');
}
if(storedTableValue){
show('2');
}else{
hide('2');
}
}
}
}
});
});
})($, $(document));
Add granite:id property as showText of the checkbox resource type.
And below is the dialog tabs which will be hidden and shown:

geoJSON onEachFeature Mouse Event

I have a problem where i try to use the onEachFeature Methode for a geoJSON Layer. I try to assign a click listener to every Feature.
The problem is that i always get that error when i click at a feature:
Uncaught TypeError: Cannot read property 'detectChanges' of undefined
I can think of that this is because the Layer is assigned before the constructor runs but to do that in the ngOnInit function wont worked either. Would be cool if ther is a good way to do that :)
constructor(private changeDetector: ChangeDetectorRef){}
fitBounds: LatLngBounds;
geoLayer = geoJSON(statesData, {onEachFeature : this.onEachFeature});
onEachFeature(feature , layer) {
layer.on('click', <LeafletMouseEvent> (e) => {
this.fitBounds = [
[0.712, -74.227],
[0.774, -74.125]
];
this.changeDetector.detectChanges();
});
}
layer: Layer[] = [];
fitBounds: LatLngBounds;
onEachFeature(feature , layer : geoJSON) {
layer.on('click', <LeafletMouseEvent> (e) => {
console.log("tets"+e.target.getBounds().toBBoxString());
this.fitBounds = [
[0.712, -74.227],
[0.774, -74.125]
];
this.changeDetector.detectChanges();
});
}
constructor(private changeDetector: ChangeDetectorRef){}
ngOnInit() {
let geoLayer = geoJSON(statesData, {onEachFeature : this.onEachFeature});
this.layer.push(geoLayer);
}
You need to make sure that the right this is accessible in your callback. You do this using function.bind() in Javascript. See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
constructor(private changeDetector: ChangeDetectorRef){}
fitBounds: LatLngBounds;
geoLayer = geoJSON(statesData, {
// Need to bind the proper this context
onEachFeature : this.onEachFeature.bind(this)
});
onEachFeature(feature , layer) {
// 'this' will now refer to your component's context
let that = this;
layer.on('click', <LeafletMouseEvent> (e) => {
that.fitBounds = [
[0.712, -74.227],
[0.774, -74.125]
];
// Aliased 'that' to refer to 'this' so it is in scope
that.changeDetector.detectChanges();
});
}
The let that = this trick is to make sure you don't have the same problem on the click event handler. But, you could also make that handler be a function in your class and use bind to set this.

How to get selected layers in control.layers?

Is there a way to select all selected layers in the control.layers with leaflet api?
I can do it with the help of jquery like this :
$('.leaflet-control-layers-selector:checked')
But maybe there is an api?
Thanks
There is no API for that but you could easily create one yourself:
// Add method to layer control class
L.Control.Layers.include({
getActiveOverlays: function () {
// Create array for holding active layers
var active = [];
// Iterate all layers in control
this._layers.forEach(function (obj) {
// Check if it's an overlay and added to the map
if (obj.overlay && this._map.hasLayer(obj.layer)) {
// Push layer to active array
active.push(obj.layer);
}
});
// Return array
return active;
}
});
var control = new L.Control.Layers(...),
active = control.getActiveOverlays();
Based on iH8's answer
L.Control.Layers.include({
getOverlays: function() {
// create hash to hold all layers
var control, layers;
layers = {};
control = this;
// loop thru all layers in control
control._layers.forEach(function(obj) {
var layerName;
// check if layer is an overlay
if (obj.overlay) {
// get name of overlay
layerName = obj.name;
// store whether it's present on the map or not
return layers[layerName] = control._map.hasLayer(obj.layer);
}
});
return layers;
}
});
Now you can use
var control = new L.Control.Layers(...)
control.getOverlays(); // { Truck 1: true, Truck 2: false, Truck 3: false }
I find this a little more useful because
all the layers are included
the key is the name of the layer
if the layer is showing, it has a value of of true, else false

Cant open popup programmatically

I have a map on which im loading the markers with geoJSON.
When the map loads i run a function buildVisibleSys which is responsible to build a list of currently visible systems on the map.
That function looks like this:
buildVisibleSys = function() {
var bounds, visibleSys;
visibleSys = [];
bounds = map.getBounds();
return systemLocations.eachLayer(function(marker) {
var link;
link = onScreenEl.appendChild(document.createElement('a'));
link.href = '#';
link.id = "marker" + marker._leaflet_id;
link.innerHTML = marker.options.title;
link.onclick = function() {
marker.openPopup();
map.panTo(marker.getLatLng());
};
});
};
map.on('load', buildVisibleSys);
In this function, for each layer im getting some data and building a html block with the names of each marker. Each of those names, associated to the link var, have a onclick event attached that will center the map on the correspondent marker. This all works except for the marker.openPopup() call i also have on that onclick event.
Any idea of what am I missing here?
I've also made a demo of the code available here:
http://jsfiddle.net/lmartins/z8wBW/
UPDATE:
Even more confusing to me is that with mouseover the same method works without a problem, that is, in the function above the following code do open the popup:
link.onmouseover = function(ev) {
marker.openPopup();
marker._icon.classList.add('is-active');
};
Change your link handler to
link.onclick = function(e) {
marker.openPopup();
map.panTo(marker.getLatLng());
e.stopPropagation();
e.preventDefault();
};
The click of the link to open the popup is bubbling down to the map and closing the popup right after it's opened.

Clickhandler on a feature in OpenLayers

I am trying to have a click event on a pop-up in openlayers. Right now I'm doing it with a hardcoded onclick in the feature:
var vector = new OpenLayers.Layer.Vector("Points",{
eventListeners:{
'featureselected':function(evt){
var feature = evt.feature;
var popup = new OpenLayers.Popup.Anchored("popup",
OpenLayers.LonLat.fromString(feature.geometry.toShortString()),
new OpenLayers.Size(275,71),
'<div id="pincontent" onclick="pindetails()"><h3>' + feature.attributes.title +'</h3><div style="display: none;" id="pindescription">'+ feature.attributes.content +'</div></div>',
null,
false
);
popup.imageSrc = 'img/popup.png';
popup.autoSize = false;
popup.backgroundColor = 'transparent';
var offset = {'size':new OpenLayers.Size(0,0),'offset':new OpenLayers.Pixel(-74,-10)};
popup.anchor = offset;
popup.panMapIfOutOfView = true;
popup.imageSize = new OpenLayers.Size(275,71);
popup.relativePosition = "br";
popup.calculateRelativePosition = function () {
return 'tr';
};
feature.popup = popup;
map.addPopup(popup);
//adding event listener
map.events.register('mousedown', popup, function(evt){alert('help')}, false);
},
'featureunselected':function(evt){
var feature = evt.feature;
map.removePopup(feature.popup);
feature.popup.destroy();
feature.popup = null;
}
}
});
But the main OpenLayers div is intercepting the click so I have to click twice.. I'm not sure if there's a way to disable this. I've looked at the openLayers documentation and I'm not sure how to use their API to add an event listener for a click on a feature.
Perhaps you should add return false; after your function call in onclick to stop event propagation (which is the default).
Also have a look at the register method:
this.map.events.register('click', this.map, function handleMapClick(e) { ... }
http://dev.openlayers.org/releases/OpenLayers-2.6/doc/apidocs/files/OpenLayers/Events-js.html#OpenLayers.Events.register
It seems that evt.stopPropagation(); does not work. But using return true; in the singleclick event handler can stop the Event-Propagation from penetrating down to cascaded layers.