Comments (12)
Hi @michaelhjulskov I have a working solution ;)
To add or delete a marker, you need access to DjangoGooglePointFieldWidget
instance in your JavaScript. I opened an issue for that #62
I believe author will agree with me or he will give me better approach how to use it.
Next thing you need to do is to override template file which renders the map. In my case it was google-point-field-widget.html
. So go ahead and create a file with the same name in your app in the path templates/mapwidgets/
You can either paste the whole content there or just extend it like I do:
{% extends "mapwidgets/google-point-field-widget.html" %}
{% load i18n %}
{% block javascript %}
<script type="application/javascript">
(function($) {
var mapOptions = JSON.parse("{{ options|escapejs }}");
var field_value = JSON.parse("{{ field_value|escapejs }}");
var wrapElemSelector = "#{{ name }}-mw-wrap";
var mapElemID = "{{ name }}-map-elem";
var googleAutoInputID = "{{ name }}-mw-google-address-input";
var locationInputID = "#{{ id }}";
var mapWidgetOptions = {
locationInput: $(locationInputID),
wrapElemSelector: wrapElemSelector,
locationFieldValue: field_value,
mapElement: document.getElementById(mapElemID),
mapCenterLocationName: mapOptions.mapCenterLocationName,
mapCenterLocation: mapOptions.mapCenterLocation,
coordinatesOverlayToggleBtn: $(".mw-btn-coordinates", wrapElemSelector),
coordinatesOverlayDoneBtn: $(".mw-btn-coordinates-done", wrapElemSelector),
coordinatesOverlayInputs: $(".mw-overlay-input", wrapElemSelector),
coordinatesOverlay: $(".mw-coordinates-overlay", wrapElemSelector),
myLocationBtn: $(".mw-btn-my-location", wrapElemSelector),
addressAutoCompleteInput: document.getElementById(googleAutoInputID),
deleteBtn: $(".mw-btn-delete", wrapElemSelector),
addMarkerBtn: $(".mw-btn-add-marker", wrapElemSelector),
loaderOverlayElem: $(".mw-loader-overlay", wrapElemSelector),
zoom: mapOptions.zoom,
markerFitZoom: mapOptions.markerFitZoom,
GooglePlaceAutocompleteOptions: mapOptions.GooglePlaceAutocompleteOptions,
markerCreateTriggerNameSpace: "google_point_map_widget:marker_create",
markerChangeTriggerNameSpace: "google_point_map_widget:marker_change",
markerDeleteTriggerNameSpace: "google_point_map_widget:marker_delete",
placeChangedTriggerNameSpace: "google_point_map_widget:place_changed"
};
var widget = new DjangoGooglePointFieldWidget(mapWidgetOptions);
map_widget_autofill(widget);
{% block extra_javascript %}
{% endblock %}
})(jQuery || django.jQuery);
</script>
{% endblock javascript %}
Please notice these 2 changed lines:
var widget = new DjangoGooglePointFieldWidget(mapWidgetOptions);
map_widget_autofill(widget);
I haven't touched anything else in this file.
map_widget_autofill()
is my own function I have in my JavaScript file script.js
. I will describe it below.
You need to tell Django to load your static file in the admin.py
:
class MyModelAdminForm(forms.ModelForm):
class Meta:
model = MyModel
exclude = []
widgets = {
'point': GooglePointFieldWidget
}
@admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
form = MyModelAdminForm
...
class Media:
js = (
'js/script.js',
)
And finally script.js
:
(function($) {
map_widget_autofill = function (widget) {
var locationInput = widget.locationInput;
var form = $(locationInput).closest('form');
var street = form.find('#id_street');
var postcode = form.find('#id_postcode');
var city = form.find('#id_city');
var country = form.find('#id_country');
// if the location input isn't set, try to fetch GPS coordinates at document load
if (!locationInput.val()) {
update_map();
}
// update every time the text inputs change
$([street, postcode, city, country]).each(function(){
$(this).keyup(function() {
update_map();
});
});
// update every time the select changes
$(country).change(function() {
update_map();
});
function update_map() {
// join all address parts values into single string
var address = street.val() + ', ' + postcode.val() + ' ' + city.val() + ', ' + country.val();
// initialize autocomplete service API
var autocomplete_service = new google.maps.places.AutocompleteService();
// try to find address prediction using autocomplete service API
autocomplete_service.getPlacePredictions({input: address}, function (predictions, status) {
// if status is incorrect, clear search value
if (status != google.maps.places.PlacesServiceStatus.OK) {
$(widget.addressAutoCompleteInput).val('');
widget.deleteMarker();
} else if (predictions.length >= 1) {
// otherwise if there is at least 1 prediction available, pick the very first one
var address_by_prediction = predictions[0].description;
// set the address as search value
$(widget.addressAutoCompleteInput).val(address_by_prediction);
// try to find the GPS coordinates of the predicted address
widget.geocoder.geocode({'address' : address_by_prediction}, function(results, status) {
if (status === google.maps.GeocoderStatus.OK) {
// check the successful result
var geo_location = results[0].geometry.location;
var latitude = geo_location.lat();
var longitude = geo_location.lng();
// add marker to map
widget.addMarkerToMap(latitude, longitude);
widget.updateLocationInput(latitude, longitude);
// set center position (or fit bounds)
widget.map.setCenter({lat: latitude, lng: longitude});
// widget.fitBoundMarker();
// set zoom (change according your needs or use bounds if you wish)
widget.map.setZoom(15);
} else {
// geocoder couldn't find a GPS...
widget.deleteMarker();
console.warn("Cannot find " + address_by_prediction + " on google geo service.")
}
});
}
});
}
}
})(jQuery || django.jQuery);
It works perfectly for me. Hope it helps you.
from django-map-widgets.
By the way...
Here is Danish translation :)
#: mapwidgets/templates/mapwidgets/google-point-field-widget.html:8
msgid "Coordinates"
msgstr "Koordinater"
#: mapwidgets/templates/mapwidgets/google-point-field-widget.html:11
msgid "Latitude:"
msgstr "Breddegrad"
#: mapwidgets/templates/mapwidgets/google-point-field-widget.html:12
msgid "Ex: 41.015137"
msgstr "Eks: 41.015137"
#: mapwidgets/templates/mapwidgets/google-point-field-widget.html:15
msgid "Longitude:"
msgstr "Længdegrad"
#: mapwidgets/templates/mapwidgets/google-point-field-widget.html:16
msgid "Ex: 28.979530"
msgstr "Eks: 28.979530"
#: mapwidgets/templates/mapwidgets/google-point-field-widget.html:18
msgid "Done"
msgstr "Færdig"
#: mapwidgets/templates/mapwidgets/google-point-field-widget.html:22
msgid "Use My Location"
msgstr "Brug Min Nuværende Lokation"
#: mapwidgets/templates/mapwidgets/google-point-field-widget.html:25
msgid "Enter or update the address here"
msgstr "Skriv eller ret adresse her"
from django-map-widgets.
Did you check out the widget jQuery triggers? Sounds like you can do this with a trigger handler.
You can find an example here:
http://django-map-widgets.readthedocs.io/en/latest/widgets/point_field_map_widgets.html#javascript-triggers
Btw, would be great, if you can open a pull request for Danish translation. :)
Thanks.
from django-map-widgets.
thanks. maybe. i cant see how to use those triggers though.
regarding the "open a pull request for Danish translation."
i would love to, but im not that familiar with github so dont know how to do it. i tried the other day in another repo, but i didnt succeed. do i need to fork this repo to open a pull request? and then change files in my files? or ?
from django-map-widgets.
i combined some code i found and my own.
this way i will put the mapwidget before the address fields, and try to fill in the fields automatically for the customer.
//gets "street_number", "route", "locality", "country", "postal_code"
function getAddressComponents(components, type) {
for (var key in components) {
if (components.hasOwnProperty(key)) {
if (type == components[key].types[0]) {
return components[key];
}
}
}
return '';
}
$(document).on("google_point_map_widget:place_changed", function (e, place, lat, lng, locationInputElem, mapWrapID) {
street_number = " " + getAddressComponents(place.address_components, 'street_number').long_name
$("input[name=street]").val(getAddressComponents(place.address_components, 'route').long_name + street_number)
$("input[name=zipcode]").val(getAddressComponents(place.address_components, 'postal_code').long_name)
$("input[name=city]").val(getAddressComponents(place.address_components, 'locality').long_name)
$('select[name=country] option[value="' + getAddressComponents(place.address_components, 'country').short_name + '"]').prop('selected', true)
});
from django-map-widgets.
here's my current approach.
My first two fields is company name and city.
When these fields is filled, we copy it to the mapwidget search field and if google api have 1 suggestion, lets assume its correct.
(almost working - I only need to find a way to trigger the mapwidget to set the point - help is much apreciated)
//gets "street_number", "route", "locality", "country", "postal_code"
function getAddressComponents(components, type) {
for (var key in components) {
if (components.hasOwnProperty(key)) {
if (type == components[key].types[0]) {
return components[key];
}
}
}
return false;
}
$(document).on("google_point_map_widget:place_changed", function (e, place, lat, lng, locationInputElem, mapWrapID) {
console.log(place);
console.log(locationInputElem); // django widget textarea widget (hidden)
console.log(lat, lng); // created marker coordinates
console.log(mapWrapID); // map widget wrapper element ID
street_number = getAddressComponents(place.address_components, 'street_number');
route = getAddressComponents(place.address_components, 'route');
if (route && street_number){$("#id_street").val(route.long_name + " " + street_number.long_name);}
else if (route){$("#id_street").val(route.long_name);}
postal_code = getAddressComponents(place.address_components, 'postal_code');
if (postal_code){$("#id_zipcode").val(postal_code.long_name);}
locality = getAddressComponents(place.address_components, 'locality');
if (locality){$("#id_city").val(locality.long_name);}
country = getAddressComponents(place.address_components, 'country');
if (country){$('#id_country option[value="' + country.short_name + '"]').prop('selected', true);}
});
function autoCallback(predictions, status) {
if (status != google.maps.places.PlacesServiceStatus.OK) {
$("#location-mw-google-address-input").val('');
return;
}
if (predictions.length == 1){$("#location-mw-google-address-input").val(predictions[0].description);}
}
function queryAutocomplete(input) {// *Uses Google's autocomplete service to select an address
var service = new google.maps.places.AutocompleteService();
service.getPlacePredictions({input: input}, autoCallback);
}
$(document).on("change", "#id_name, #id_city", function(event){
if ($("#id_name").val() && $("#id_city").val()){
queryAutocomplete( $("#id_name").val() + ", " + $("#id_city").val() );
}
});
as soon the point is set on the map, it will autofill the other address fields.
from django-map-widgets.
Hi guys. I would like to achieve a similar thing. I have 1 point field and 2 text inputs (street and city). I would like to achieve the following:
- once the user enters something into street and city input fields, the concatenated value will be passed into Google Autocomplete input of the point field
- trigger search
- then the first available result will be automatically selected
- map marker will change according to the result
So far by now, I succeeded with the 1st step:
var map_input = $('#point-mw-google-address-input');
var street = $('#id_street');
var city = $('#id_city');
$('#id_street, #id_city').keyup(function() {
var address = street.val() + ', ' + city.val();
map_input.val(address);
});
but I am not able to figure out how to trigger the autocomplete search and add marker automatically.
I tried google.maps.event.trigger(map_input, 'place_changed');
but it didn't work.
Can you please help me? Which triggers should I use? Thank you very much in advance.
from django-map-widgets.
Eric take a look at my form and maybe my code can be part of the solution. But I got stuck couldn't find a way to call the map so I had to reload the map which doesn't work correctly. See the other comment I made a few minutes ago
from django-map-widgets.
Great Great Great finally breakthrough
I will give it a punch one of the next days.
Thanks for sharing your approach :-)
from django-map-widgets.
I couldnt override the template :C
no matter if I create a new one templates/mapwidgets/google-point-field-widget.html
in my project folder or if I change template_name = "mapwidgets/my_template.html"
in my own widget
class GooglePointFieldWidgetJQuery(GooglePointFieldWidget):
template_name = "mapwidgets/my_template.html"
Django never look in my defined templates dir (altough it works with custom admin templates)
Template-loader postmortem
Django tried loading these templates, in this order:
Using engine django:
django.template.loaders.filesystem.Loader: /home/monadical/.local/share/virtualenvs/pennybags-N4zD9cT2/lib/python3.7/site-packages/django/forms/templates/mapwidgets/my_template.html (Source does not exist)
django.template.loaders.app_directories.Loader: /home/monadical/.local/share/virtualenvs/pennybags-N4zD9cT2/lib/python3.7/site-packages/django/contrib/admin/templates/mapwidgets/my_template.html (Source does not exist)
django.template.loaders.app_directories.Loader: /home/monadical/.local/share/virtualenvs/pennybags-N4zD9cT2/lib/python3.7/site-packages/django/contrib/auth/templates/mapwidgets/my_template.html (Source does not exist)
django.template.loaders.app_directories.Loader: /home/monadical/.local/share/virtualenvs/pennybags-N4zD9cT2/lib/python3.7/site-packages/graphene_django/templates/mapwidgets/my_template.html (Source does not exist)
django.template.loaders.app_directories.Loader: /home/monadical/.local/share/virtualenvs/pennybags-N4zD9cT2/lib/python3.7/site-packages/mapwidgets/templates/mapwidgets/my_template.html (Source does not exist)
django.template.loaders.app_directories.Loader: /home/monadical/.local/share/virtualenvs/pennybags-N4zD9cT2/lib/python3.7/site-packages/bootstrap4/templates/mapwidgets/my_template.html (Source does not exist)
from django-map-widgets.
@jeasoft This could be related with your template dirs settings. Your class implementation should be fine for template overriding.
https://docs.djangoproject.com/en/2.2/ref/settings/#dirs
from django-map-widgets.
ok, i know is more a django issue but thanks for the reply :)
this is my TEMPLATE
setting:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [TEMPLATES_DIR],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
with TEMPLATES_DIR = os.path.join(BASE_DIR, 'templates')
which translates to '/home/monadical/pennybags/pennydjango/templates'. And is in that templates
folder where I create another folder mapwidgets
and put the template my_template.html
.
Anyway, I found that I could use javascript triggers to catch the place_changed
event (which is not explict in docs) and with that I could grab the autocompleted address. That was what I needed in first place.
Thanks for the help, is a cool widget 😸
from django-map-widgets.
Related Issues (20)
- cannot import name six HOT 5
- Detail page stuck
- Add delay for autocomplete HOT 1
- Support of polygon fields HOT 1
- map image not found HOT 1
- This project support Django3.x? HOT 3
- place.geometry can be undefined in handleAutoCompletePlaceChange
- Add documentation for Google APIs that have to be enabled HOT 5
- missing six requirement
- how can I change the template of form column? HOT 1
- After loading {{form.media}} javascripts in the page stop working HOT 3
- docker-compose seems broken HOT 2
- Undefined geocoding response object "place" when triggering "google_point_map_widget:marker_change" HOT 3
- .ugettext_lazy() is deprecated in django 4.0 HOT 1
- Form field errors are not shown HOT 1
- The map doesn't appear and ScrollWheel zooming settings HOT 4
- Invalid JSON error reported whenever a `PointField` is saved with the `GooglePointFieldWidget` in the Django admin
- How to make a map widget read-only? HOT 1
- How to set the default MapTypeId to satellite? HOT 1
- GooglePointFieldInlineWidget doesn't work with Django 4.2.2 HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from django-map-widgets.