I'm having issues setting the visibility of custom Google Maps API v3 overlays. I have a gmaps API page with various overlay, such as polygons, polylines and symbols. Each has an associated text label, made using a custom overlay I adapted from the answer to [this Stack Overflow post][1]
On page load, the actual overlays (polylines, polygons, markers etc - the built in API objects) work correctly. They are displayed based on the default checkbox states. However, the labels are all displayed, regardless if their check box was set by default. If I cycle the checkboxes, everything works correctly.
The overlays are stored as an object called 'overlays' with layout 'description: [polyline, customoverlaylabel]'
Checkbox example code:
<input type="checkbox" id="sun" onclick="refreshCheck('sun')">Sun</input>
This is how I sync whether a display is hidden or visible to the checkbox:
function refreshCheck(overlay) {
var box = document.getElementById(overlay).checked
var lines = overlays[overlay][0]
var text = overlays[overlay][1]
lines.setVisible(box, overlay)
if (box === true) {
text.show()
}
else {
text.hide()
}
}
This code refreshes all the checkmarks, at the end of the javascript head.
var overlayNames = []
for (var k in overlays) overlayNames.push(k)
for (var o in overlayNames) refreshCheck(overlayNames[o])
Here's the hide method of the custom text overlay:
TxtOverlay.prototype.hide = function(){
if (this.div_) {
this.div_.style.visibility = "hidden";
}
}
It's failing the if (this.div_) check, and not doing anything. If I remove the check, it produces an error since this.div_ doesn't exist.
One way to work around it would be to automatically cycle all checkbox states once the page loads (manually doing to solves it). There might be a more fundamental fix. No matter where I attempt to .hide() or .show() a label in my javascript, it doesn't work - it only works when referenced by the checkbox being clicked.
The issue is the moment when you call refreshChecks() .
You assume that the TxtOverlay's already have been added at this time, but that's not the case(that's why the div_'s are still null).
The TXToverlay's (like any object/shape) on a map will be added when the projection of the map is ready.
A possible approach would be:
Instead of using the visible-property of the shapes/markers/etc. to toggle their visibility use the map-property.
The TXTOverlay's are also MVCObject's, you only have to bind the map-property of the TXTOverlay's to the map-property of the related shape.
Related
The Problem:
Before I began adding the div swaps, I could only type into the left (from_value) input and the result of the calculations would be applied via ajax to the right (to_value) input.
I would like to allow the user to type into either box and have the results display in the opposite box they're typing in.
What I am doing to make this happen:
I am swapping the left div with the right div on mouseover of the to_value input. Here's the code i'm using to do the swap:
$.fn.swapWith = function (that) {
var $this = this;
var $that = $(that);
// create temporary placeholder
var $temp = $("<div>");
// 3-step swap
$this.before($temp);
$that.before($this);
$temp.after($that).remove();
return $this;
};
var leftSide = $('#left-side');
var rightSide = $('#right-side');
$('#to_value_input').on('mouseover', function () {
$(rightSide).swapWith(leftSide);
});
This effectively swaps the divs, bringing along with it ids and names of the inputs which retains functionality of my server-side script to perform calculations. As expected, the from_unit select list and to_unit select list are swapped and their values / displayed text are also swapped from one side to the other.
I would like to swap the values and text of the two select boxes either directly before or more likely, directly after the div swap so it appears as if nothing changed on screen.
Similar questions that I have reviewed:
How to swap values in select lists with jquery?
How to swap selected option in 2 select elements?
I have tried several variations of each of the provided solutions. This seems like it should be a fairly simple thing to do but I find myself stuck. Any further help would be greatly appreciated.
The full code base is available on github if you need to see it: https://github.com/pschudar/measurement-conversion
I have made minor changes to the code hosted there to accommodate the div swaps.
I'm using leaflet-groupedlayercontrol to add my grouped layers to the map and I have a problem:
All the layers are deselected, but I want to select all by default. I am using checkboxes, not radio buttons.
var groupedOverlays = {
"Select": {}
};
groupedOverlays["Select"]["Group 1"] = groups[0];
groupedOverlays["Select"]["Group 2"] = groups[1];
groupedOverlays["Select"]["Group 3"] = groups[2];
// Use the custom grouped layer control, not "L.control.layers"
L.control.groupedLayers(null, groupedOverlays, {collapsed:false}).addTo(map);
I tried to select them with JS, but didn't worked.
If you know a solution for LeafletJS, but not for that particular plugin, it's ok too.
Whether a Leaflet layer is ""selected"" or not in the built-in L.Control.Layers depends on whether the layer is added to the map or not.
e.g. this will display a L.Control.Layers with the checkbox off:
L.control.layers({}, {
Foo: L.marker([0,0])
}).addTo(map)
...while this will display it with the checkbox on:
L.control.layers({}, {
Foo: L.marker([0,0]).addTo(map)
}).addTo(map)
I expect the behaviour of the GroupedLayers control to be similar. Just double-check whether you add the layers to the map or not. Also notice that the checkboxes' state updates whenever layers are added/removed to/from the map by any means, at any time.
I have a kendo grid with custom popup edit window to imitate popup edit, but with batch edit. Everything works fine, but I am experiencing a small issue. Whenever value is changed, the grid cell does not have that red triangle thingy in the corner indicating that this particular value different from original.
As I understand in this post, manually made changes in datasource does not appear on the grid, so I have to add them manually.
This post 'manually maintain dirty cell marker on paging in Kendo grid' gives an idea how to get it working. I could attach some listeners to kendoWindow inputs, track what fields are being edited, compare old and new values...
But is there a less painful way to achieve this functionality? Maybe there is some built in kendo function to achieve that?
Here's a small working example http://dojo.telerik.com/aSaXe/4
The red "dirty" marks appear automatically only when the built-in in-cell editing is used. From this point of view, your scenario requires these to be added manually after the custom editing popup is closed.
You may find the change event of the data item useful in the task. It will be fired each time a value in the popup is changed and the respective textbox is blurred.
var uid = $(e.target).parents('tr').attr('data-uid');
var grid = $('#grid').data("kendoGrid");
var dataItem = grid.dataSource.getByUid(uid);
dataItem.bind("change", function(args) {
// args.field
});
Finally, keep in mind that each change in the Grid dataSource causes the whole table to be redrawn (unless the built-in in-cell editing is used), so you will lose any previously applied custom styling.
You can use the save event on your kendo grid as:
save: function (e) {
addDirtyUid(e.model.uid);
setTimeout(refreshVisualDirtyElements, 100);
}
Other functions and var:
var dirtyIds = [];
function addDirtyUid(currentUid) {
if (dirtyIds.indexOf(currentUid) === -1) {
dirtyIds.push(currentUid);
}
}
function refreshVisualDirtyElements() {
for (var i = 0; i < dirtyIds.length; i++) {
var thisUid = dirtyIds[i];
$("tr[data-uid='" + thisUid + "']").find("td:eq(0)").addClass("k-dirty-cell");
$("tr[data-uid='" + thisUid + "']").find("td:eq(0)").prepend('<span class="k-dirty"></span>');
}
}
Is it possible to control (enable/disable) Google Places Autocomplete SearchBox (google.maps.places.SearchBox) service predictions?
Or in other words: is it possible to temporarily detach HTML input element from Autocomplete SearchBox service and then reattach it?
The thing is that I display service results just bellow HTML input element attached to SearchBox service. Problem is that after results are displayed and user focuses back on the input element, predictions are displayed over results and obscure their view. I would like to disable predictions until text in input element is changed by the user.
EDIT 26/Aug/2016:
Disable predictions is currently not supported by the Javascript API. Therefore I have just opened a feature request on Google. If you are interested in the feature please vote for it: Autocomplete SearchBox - Control (enable/disable) predictions..
EDIT 07/Sep/2016 - bounty award update:
Thanks to all of you who participated in answering and also in promoting the question.
Primary objective of the award was to find solution using currently available means. I am afraid this did not happen so I decided not to award the bounty.
Although none of answers bellow provides a solution, each provides some sort of lead, so thank you! Maybe those leads will point to a solution in future.
Secondary objective of the award (although not communicated directly) was to promote Autocomplete SearchBox - Control (enable/disable) predictions feature request. Its status changed to NeatIdea and has been assigned internal tracking number. It is a good sign.
What you can do is, After the user selects the place, you can add a class disabled to that input field.., This will help you enable/disable predictions based on class name.
And where you have the autocomplete code, you can wrap it within if else statement.
let field = document.getElementById('location');
if ( field.className.indexOf('disabled') > -1 ) {
google.maps.event.clearInstanceListeners(field);
}
else {
let autocomplete = new google.maps.places.Autocomplete( field, {types: ['geocode']} );
autocomplete.addListener('place_changed', () => {
let place = autocomplete.getPlace();
let filed_val = field.value; // incase you need it
field.classList.add('disabled');
});
}
This will remove the autocomplete after user selects a place.. and later if you want, you can remove the disabled class from this field and it will work again.
My solution in AngularJS – it is extract from a directive.
.pac-contained is created after instance of an Autocomplete service is created, e.g.: new google.maps.places.Autocomplete(…) or new google.maps.places.SearchBox(…).
What I do is to find just created .pac-container in the document, store its reference and mark that container as already processes (by adding an arbitrary class .predictions-control on it). "Marking" the container is needed only when more than one .pac-container is expected to be present in application.
Now with the reference I can control visibility (hide or show) of the .pac-contained with predictions.
// Container element with predictions.
var pacContainer = null;
/***
* Find predictions container without predictions-control class set.
* Then set predictions-control class to it and convert it into
* Angular's jqLite object.
* #return {jqLite object} - container or null when not found.
*/
function getPredictionsContainer() {
// Get div.pac-container without predictions-control class.
var e = document.querySelector('div.pac-container:not(.predictions-control)');
if (e){
var container = angular.element(e);
container.addClass('predictions-control');
console.log('predictions-control: Container found.');
return container;
} else {
console.warn('predictions-control: Container not found!');
}
return null;
} // getPredictionsContainer
/***
* Loop in 50ms intervals until container is found.
*/
function untilContainerFound(){
pacContainer = getPredictionsContainer();
if (pacContainer == null){
$timeout(untilContainerFound, 50);
}
} // untilContainerFound
this.init = function() {
untilContainerFound();
}; // this.init
/***
* Prevent predictions to be displayed when user clicks on the
* input element. It is achieved by adding ng-hide CSS class to
* predictions container. Predictions container is identified by
* ".pac-container" CSS class selector.
*/
this.hidePredictions = function() {
// If predictions container was not found at directive
// initialization try to find it now.
if (pacContainer === null){
pacContainer = getPredictionsContainer();
}
if (pacContainer){
console.log('predictions-control: Hiding predictions.');
pacContainer.addClass('ng-hide');
} else {
console.warn('predictions-control: Container not found!');
}
}; // this.hidePredictions
/***
* Show predictions again by removing ng-hide CSS class from
* predictions container.
*/
this.showPredictions = function() {
console.log('predictions-control: Showing predictions.');
if (pacContainer){
pacContainer.removeClass('ng-hide');
}
}; // this.showPredictions
Call init() right after service instance is created:
// Create SearchBox service for auto completing search terms.
autocomplete = new google.maps.places.SearchBox( inputElem[0] );
// OR
// autocomplete = new google.maps.places.Autocomplete( ..... );
autocomplete .addListener('places_changed', callback);
predictionsCtrl.init();
Note:
As long as it is guaranteed that two Autocomplete services are not created at the same time (e.g.: each service is on different tab) or can wait with creation of next service until .pac-container for previous service is found, it reliably works even with multiple instances of Autocomplete service.
There is no way, or much point in having one: predictions are the whole point of SearchBox, its reason to be. If you don't want predictions, you can just use Text Search in the Places library.
If the user clicks/focuses again on the search box, s/he probably didn't care for the results that get obscured by the suggestions. The same behavior is in Google Maps, and it's not an issue, is it?
If you can't put some space between the SearchBox and results (like in this tool), and you absolutely must disable suggestions for a moment, I'd say you can destroy the google.maps.places.SearchBox object and create a new one later, attached to the same HTML input element.
Possibly valuable information.
This is relevant around API V3.29 (not sure if it will always be accurate).
The div element that the API creates for the autocomplete has a class of "pac-container pac-logo".
Utilizing document.querySelector('.pac-container') you may be able to set it's style attribute to display: none on a click event elsewhere.
NOTE: When your users click back in the searchBox google will change the style attribute back to whatever is appropriate, so you only have to set it once, you shouldn't have to set it back again.
(this may be easier and cleaner than getting angular involved).
Hope that helps someone (I had to add a CSS rule to increase the z-index in an application to make the autocomplete show up)
If you add the disabled attributed to the textbox this will disable predictions.
Using readonly attribute does not.
This may help in some circumstances.
I'm working on a map project where I built my own UI. Part of it is a navigation where you can filter the map by clicking a div button in a column floating at the edge of the map. It works perfectly fine, currently, but it is overly complicated and will be a major issue to explain how to add new buttons as the data expands.
The gist is that it first defines a variable as the button id, and on click resets all the classes to a default set, except for the button which is clicked gains the class 'active', and executes a function which sets filtering based on an assigned id in the dataset or return true for All.
The way I have it, which is functional but extremely cluttered
Edit: I've made a few changes that substantially reduce the lines, but I still don't know how to combine multiple similar copies of these into a single function
//define variable to point to a specific button
var navigation = document.getElementById('navigation').children;
var all = document.getElementById('filter-all');
....
//[Currently] nine additional variables of nearly the same thing (now just for selecting the button pressed)
//Show all
all.onclick = function() {
navigation.className = navigation.className.replace(new RegExp('\b' + active + '\b'),'');
this.className += ' active'; //for some reason this doesn't actually add the space
map.featureLayer.setFilter(function(f) {
// Returning true for all markers shows everything.
return true;
});
map.fitBounds(map.featureLayer.getBounds());
return false;
};
....
//Followed by nine nearly identical functions that just reset classes, and sets filtering parameters
I know there is a way to combine the functions to do the same thing without so much bulk, but my search is so far fruitless.