How to get ID of layer in feature group on click - leaflet

I have a feature group containing several markers.
And I have this code to respond to a click on any of the markers:
sampleFeatureGroup.on("click", function(){
alert(this.id); // something like this
});
I want to be able to get the id of the marker which is clicked on from within the function, however "this" refers to the feature group, so I cannot find the id of the marker clicked on, this seems like it should be easy but I can't figure it out.

You must use eachLayer to iterate through the featureGroup, and then bind a function to the click event, like this:
group.eachLayer(function(layer) {
layer.on('click', function(){
alert(this._leaflet_id)
});
});
Here's a working example on Plunker:
http://plnkr.co/edit/4fh7vhVet8N0iD4GE3aN
And here's the reference to eachLayer:
http://leafletjs.com/reference.html#layergroup-eachlayer

Although it is possible to retrieve the ID using this._leaflet_id, this is not best practice because variables prefixed with _ should be treated as private.
Instead, it would be better to use the getLayerId() function like below:
group.eachLayer(function(layer) {
layer.on('click', function(){
alert(group.getLayerId(layer))
});
});

Related

Update Popup data in Mapbox GL JS

I'm making a fleet tracker with Mapbox GL JS, it gets the data from a GeoJson and inserts in the map, as the Add live realtime data, I've also integrated the Mapbox store locator example, by now I can update in realtime the sidebar and the points on the map. A modification that I would like to make is not display only one single popup, but a popup for every icon located there. I would like to know how to update this popups, because in the way I'm making it's creating a new popup every movement of the object, but is not closing the previous one. Here is the function that I'm using for the popups
function createPopUp(currentFeature, data) {
var popUps = document.getElementsByClassName('mapboxgl-popup');
//if (popUps[0]) popUps[0].remove();
// mapboxgl.Popup.remove();
if (map.getZoom() > 9) {
var popup = new mapboxgl.Popup({closeOnClick: false})
.setLngLat(currentFeature.geometry.coordinates)
.setHTML('<h3> Aeronave: '+ currentFeature.properties.dev_id + '</h3>' +
'<h4> Curso: ' + currentFeature.properties.curso + 'ยบ<br> Altitude: ' + currentFeature.properties.alt + ' ft<br> Sinal: ' + currentFeature.properties.rssi +'</h4>')
.addTo(map)
.setMaxWidth("fit-content(10px)");
} else{if (popUps[0]) popUps[0].remove()};
}
If I uncomment the popUps[0] line it will only allow 1 popup to be displayed, I've also tried to change dynamically the number between the [] by the number of active tracked devices, it reduced the popup number, but it still repeat some popups of one ID. Also I've tried to change the class name via the .addClasName but it didn't worked.
Thanks
Without seeing how you're calling the createPopUp method in the context of your application, it is difficult to diagnose exactly what is going wrong. I'm not sure why a new popup is being created each time the map moves, so it sounds like you might be calling this method within the Map#on('move') event. That being said, I'm assuming that you're iterating over all of your features and calling the createPopUp method for each in order to initialize all of the popups.
Rather than using an array of DOM nodes generated with var popUps = document.getElementsByClassName('mapboxgl-popup');, I'd recommend specifying a unique and reproducible class name for each feature's popup element when initialized. It looks like each of your features has a dev_id property, so you could do something like:
var popup = new mapboxgl.Popup({closeOnClick: false, className: `mapbox-gl-popup-${dev_id}`})
If you need to change the popup for a particular feature in the future, you can create a helper function to retrieve the relevant DOM node:
function retrievePopUp(feature) {
return document.getElementsByClassName(`mapboxgl-popup-${feature.properties.dev_id}`);
}

Algolia Instantsearch - add a callback after results are displayed?

I have an application that allows users to search for artwork and build their own portfolios. I'd like to show whether a piece of artwork that is returned in an algolia search is contained within the users portfolio, but I can't seem to find a way to make a callback once results are displayed to the user.
is it possible to add a callback to the search results?
I suppose you use instantsearch.js library for displaying the results.
Then there are two ways on how to achieve what you want.
1) Use transformData parameter of hits widget
The parameter takes callback and passes results there. So you can modify the results before it gets displayed - you can add a new parameter based on if the record is the one which should be highlighted.
Example:
search.addWidget(
instantsearch.widgets.hits({
container: '#hits-container',
templates: {
item: itemTemplate
},
transformData: {
allItems: function (results) {
console.log(results);
// Modify results
return results;
}
}
})
);
2) Use custom widget to hook a render callback
You can write a simple custom widget where you specify only the render option:
search.addWidget({
render: function(data) {
console.log(data);
}
});
In data parameter you'll get all information about the search, inluding helper, last state and the latest results.
Both approaches you can see live in small jsFiddle: https://jsfiddle.net/JanPetr/g54hzrzp/

Angular JS update model from dom

I've seen heaps of questions about this, and all of them seem to be solved by either calling $scope.$apply(), $scope.$digest(), or by triggering the change() method on the input. But I can't seem to get it working with any of those methods. In this fiddle, I can type a name into the box and get the model value to update as I type. But when I click the link, to set the input name to a certain value, I want the model name to update. What do I need to do?
The reason I'm trying to do this is I want to be able to refresh my angular model when a user autofills the form, using the browser autofill or LastPass or similar. Surely there's some angular command to refresh the model from the DOM?
http://jsfiddle.net/PXCUq/
$(function () {
$('#setFirstName').click(function () {
$('input.firstname').val('Test Name');
angular.element($('input.firstname')[0]).scope().$apply();
// Model still not updated
});
});
Get the scope, then change the ng-model property:
$('#setFirstName').click(function() {
var scope = angular.element($('input').get(0)).scope()
scope.firstname = 'Test Name';
scope.$apply();
});
You can come up with a better jQuery selector. I was only focusing on the Angular part.
Fiddle.

How to reference dynamically added element after DOM loaded without a need to act on any events?

I know there is .on and .live (deprecated) available from JQuery, but those assume you want to attach event handlers to one ore more events of the dynamically added element which I don't. I just need to reference it so I can access some of the attributes of it.
And to be more specific, there are multiple dynamic elements like this all with class="cluster" set and each with a different value for the: title attribute, top attribute, and left attribute.
None of these jquery options work:
var allClusters = $('.cluster');
var allClusters2 = $('#map').children('.cluster');
var allClusters3 = $('#map').find('.cluster');
Again, I don't want to attach any event handlers so .on doesn't seem like the right solution even if I were to hijack it, add a bogus event, a doNothing handler, and then just reference my attributes.
There's got to be a better solution. Any ideas?
UPDATE:
I mis-stated the title as I meant to say that the elements were dynamically added to the DOM, but not through JQuery. Title updated.
I figured it out. The elements weren't showing up because the DOM hadn't been updated yet.
I'm working with Google Maps and MarkerClustererPlus to give some more context, and when I add the map markers using markerclustererplus, they weren't available in the javascript code following the add.
Adding a google maps event listener to my google map fixed the problem:
google.maps.event.addListener(myMarkerClusterer, 'clusteringend', function () {
// access newly added DOM elements here
});
Once I add that listener, all the above JQuery selectors and/or methods work just fine:
var allClusters = $('.cluster');
var allClusters3 = $('#map').find('.cluster');
Although this one didn't, but that's because it only finds direct decendants of parent:
var allClusters2 = $('#map').children('.cluster');
Do what you need to do in the ajax callback:
$.ajax(...).done(function (html) {
//append here
allClusters = $('.cluster');
});
If you want them to be separate, you can always bind handlers after the fact, or use $.when:
jqxhr = $.ajax(...).done(function (html) { /* append html */ });
jqxhr.done(function () { allClusters = $('.cluster') });
$.when(jqxhr).done(function () { /* you get it */ });
If these are being appended without ajax changes, then just move the cluster-finding code to wherever the DOM changes take place.
If that's not an option, then I guess you would just have to check on an interval.

ASP.net MVC Set Checkboxes to checked Clientside

My situation is: Im making a simple inbox page. The inbox is a listing made from a DevExpress grid. Each row in the grid has a checkbox that the user can check so that they can multi delete records (similar to yahoo mail etc).
When the user clicks the select all link or the clear all link i need to set all the checkboxes within the grid to be checked or unchecked. How do I go about this with client-side scripting? Thanks
The easiest way to do this is to use jQuery. With the right selector it's pretty much a one liner. I don't know how much you know about jQuery so here's a link to the selector docs if you want to read up:
http://api.jquery.com/category/selectors/
The selector will depend on the layout of your page. I've done it before using something like this:
$("#tableId tr td input:checkbox").attr("checked", true);
In this example all checkboxes within a table with an id of "tableId" are checked
Using jquery it should be pretty easy- assuming you can use one of the selectors to select all of the checkboxes (take a look at the different jquery selectors http://api.jquery.com/category/selectors/).
Attach a toggle handler:
$('Selector for the "select all" checkbox>').toggle(function() {
alert('First handler for .toggle() called.');
}, function() {
alert('Second handler for .toggle() called.');
});
Select all checkboxes and when toggled switch the checked state of the other checkboxes:
$('<Selector for the ones you want to toggle>').attr('checked', true);
Provide some sample HTML, or a link to a page, if you need further help.
So putting it together, assuming your "select all" checkbox had an ID of "uxSelectAll" and the ones you want to change have a CSS class of "checkbox-mail-items" it would be something like:
$('#uxSelectAll').toggle(function() {
$('.checkbox-mail-items').attr('checked', true);
}, function() {
$('.checkbox-mail-items').attr('checked', false);
});
you can create a delegate (jquery) for all the checkboxes once you've done the answer above. with something like to perform an action for each check box:
$('div.myGridDivClass tbody').delegate(':checkbox', 'click', function(){
var $checkedRow = $(this), $row = $checkedRow.closest('tr')
// check row is checked
// toggleclass for checked css class and apply to the $row or whatever u want
// do something here
});