Using Declarative Bindings with API Data - javascript

I'm creating a tool which displays nearby points of interests on a map. When a marker is clicked the map should display a list of WikiPedia articles relating to that landmark.
I'm wondering if anyone can point me in the right direction as far as getting ko.observables working with my API return. I would like to avoid appending everything to the page like I am right now, I also believe it's causing some issues with my info windows not closing properly when another marker is clicked.
Some of the things I've tried was changing the $wikiData.append to an observable and then changing the contentString variable to include the data-bindings, but alas it did not work out.
The API I'm using is the WikiPedia API. Here is the code:
function mapPin(name, lat, long, text) {
this.name = ko.observable(name);
this.lat = ko.observable(lat);
this.long = ko.observable(long);
this.text = ko.observable(text);
var marker = new google.maps.Marker({
position: new google.maps.LatLng(lat, long),
map: mapView,
animation: google.maps.Animation.DROP
});
function toggleBounce() {
if (marker.getAnimation() != null) {
marker.setAnimation(null);
} else {
marker.setAnimation(google.maps.Animation.BOUNCE);
}
}
function article(content, url) {
var self = this;
self.content = content;
self.url = url;
}
function apiData() {
var wikipediaURL = 'http://en.wikipedia.org/w/api.php?action=opensearch&search=' + name + '&format=json&callback=wikiCallback';
var wikiRequestTimeout = setTimeout(function () {
$wikiData.text ("Failed to get Wikipedia resources");
}, 5000);
$.ajax({
url: wikipediaURL,
dataType: "jsonp",
success: function (response) {
viewModel.articleList.removeAll();
var articleList = response[1];
for (var i = 0; i < articleList.length; i++) {
articleStr = articleList[i];
var url = 'http://en.wikipedia.org/wiki/' + articleStr;
viewModel.articleList.push(new article(articleStr, url));
}
clearTimeout(wikiRequestTimeout);
}
});
}
var contentString = '<!-- ko foreach: articleList --><li><a data-bind="attr: {href: url}, text: content"></a></li>';
var infowindow = new google.maps.InfoWindow({});
google.maps.event.addListener(mapView, 'click', function () {
infowindow.close();
});
google.maps.event.addListener(marker, 'click', function () {
infowindow.close();
toggleBounce();
infowindow = new google.maps.InfoWindow({
content: text + contentString
});
infowindow.open(mapView, marker);
apiData();
});
}
var mapView = new google.maps.Map(document.getElementById('map-canvas'), {
zoom: 12,
center: new google.maps.LatLng(61.196148, -149.885577),
});
var viewModel = {
articleList: ko.observableArray([]),
pins: ko.observableArray([
new mapPin("Alaska Communications", 61.196148, -149.885577, "test11"),
new mapPin("Moose's Tooth", 61.190491, -149.868937, "test2")
]),
// TODO
query: ko.observable(''),
search: function (value) {
viewModel.pins[0].removeAll();
for (var i in pins) {
if (pins[i].name.toLowerCase().indexOf(valkue.toLowerCase()) >= 0) {
this.pins.push(pins[i])
}
}
}
};
// Initiates the viewModel bindings.
ko.applyBindings(viewModel);
You can find a working version of the site here:
http://jamesiv.es/projects/map

Your articleList should be an observableArray in your view model with a binding in the view for that.
var viewModel = {
articleList: ko.observableArray([]),
pins: ko.observableArray([
new mapPin("Alaska Communications", 61.196148, -149.885577, "test11"),
new mapPin("Moose's Tooth", 61.190491, -149.868937, "test2")
]),
};
First make a little DTO:
function article(content, url) {
var self = this;
self.content = content;
self.url = url;
}
When you get the data back from our ajax call, first clear the array:
viewModel.articleList.removeAll();
Then just loop through and do something like this:
viewModel.articleList.push(new article(articleStr, url));
Do the formatting in your view with a binding like this:
<!-- ko foreach: articleList -->
<li><a data-bind="attr: {href: url}, text: content"></a></li>
<!-- /ko -->

Related

Call function for content of infowindow when opening not only on init

I have some objects with markers. These objects have dynamic data and I would like to output them in the infowindow of the marker of the object.
So far this is what I have:
function createRandomObjectWithAMarker() {
var marker;
var aData = "dataToDisplay";
var infowindow = new google.maps.InfoWindow({
content:callThisFunctionWhenOpenWindow(aData)
});
marker.addListener('click', function() {
infowindow.open(map, marker);
});
randomObject = {
/*
someData
*/
marker: marker
};
return randomObject;
}
And I would like that this function to be called when I click the marker to show the return modifiedData as the content of the infoWindow.
function callThisFunctionWhenOpenWindow(aData){
/*
do some stuff on aData
*/
return modifiedData;
}
But actually, this function is called once: only when I init the randomObject with the call of createRandomObjectWithAMarker(). So if I show the infoWindow after some time, when the datas would not be the same as when the script starter, it will still display the same output.
Is there any way to do that? If yes how?
Try something like this?
This way the infowindow (and it's data) is only created when you click the marker
function createRandomObjectWithAMarker() {
var marker;
var aData = "dataToDisplay";
marker.addListener('click', function() {
var infowindow = new google.maps.InfoWindow({
content:callThisFunctionWhenOpenWindow(aData)
});
infowindow.open(map, marker);
});
randomObject = {
/*
someData
*/
marker: marker
};
return randomObject;
}

Mapbox handle multiple GEOJSON files with loadURL

I'm currently working on a map that's meant to load multiple layers from different sources based on a config.json file.
Each layer should also display a popup on click but for some reason I only get the popup for the last loaded layer.
I'm using the ready event on the layers to make sure all the data gets loaded and iterating through them using .eachLayer method before binding the popup but still no success and can't figure out what am I missing.
Please find below my code as well a reproduction on: plnkr.co
var myMap = function(options) {
var self = this;
this.settings = $.extend({
layersConfig: 'config.json',
layerData: 'layer',
accessToken: 'pk.eyJ1IjoibWF0dGJsaXNzIiwiYSI6ImNpb3dwczBwZjAwOTh3OWtqOWZ1aG5ob3gifQ.Ot6GdtKew9u27TROm_4A6Q'
}, options);
this.map;
this.layers;
$.ajax({
url: this.settings.layersConfig,
cache: true
}).done(function(data) {
self.init(data);
});
};
myMap.prototype = {
init: function(data) {
var self = this,
settings = this.settings;
L.mapbox.accessToken = settings.accessToken;
var map = this.map = L.mapbox.map('map', 'mapbox.streets')
.setView([54.6, -2.3], 4);
var popup = new L.Popup({
minWidth: 250
});
for (var i = 0; i < data.length; i++) {
var featureLayers = this.layers = L.mapbox.featureLayer(null, {
style: {
weight: 2,
color: data[i].color,
fillColor: data[i].color,
fillOpacity: 0.4
}
}).addTo(map);
// load layers data
featureLayers.loadURL(settings.layerData + data[i].layerId + '.json')
.on('ready', function(e) {
featureLayers.eachLayer(function(layer) {
// cache layer properties
var layerProps = layer.feature.properties;
// cache feature bounds
var bounds = layer.getBounds().toBBoxString();
// bind modal
layer.bindPopup(showPopup(layer, bounds));
});
});
}
map.on('popupopen', function() {
$('.zoom-to').on('click', function() {
var array = $(this).data('zoom').split(',');
map.fitBounds([
[array[1], array[0]],
[array[3], array[2]]
])
});
});
function showPopup(popup, bounds) {
var popupData = popup.feature.properties;
var popupLabel = popupData.NAME;
var popupStructure = '<div class="leaflet-popup-label">' + popupLabel + '</div><button class="zoom-to" data-zoom="' + bounds + '">Zoom to</button></div>'
return popupStructure;
}
}
}
var map = new myMap();
.on('ready',...)
^ Has nothing to do with an AJAX call.
You need to perform actions after the ajax call is finished, that is, inside the AJAX callback.
Here:
}).done(function(data) {
/* do stuff */
});
Found the issue.
Just replace featureLayers.eachLayer with e.target.eachLayer and the popup will show as desired.

Google Map AddListener issue

I have written following code.. which fires Add Listener event on Place_change but i want to fire that event on page load.. which is copied from google and i have made some changes.
function _initMap(latlng){
// google map
$map = $(".lwizard-step1-map");
_latlng = latlng || new google.maps.LatLng(41.3833333,2.1833333);
if (!_STEP1_LOCATION_MAP) {
_STEP1_LOCATION_MAP = new google.maps.Map($map[0],{
center: _latlng,
zoom:11,
mapTypeId:google.maps.MapTypeId.ROADMAP,
streetViewControl:false,
scrollwheel:false
});
} else {
_STEP1_LOCATION_MAP.setCenter(_latlng);
}
if (!_STEP1_LOCATION_MARKER) {
_STEP1_LOCATION_MARKER = new google.maps.Marker({
position: _latlng,
map: _STEP1_LOCATION_MAP,
draggable: true
});
google.maps.event.addListener(_STEP1_LOCATION_MARKER, "dragend", function(event) {
var lat = event.latLng.lat();
var lng = event.latLng.lng();
$("#lwizard-step1-location-autocomplete").val('');
STEP1_PLACE.latLng = [lat,lng];
STEP1_PLACE.address = null;
$.ajax({
url: 'http://maps.googleapis.com/maps/api/geocode/json?latlng='+lat+','+lng+'&sensor=false',
success: function(response){
if ((response) && (response.results) && (response.results.length)){
$(".lwizard-step1-chosen-address").parent().show();
var $address = $(".lwizard-step1-chosen-address");
$address.text(response.results[0].formatted_address);
$("#lwizard-step1-adjust-onmap").data("location", response.results[0].geometry.location);
STEP1_PLACE.address = nc.utils.convertGoogleAddressComponents(response.results[0].address_components);
}
}
})
});
} else {
_STEP1_LOCATION_MARKER.setPosition(_latlng);
}
}
// autocomplete
$input = $("#lwizard-step1-location-autocomplete");
_STEP1_LOCATION_GA = new google.maps.places.Autocomplete($input[0],{ types: [] });
google.maps.event.addListener(_STEP1_LOCATION_GA, 'place_changed', function () {
var place = _STEP1_LOCATION_GA.getPlace();
console.log(place)
var $where = $(".lwizard-step1-chosen-address");
var found = ((place) && (place.formatted_address) && (place.geometry) && (place.id));
if (place && found) {
$("#lwizard-step1-adjust-onmap").data("location", place.geometry.location);
STEP1_PLACE.address = nc.utils.convertGoogleAddressComponents(place.address_components);
STEP1_PLACE.latLng = [place.geometry.location.lat(),place.geometry.location.lng()];
STEP1_PLACE.location = place.geometry.location;
$where.parent().show();
$where.text(place.formatted_address);
$("#lwizard-step1-right-1").show();
$("#lwizard-step1-right-2").hide();
_initMap(place.geometry.location);
} else {
$where.parent().hide();
$("#lwizard-step1-right-1").show();
$("#lwizard-step1-right-2").hide();
_initMap();
}
});
I have one textbox named lwizard-step1-location-autocomplete which shows value from session named SessionLocation on page load.
but i also want to show the map on page load of the location specified in textbox.
But the problem is the addlistener fires only on textbox change means Place_changed event.
pls. Give some suggestions or new method for it.
You can just call change event on load and than it will fired.
$('#lwizard-step1-location-autocomplete').trigger("change");
Or with js;
document.getElementById('lwizard-step1-location-autocomplete').fireEvent("onchange");

Google Maps: load markers from JSON

I have the following JS which processes a form, returns the results as JSON and then should plot the json as markers on the map. Although I do get a json response, nothing is plotted. I'm not very familiar with Javascript, so could someone let me know what the error is below?
Thanks
The JSON:
[
{
"lat": "53.598660370500596",
"lng": "-113.59166319101564"
}
]
The Javascript
$(function () {
$('#search').click(function () {
var projectNumber = $('#project_number').val();
var setdistance = $('#setdistance').val();
var companyType = $('#company_type').val();
//syntax - $.post('filename', {data}, function(response){});
$.post('business_location_get_map.php', {
project_number: projectNumber,
setdistance: setdistance,
company_type: companyType
}, function (ret) {
$('#result').html(ret);
});
});
});
function initialize() {
var mapOptions = {
center: new google.maps.LatLng(53.5435640000, -113.5),
zoom: 8
};
var map = new google.maps.Map(document.getElementById("map_canvas"),
mapOptions);
}
google.maps.event.addDomListener(window, 'load', initialize);
function plot() {
$.getJSON('business_location_get_map.php', function (data) {
var location;
$.each(data, function (key, val) {
addMarker(key.lat, key.lng);
});
});
}
function addMarker(lat, lng) {
marker = new google.maps.Marker({
position: new google.maps.LatLng(lat, lng),
map: map,
icon: redImage
});
markersArray.push(marker);
}
google.maps.event.addDomListener('search', 'click', plot);
EDIT:
I've changed
(key.lat, key.lng)
to
(val.lat, val.lng)
I still have the same results,
The problem is about $.each()
You are passing key.lat and key.lng to addMarker(), however key is the index of the current element in array. You must use val as it's the real value:
$.each(data, function (key, val) {
addMarker(val.lat, val.lng);
});
More info about the usage of $.each(): https://api.jquery.com/each/

AJAX call in an Infowindow: Scope Issue

Or at least I believe it's a scope issue, correct me if I'm wrong.
I have a for loop that generates markers on my map. Each infowindow loads different content using callbacks to an ajax function.
I've simplified this sample to outline the problem.
var xhr = "";
var infowindow = new google.maps.InfoWindow();
var marker, i;
var polylineCoordinates = [new google.maps.LatLng(78.782762, 17.917843),
new google.maps.LatLng(-0.829439, -91.112473),
new google.maps.LatLng(15.066156, -23.621399),
]
function createHttpRequest() {
try {
xhr = new XMLHttpRequest();
return xhr;
}
catch (e)
{
//assume IE6
try {
xhr = new activeXBbject("microsoft.XMLHTTP");
return xhr;
}
catch (e) {
return alert("Unable to create an XMLHttpRequest object");
}
}
}
function initialize() {
var mapOptions = {
center: new google.maps.LatLng(78.782762,17.917843),
zoom: 10,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"),
mapOptions);
}
//I recreated the polylineCoordinates array (see above)
//to try and replicate and real array in the script
for (i = 0; i < polylineCoordinates.length; i++) {
marker = new google.maps.Marker({
position: polylineCoordinates[i],
map: map
});
google.maps.event.addListener(marker, 'click', (function (marker, i) {
return function () {
infowindow.setContent("<div id=\"infowindow\">" + getStationInfo(infoWindowDiv) + "</div>");
infowindow.open(map, marker);
}
})(marker, i));
} //End adding markers loop
function infoWindowDiv(stationInfo) {
var add = document.createTextNode(stationInfo);
document.getElementById("infowindow").appendChild(add);
}
function getStationInfo(callback) {
//createHttpRequest() exists globally
var xhr = createHttpRequest();
var url = "stations.php" //edited out the original URL
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
var stationInfo = "This is a Test";
return callback(stationInfo)
} //end readyState
} //end readystatechange
xhr.open("GET", url, true);
xhr.send(null);
} //end getStationInfo
Small Edit: Moved functions outside of the loop
Edit 2: There is nothing wrong with the ajax call, the url was edited for the sake of the sample code. Notice the final output shows "This is a test" in the infowindow which clearly states a successful callback was performed. Moreover, notice there is no responseText or responseXml. The variable being sent back has nothing to do with the url
The callback works fine but for some reason it's topped with the dreadful 'undefined' on top of it.
Console shows nothing.
Output:
undefined
This is a test
What am I doing wrong? How can it be undefined if it works?
What is happening:
you click on the infowindow
getStationInfo(infoWindowDiv) is called, fires off an AJAX request, but returns nothing useful ("undefined", there is no return statement)
The AJAX function will encounter an error (url "Unnecessary at this point" will not cause the onreadystatechange function to fire). But you tell us that isn't a problem.
The script encounters the javascript error Uncaught TypeError: Cannot call method 'appendChild' of null because the div with id infowindow hasn't been attached to the DOM.
Suggest adding an event listener on the infowindow to not attempt to access the div with id="infowindow" until it has been rendered (domready).
Working code:
var xhr = "";
var infowindow = new google.maps.InfoWindow();
var map = null;
var marker, i;
var polylineCoordinates = [new google.maps.LatLng(78.782762, 17.917843),
new google.maps.LatLng(-0.829439, -91.112473),
new google.maps.LatLng(15.066156, -23.621399)
]
function initialize() {
var mapOptions = {
center: new google.maps.LatLng(78.782762,17.917843),
zoom: 10,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"),
mapOptions);
for (i = 0; i < polylineCoordinates.length; i++) {
marker = new google.maps.Marker({
position: polylineCoordinates[i],
map: map
});
google.maps.event.addListener(marker, 'click', (function (marker, i) {
return function () {
infowindow.setContent("<div id=\"infowindow\" style=\"height:50px;width:200px;\"></div>");
infowindow.open(map, marker);
google.maps.event.addListenerOnce(infowindow,"domready", function(){
getStationInfo(infoWindowDiv);
});
})(marker, i));
} //End adding markers loop
}
function infoWindowDiv(stationInfo) {
var add = document.createTextNode(stationInfo);
document.getElementById("infowindow").appendChild(add);
}
function getStationInfo(callback) {
var stationInfo = "This is a Test";
callback(stationInfo)
} //end getStationInfo
google.maps.event.addDomListener(window, 'load', initialize);

Categories

Resources