I have the view
class FoursquareSearch.Views.Origin extends Backbone.View
events:
'change [name=origin]': 'setOrigin'
'click [name=geolocate]' : 'geolocate'
geolocate: ->
navigator.geolocation.getCurrentPosition(#handle)
handle: (response) ->
#model.set(coords: response)
I am trying to determine the location of a device and then set the model with the response. However I get
Uncaught TypeError: Cannot call method 'set' of undefined
The weird thing is this only happens when its inside this function. For example if I use:
geocode: (location) ->
data =
location: location
$.ajax(
type: 'POST'
url: '/search/geocode'
data: data
dataType: 'json'
error: (jqXHR, textStatus, errorThrown) =>
alert("ERROR")
success: (response, text, xhr) =>
#model.set(coords: response)
#center(#model.get('coords'))
)
Inside the same view it works, and it works well... However I just can't get the other function to set the model. I think this is something about it being Asynchronous. I am by no means an expert at this, I am picking up Backbone as a I go along, but this is stumping me!
The Geolocation API doesn't specify any particular context for the getCurrentPosition callback functions so this inside the callbacks is probably window; window usually won't have a model property so this:
handle: (response) ->
#model.set(coords: response)
ends up looking like this when getCurrentPosition calls it:
handle: (response) ->
window.model.set(coords: response)
So handle tries to call set on the non-existent window.model and there's your Cannot call method 'set' of undefined error.
Try defining handle as a bound method:
handle: (response) => # fat arrow here
#model.set(coords: response)
Your other #model.set calls are working fine because # is your view object and that does have a model property.
Related
I have following Service, the query always returns an array.
.factory('ClosingDocService', ['$resource', function ($resource) {
return $resource("http://localhost:5001/api/ClosingDoc/:id",
{ id: "#id" },
{
'query': { method: 'GET' },
'save': { method: 'POST', transformRequest: angular.identity, headers: { 'Content-Type': undefined } }
});
}])
However, the following call always got the error?
ClosingDocService.query({ category: model.category }).$promise
.then(function (x) { });
Using fiddler shows the following correct url has been called and the values were returned.
http://localhost:5001/api/ClosingDoc?category=XXX
Error:
angular.js:13920 Error: [$resource:badcfg] Error in resource configuration for action query. Expected response to contain an object but got an array
I found a solution - adding isArray: true. But why?
'query': { method: 'GET', isArray: true },
This occurs due to how $resource object methods are handled.
From the docs on $resource:
It is important to realize that invoking a $resource object method immediately returns an empty reference (object or array depending on isArray). Once the data is returned from the server the existing reference is populated with the actual data. This is a useful trick since usually the resource is assigned to a model which is then rendered by the view. Having an empty object results in no rendering, once the data arrives from the server then the object is populated with the data and the view automatically re-renders itself showing the new data. This means that in most cases one never has to write a callback function for the action methods.
Essentially, unless you provide isArray: true, the $resource object method pre-allocates an Object for the data that is to be returned. When the data being returned actually ends up being an array, that data can't be populated into the pre-allocated object.
I had the same issue too. What worked for me was the following fix
Changing the query to get as follows:
ClosingDocService.get({ category: model.category }).$promise
.then(function (x) { });
Below is an example function of a model being saved, and the error handling involved. I want to put control flow in the error handling so for specific types of errors one thing happens and for another type of error, another thing happens, but I can't seem to examine the xhr with console logs or debugger.
propChange: function (newName) {
var self = this;
var oldProp = this.model.get('prop');
this.model.save({ name: newProp }, {
success: function (data, textStatus, xhr) {
...
},
error: function (model, xhr) {
//I can't console.log anything hear, use debugger, or put a break point here so how do I investigate or experiment with what model and xhr could be at this point?
}
});
},
I've got a weird error in angularjs. I've used $resource module in angularjs to make rest requests by registering this service
$provide.service("CustomerService", ['$resource', function ($resource) {
return $resource('/com/caspco/customers/:url:id', {}, {
query: { method: 'GET', isArray: true, params: {id: '#id'}},
find: { method: 'POST', isArray: true ,params:{url:'search'}},
.......... other actions ..........
});
}]);
in the rest server side I have a search method with the above url of find action that returns a json array. when I what to call find action by this way in the controller :
service.$find().$promise.$then(function (res) {
console.log("resource is" + res);
}, function (error) {
console.log("error");
});
It raises
TypeError: (anonymous function) angular.js:2070
(anonymous function) angular.js:1516
k.$apply angular.js:2575
(anonymous function) angular.js:4323
o.event.dispatch jquery.js:3
r.handle
Methods on the service are not prefixed by the '$' character. Methods on resources returned from the service are prefixed by '$'. The 'then' method on the $promise property is also not prefixed by '$'.
I think if you clean up that syntax you will be on the right track.
See the angular docs.
First off your code to call the service should be:
service.find().$promise.then(function (res) {
console.log("resource is" + res);
}, function (error) {
console.log("error");
});
The next thing to check, is your response actually an array. I got caught out big time with this, I was calling a service to get a list of objects from the backend, but before returning them, the backend was wrapping them inside a Response object, so what angular received was not an array, it was an object with an array inside it.
I have a Backbone model that is making a successful server request. The callback is using backbone's trigger method to trigger an event in the associated view, and its passing the parsed json response from the server request as the second parameter of the trigger method, as described in the backbone documents. In the view, the event triggers the render method, but the response object is null in the view. It's throwing this error
Uncaught TypeError: Cannot read property 'word' of null
Can anyone see what I might be doing wrong?
The server request from the model
new: function() {
var _this = this;
console.log("new function of model");
$.ajax({
url: "/gamestart",
type: "GET",
success: function(response) {
console.log(response);
var json = $.parseJSON(response);
_this.set({lost: false});
_this.set({win: false});
_this.trigger("gameStartedEvent", json);
}
})
},
the event in the initializer method of the view which triggers the render method to render the response
this.model.on("gameStartedEvent", this.render, this);
the render method where the response is null
render: function(response) {
$("#hint").show();
console.log(response);
var html = this.template({characters: response.word});
this.el.hide();
this.el.html(html).show();
},
Note, if it matters, the view is being instantiated with the model
var word_view = new WordView({model: game})
Update
Actually the error's happening here. The response is returning successfully but i'm parsing it incorrectly. The variable json is null when I check the console. Can you see what I'm doing wrong?
var json = $.parseJSON(response);
console.log(json)
Response
Object {word: Array[6]}
word: Array[6]
__proto__: Object
There was actually no need to call parseJSON on the response object. I could get the word property directly off the response object, so I just passed the response object as the second argument to trigger, and called response.word in the view.
$.ajax({
url: "/gamestart",
type: "GET",
success: function(response) {
console.log(response.word);
var json = $.parseJSON(response); ####unnecessary to parseJSON here
console.log(json);
_this.set({lost: false});
_this.set({win: false});
_this.trigger("gameStartedEvent", response);
}
})
},
this.model.save({
success: function(model, response){
console.log('success');
},
error: function(){
console.log('error');
}
})
The model is correctly posted to the server which handles the save, but the success callback is not fired. Do I need to send something back from the server ?
The first argument of save is the attributes to save on the model:
this.model.save( {att1 : "value"}, {success :handler1, error: handler2});
For some unknown reason, none of the above method worked for me. The api only was not hit in my case.
But later while searching on this, I bumped into this link, where some one had tried null instead of {} as the first parameter.
this.model.save(null, {
success: function (model, response) {
console.log("success");
},
error: function (model, response) {
console.log("error");
}
});
so, this worked for me. Hope this helps you too.
Your server must return a JSON object. If the response is not a JSON object, the callbacks will not fire.
If for success your server doesn't return a JSON object, perform a save with dataType:"text" option, like this:
this.model.save([],{
dataType:"text",
success:function() {},
error:function() {}
});
With this option it will not be waiting for a JSON in response, but a text, and thus the callback will be launched.
You may use underscore lib as follows as backbone already depends upon this. Remember first argument of save must either have attributes or you may just pass {} in case you want to save model itself.
this.model.save({}, _.bind(function(model, response){
//Do whatever you want e.g.
this.collection.add(model)
}, this))
so im a little confused - do i still need to pass in all attributes in order for me to call a save event? what if my model is large.. i dont wish to set every property manually
im calling model.save and attempting to do the following:
this.model.save(
{
success: function (model, response) {
console.log('model saved');
}
});
ok just to answer my own question incase anyone finds this post, i did the following which works:
this.model.save({ id: this.model.get('id') },
{
success: function (model, response) {
console.log("success");
},
error: function (model, response) {
console.log("error");
}
});
EDIT: I couldn't reply to you for some reason, but I can edit
but you don't have to set id: this.model.get('id') you can just pass a blank object because a blank attribute just won't extend attributes, does nothing:
this.model.save({}, {
success: function (model, response) {
console.log("success");
},
error: function (model, response) {
console.log("error");
}
});
The following is the code that i am using for backbone model save.
this.model.save(model,{
success:function(model){
console.log("Saved Successfully");
},
error:function(model){
console.log("Error");
}
});
Cheers
Roy M J
For those that want to save a model, without updating the attributes, you can do the following:
model.once("sync", function(model, response, options){
//
});
model.once("error", function(model, response, options){
//
});
model.save();
In you initialize function, bind the sync method to a method you define (onSaveSuccess)
initialize: function (options) {
this.model.on('sync', _.bind(this.onSaveSuccess, this));
},
onSaveSuccess: function() {
console.log('saved');
this.render();
},
This way, any time you run this.model.save(), it will run the onSaveSuccess function as a callback if your sync is successful