Backbone: Can't remove a view when call method remove() - javascript

I have tried to remove a view that contains makers that are rendered in map. When I click a button, the event click .ver is being activated, but nothing happens and I receive the following error: Uncaught TypeError: undefined is not a function. I don't know what I'm doing wrong.
My code:
ev.views.Home_Eventos = Backbone.View.extend({
initialize: function(map){
this.template = _.template(ev.templateLoader.get('home_list_eventos'));
this.map = map;
//console.log(this.map);
this.render(this.map);
},
events: {
'click .ver' : 'teste'
},
teste: function(){
ev.views.Markers.remove();
},
render: function(map){
var that = this;
that.map = map;
var imagens = new ev.models.ImagemCollection();
imagens.fetch({
success: function(){
that.$el.html(that.template({imagens: imagens.models}));
var marcadores = imagens.models;
setTimeout(function() {
that.local(that.map);
var marcadores = new ev.views.Markers(that.map);
}, 0);
return that;
}
});
}
});
ev.views.Markers = Backbone.View.extend({
initialize: function(map){
this.map = map;
this.render(this.map);
},
render: function(map){
var that = this;
var imagens = new ev.models.ImagemCollection();
imagens.fetch({
success: function(){
var marcadores = imagens.models;
setTimeout(function() {
_.each(marcadores, function(marcador){
var myLatlng = new google.maps.LatLng(marcador.get('latitude'),marcador.get('longitude'));
var marker = new google.maps.Marker({
position: myLatlng,
map: that.map,
});
});
}, 0);
return that;
}
});
}
});

You need to keep a reference to the views you want to remove. For example, you have:
var marcadores = new ev.views.Markers(that.map);
you could save a reference on your view by doing this instead:
that.marcadores = new ev.views.Markers(that.map);
and then in your event handler,
teste: function(){
this.marcadores.remove();
},
This will destroy the Marker view that you've created but may not clean up the Google Maps markers managed by that view.

When you write the following:
ev.views.Markers = Backbone.View.extend({ ... });
You are creating a new class. In order for ev.views.Markers.remove(); to work correctly ev.views.Markers needs to refer to an instance of a class rather than the class itself.
You're getting the error Uncaught TypeError: undefined is not a function because ev.views.Markers.remove is not a function, ev.views.Markers.prototype.remove is.

Related

How to load google maps as callback?

I have defined an ajax function that get some records from the database, after the data was taken I want load google maps, so I passed the function as callback:
get_records = function (callback) {
var postUrl = "someurl";
var postData =
{
csrfToken: "token",
};
$.post(postUrl, postData, function (response) {
callback(response);
});
};
this is the callback:
get_records(function(result){
init_map();
});
where init_map is:
init_map = function () {
var map;
google.maps.event.addDomListener(window, "load", function () {
map = new google.maps.Map(document.getElementById("map"), {
center: new google.maps.LatLng(33.808678, -117.918921),
zoom: 12,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var infoWindow = new google.maps.InfoWindow();
function createMarker(options, html) {
var marker = new google.maps.Marker(options);
if (html) {
google.maps.event.addListener(marker, "click", function () {
infoWindow.setContent(html);
infoWindow.open(options.map, this);
});
}
return marker;
}
var marker0 = createMarker({
position: new google.maps.LatLng(40.9117877, 14.7679659),
map: map,
icon: "../assets/img/marker-green.png"
}, "<h1 class='black-content'>Marker 0</h1><p>This is the home marker.</p>");
});
return map;
};
if I place the init_map function outside get_records the map is displayed, but with the code above I get no map displayed, why?
If there's no response coming from the ajax post, the callback will never be called.
A better approach is to use the Promise interface:
var jqxhr = $.post("example.php", function() {
console.log("success")
}).done(function() {
// call init_map() on done?
}).fail(function() {
// call init_map() on error?
}).always(function() {
// call init_map() regardless the response?
})
Also, you're using google.maps.event.addDomListener to add a listener during window load. If you're sure that your page is loaded before making the ajax post, you don't need this listener. You'll only need to create a new map inside the init_map function.

Why does typescript not see my function?

I have the following Listener that is in a onInit method:
google.maps.event.addListener(this.map, 'click', function(event) {
console.log(event.latLng);
var lt = event.latLng.lat;
var ln = event.latLng.lng;
console.log(lt());
console.log(ln());
this.onStoreMarker(lt(),ln());
});
Outside I have a method which is called when the event is triggered, Angular2 compiles this fine but when I try activate the event by clicking when running the app I get a error saying
Uncaught TypeError: this.onStoreMarker is not a function
onStoreMarker(lt: number,ln: number) {
var newStore = prompt("Please enter your store name", "Store Name");
var location = {lat: lt, lng: ln};
var marker = new google.maps.Marker({
position: location,
map: this.map,
title: newStore,
});
}
Change
google.maps.event.addListener(this.map, 'click', function(event) {
with
google.maps.event.addListener(this.map, 'click', (event)=> {
your this is not refering to your component.
Alternatively, the old js way:
var self = this; // store this in a variable to use it later
google.maps.event.addListener(this.map, 'click', function(event) {
console.log(event.latLng);
var lt = event.latLng.lat;
var ln = event.latLng.lng;
console.log(lt());
console.log(ln());
self.onStoreMarker(lt(),ln()); // <-- use it later
});
I suggest you to read this for a more in-depth answer: How to access the correct `this` inside a callback?

linking a function to a clicking event

Im trying to code a functionality so when I click on a certain location button that markers info window pops up. Ive got the code to console log the certain title and location of the marker, but when i try to call the infowindow in the viewmodel it comes up undefined. I know that the Infowindow is in the init function for google map, i cant seem to figure out how to open it from the viewmodel
and here is my code for the view model:
function viewModel() {
this.marker = locationArray().location;
this.openWindow = function(location){
if(this.marker){
console.log(this.marker);
};
}
}
and my click event:
google.maps.event.addListener(marker,'click', (function(marker){
return function() {
viewModel()
infoWindow.setContent("<div>" + marker.title + "</div>");
infoWindow.open( map, marker);
}
})(marker));
here is my google map api, hopefully this will help :
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
center: {lat: 41.764117, lng: -72.676470},
zoom: 13,
styles: style
});
// Iterates through the locationArray and gives the marker a proper
// location.
for(var i = 0; i < locationArray().length; i++){
var locations = locationArray()[i].location;
var title = locationArray()[i].title;
var marker = new google.maps.Marker({
position: locations,
map: map,
title: title,
animation: google.maps.Animation.DROP
});
locationArray()[i].marker = marker;
var message = "hello world!";
var infoWindow = new google.maps.InfoWindow({
content: title,
position: locations
});
google.maps.event.addListener(marker,'click', (function(marker){
return function() {
viewModel();
infoWindow.setContent("<div>" + marker.title + "</div>");
infoWindow.open( map, marker);
}
})(marker));
};
};
ko.applyBindings(new viewModel());
You definition of marker in the ViewModel is ambiguous. You should in general define the observables inside the ViewModel.
function ViewModel(data) {
var self = this;
self.locations = ko.observableArray(data.locations || []);
self.showMarker(function(loc) { // bind this to click (in html)
loc.marker.showInfo(); // I'm not sure if loc.marker.click() would work
});
}
var locations = []; // your list of locations
ko.applyBindings( new ViewModel({locations: locations}) );
And you can bind the click event of your marker in a simpler way, that'll also help to easily call the
marker.showInfo = function() {
infoWindow.setContent("<div>" + this.title + "</div>");
infoWindow.open( map, this);
};
google.maps.event.addListener(marker,'click', marker.showInfo);

IndexedDB error: Uncaught DataCloneError: Failed to execute 'put' on 'IDBObjectStore': An object could not be cloned

I am using the Google maps API along with the HTML 5 geolocation API to display my position as a marker on a map. Once this marker is displayed I have a simple on marker double-click function that saves a new marker to my current position using indexedDB. Everything goes well until the Object is about to be stored, then I received the message "Uncaught DataCloneError: Failed to execute 'put' on 'IDBObjectStore': An object could not be cloned." in the console. my code is as follows:
function initialize() {
var mapProperties = { // Set the maps properties
center: new google.maps.LatLng(55.8580, -4.2590),
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map-overview"), mapProperties); //Display the map in the map-overview div
function NogeoLocation(e) { //A function to handle users that do not have Geolocation
if (e) {
var content = 'Error: Unfortunately the Geolocation service failed.';
} else {
var content = 'Error: Sorry, Your web browser doesn\'t support geolocation.';
}
var options = { //Error options
map: map,
position: new google.maps.LatLng(60, 105), //Set new position on Error
content: content //Display error message
};
var infowindow = new google.maps.InfoWindow(options);
map.setCenter(options.position);
}
//Using HTML5 Geolocation
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function (position) {
var position = new google.maps.LatLng(position.coords.latitude,
position.coords.longitude);
var contentString = "Here is your current location!" + "<button onclick='myBtn()'>Save</button>"
var infowindow = new google.maps.InfoWindow({
content: contentString
});
var marker = new google.maps.Marker({
position: position,
map: map,
title: 'My House'
});
google.maps.event.addListener(marker, 'click', function () {
infowindow.open(map, marker);
});
var db;
function indexedDBOk() {
return "indexedDB" in window;
}
google.maps.event.addListener(marker, 'dblclick', function () {
alert("dbl Click");
console.log(position);
if (!indexedDBOk) return;
var openRequest = indexedDB.open("idarticle_people", 1);
openRequest.onupgradeneeded = function (e) {
var thisDB = e.target.result;
if (!thisDB.objectStoreNames.contains("people")) {
thisDB.createObjectStore("people");
}
}
openRequest.onsuccess = function (e) {
console.log("running onsuccess");
db = e.target.result;
var transaction = db.transaction(["people"], "readwrite");
var store = transaction.objectStore("people");
//Define a marker
var marker = {
position: position,
map: map,
title: 'New Marker'
}
console.log(marker);
console.log("about to perform add");
//Perform the add
var request = store.put(marker, 1);
console.log("added");
request.onerror = function (e) {
console.log("Error", e.target.error.name);
//some type of error handler
}
request.onsuccess = function (e) {
console.log("Woot! Did it");
}
}
openRequest.onerror = function (e) {
//Do something for the error
}
});
map.setCenter(position);
}, function () {
NogeoLocation(true); // Refers to NogeoLocation function
});
} else {
// If the user's browser doesn't support Geolocation
NogeoLocation(false);
} //End of HTML5 GeoLocation
} // End of the function that initializes Google Maps
google.maps.event.addDomListener(window, 'load', initialize); //On page load, execute initialize()
marker can't be cloned because the object stored in the map-property contains a reference to a DOMNode(#map-overview), which can't be cloned (see:Things that don't work with structured clones).
Remove the map-property, it will not be re-usable at all because the google.maps.Map-instance will not exist when you retrieve the marker later.
I discovered that the reason for the error was to try to add an object that was not recognized to a cache.
this.storage.set ("page", HomePage);
I switched to a string and it worked
this.storage.set ("page", "HomePage");

Updating a view from collection

I am having a little trouble rendering my player.
Im trying to create a structure using backbonejs that will
Add a new player view when a new player joins
Updates a player score
In updatePlayerData I update the player score if the player model has already been created. If not I create a new one.
The problem is in playerScore. I am creating a span everytime render is called. This is causing many player classes to be creating under the one player view
I need to be able to loop through my current views and update the player of a certain id. I need to then re render that player view so the new content is displayed.
For reference here is my code so far:
var Player = Backbone.Model.extend({});
var PlayerView = Backbone.View.extend({
className: 'player',
initialize: function () {
_.bindAll(this, 'render');
},
render: function () {
this.playerScore();
return this;
},
playerScore: function(){
$('<span/>', {
class: 'playerScore'
}).appendTo($(this.el)).html(this.model.get('score'));
}
});
var Team = Backbone.Collection.extend({
model: Player
});
var TeamView = Backbone.View.extend({
el: '#users',
initialize: function () {
_.bindAll(this, 'updatePlayerData', 'addPlayer');
this.collection.bind('add', this.addPlayer);
this._subviews = [];
},
updatePlayerData: function (playerEntity) {
var view = _(this._subviews).find(function(v){
return playerEntity.id == v.model.get('id');
});
if(view){
view.model.set({
score: playerEntity.score
});
view.render();
return;
}
var playerModel = new Player();
playerModel.set({
score: playerEntity.score
});
this.collection.add(playerModel);
},
addPlayer: function (player) {
var playerView = new PlayerView({
model: player
});
var playerHtml = playerView.render().el;
$(this.el).html(playerHtml);
this._subviews.push(playerView);
}
});
Try modifying your playerScore method to something like:
playerScore: function () {
if (!$(this.el).find('span.playerScore').length) {
$('<span/>', {
class: 'playerScore'
}).appendTo($(this.el)).html(this.model.get('score'));
} else {
$(this.el).find('span.playerScore').html(this.model.get('score'));
}
}

Categories