Backbone.js Model Post - backbone.js

Im building an application where I need to POST data to a server. At this point the only thing I have been using models for is to receive/fetch data in the app. Now I want to POST. I was wondering how to do that in a good way. I guess by a model called "postmodel" or something?
I basically just want to call on an api and pass some params. Like this kind of:
/api/?action=answerQuestion&question_id=*my id here*
How would I do something like that?

What you would have to do is create a new function within your model and perform the request "manually" from that function. Something like this:
MyModel = Backbone.Model.extend({
initialize: function(){
...
},
post_to_api: function(){
var data = {
"question_id": this.id,
"action": "answerQuestion"
};
$.post( "http://api.com/", data, function( response ) {
// post success callback
});
},
});
Then to use this, you would instantiate MyModel and call the post_to_api function:
var model = new MyModel( { "id": 42 } );
model.post_to_api();

Related

Backbone.js / require.js - Override model function to work with backend as a service

Good morning guys. I have a little understanding problem with backbone.js. i have a javascript sdk from a backend as a service with some getter and setter methods to get datas from this platform.
I have load this javascript sdk with require.js an it´s work fine. Now i need to create some models that work with this getter and setter methods to get this data to my collection an finally to my view. I do not have any clue...maybe someone have the right idea for me.
This is my current model:
define(['jquery','underscore','backbone'], function($,_,Backbone) {
var holidayPerson = Backbone.Model.extend({
initialize: function() {
console.log("init model holidayPerson");
this.on("change", function(data) {
console.log("change model holidayPerson"+JSON.stringify(data));
});
}
});
return holidayPerson;
});
Actually i create an instance of my model in my view:
define(['jquery','underscore','backbone','text!tpl/dashboard.html','holidayPerson','apio'], function($,_,Backbone,tpl, holidayperson, apio) {
template = _.template(tpl);
var usermodel = new holidayperson();
var dashboardView = Backbone.View.extend({
id: 'givenname',
initialize: function() {
console.log("dashboard view load");
usermodel.on('change', this.render);
var user = new apio.User();
user.setUserName('xxx');
user.setPassword('xxx');
apio.Datastore.configureWithCredentials(user);
apio.employee.getemployees("firstName like \"jon\" and lastName like \"doe\"", {
onOk: function (objects) {
console.log("apio: " + JSON.stringify(objects));
usermodel.set({mail: objects[0]['data']['mail'],lastname: objects[0]['data']['lastName'], username: objects[0]['data']['userName'], superior: objects[0]['data']['superior']});
}
});
},
render: function() {
console.log("render dashboard view");
console.log(usermodel.get('mail'));
console.log(usermodel.get('lastname'));
this.$el.html(template());
return this;
}
});
return dashboardView;
});
I think this not the right way...can i override the getter and setter method from this model ? Or maybe the url function ? Anyone now what is the best practice ?
Thanks a lot :-)
First of all, make sure that your render operation is asynchronous, as your API call will be and the usermodel params won't be set until that operation completes. If you render method fires before that, it will render the empty usermodel, since the data will not be there yet.
Second, a model need not fetch its own data, in my opinion. If you are going to have multiple users, you could use a collection to hold those users and then override the collection's sync method to handle the fetching of data from the API, but if there's no collection, it seems logical to me to have a method that does the data fetching and setting thereafter, as you've done.

Loading Backbone model by custom attribute

Lets say, I have the following Backbone model :
var Meal = Backbone.Model.extend({
defaults: {
"appetizer": "caesar salad",
"entree": "ravioli",
"dessert": "cheesecake"
},
urlRoot : api/meals,
idAttribute : id,
// some other stuff
});
Assuming that I have a backend Spring MVC conroller that intercept GET requests, so when I fetch my model, using
myMeal.fetch();
my model gets loaded from the server.
for now everything is clear, my question is, what if I have another method on the backend that takes a string as parameter and return as responsebody, the right json element.
how can I make that call from my model ?
I'm looking for something like this :
var meal = new Meal({'entree': 'value'});
meal.fetch({
// if there is no id, and 'entree' is given, I want to call /
// api/findByEntree passing this.entree as parameter.
});
I want to make an Ajax call from the object itself to the backend, by specifying the url inside the Backbone model.
urlRoot can be a function so no need to override fetch. I believe you could just do something like this:
var Meal = Backbone.Model.extend({
defaults: {
"appetizer": "caesar salad",
"entree": "ravioli",
"dessert": "cheesecake"
},
urlRoot : function() {
return 'api/' + this.get('id') ? 'meals' : 'findByEntree';
},
idAttribute : id,
// some other stuff
});
You can override the default fetch, intercept the call, do some verification and then pass onto the original fetch:
var Meal = Backbone.Model.extend({
fetch: function(options) {
if(this.has('id')) {
Backbone.Model.prototype.fetch.call(this, options);
} else {
this.findByEntree(options);
}
},
fetchByEntree: function(options) {
...
}
});
however, keep in mind that you'll need some extra logic to deal with the case of trying to fetch a new Meal, which won't have neither id nor entree.

How to call a custom JSON request, then build a collection of models from that, using Backbone.js?

I'm new to Backbone.js and have been trying to understand three things:
1) How and where to call a custom JSON request?
2) How to translate that JSON request into a model?
3) How to create a collection of those models?
My JSON looks something like this:
{"flavor": "vanilla",
"message": "ok",
"count": 10,
"rows": [{"data":["this", "is", "123", "something"],
"moreData": "more stuff here",
"moreInfo": "more info here"},
{"data":["even", "more", "456", "something"],
"moreData": "even more stuff here",
"moreInfo": "even more info here"},
{"data":["it", "doesn't", "123", "end"],
"moreData": "more and more stuff here",
"moreInfo": "more and more info here"}]
}
I want to have each "data" be an Item. I want a List to contain many Items.
I have to make a custom JSON request (not via $.ajax or anything like that - it is in a separate library that has to be called via a specific function, that provides a callback option) - do I make this call in the Backbone.Model or the Backbone.View?
Basically the JSON response format that I will be getting is not within my control, hence the messy sample above. I'd like to format some of this JSON into nicer attributes as a bonus. I was hoping to do it in the Item model.
How can I translate that request into a model? If I have to modify certain attributes?
How can I create a collection of those models? So far I have
var ItemList = Backbone.Model.extend({
})
var ItemLists = Backbone.Collection.extend({
model: ItemList,
});
var ItemView = Backbone.View.extend({
el: $("#dashboard"),
template: _.template($("#item-template")),
initialize: function(){
this.render();
},
render: function(){
var item = new Item();
var content = this.template(item);
$(this.el).html(content);
}
});
Please help, any Backbone.js experts. This should be a relatively straightforward question for someone experienced!
var ItemList = Backbone.Model.extend({
parse: function(response) {
// response = {"data":["this", "is", "123", "something"],
// "moreData": "more stuff here",
// "moreInfo": "more info here"}
// You can make additional changes here
return response;
}
});
var ItemLists = Backbone.Collection.extend({
model: ItemList,
parse: function(response) {
return response.rows;
}
});
Is this what you were thinking? This takes the rows and makes each one into an ItemList. You can do other manipulation within the parse, if needed. Then you can set the url and use fetch.

Backbone.js - Liking a post, code refactoring

Right now I'm using this code to Like a post. I'm Using jQuery methods to change the Like to Unlike and to change the Like count
View
Streaming.Views.StreamsIndex = Backbone.View.extend({
events: {
'click .like-icon': 'post_liked',
},
initialize: function(){
this.model = new Streaming.Models.StreamsIndex();
this.model.bind('post_likeSuccess', this.post_likeSuccess);
},
post_liked: function(event) {
event.preventDefault();
event.stopPropagation();
current_target = $(event.currentTarget);
liked_id = current_target.attr("id");
href = current_target.attr('href');
this.model.like(href, liked_id); // calls model to send API call for Like
},
post_likeSuccess: function(data, liked_id) {
$("#" + liked_id).attr({
"href": data.unlike,
"title": "Unlike",
"rel": "Unlike",
"class": "likehead-ico_active" // changing like icon
});
//changing like count
$("#"+ liked_id+"_count").text(parseInt($("#"+ liked_id+"_count").text()) + 1);
}
});
Model:
Streaming.Models.StreamsIndex = Backbone.Model.extend({
like: function(href, liked_id) {
var self = this;
$.ajax({
url: href,
type: "POST",
dataType: "json",
async: false,
success: function (data) {
self.trigger('post_likeSuccess', data, liked_id);
},
error: function (data) {
self.trigger('post_likeFail', data, liked_id);
alert("This action was not performed");
}
});
}
});
Is there a better way I can do this?
After liking a post can I change the Like text to unLike, Change the like count in a better way without using jquery?
There are several issues with this code. I'll try address them one by one.
Looks like your Streaming.Views.StreamsIndex has several posts in it. It should be broken down into a component views, that are rendered through a collection, so that each model in the collection is bound to a view. You could, maybe call it Streaming.Views.StreamPost
Your initialize method would have:
this.collection = this.model.posts(); // Or something to this effect
Your render method would have:
// addPost is a function
// that takes 'post' as a parameter
// build the corresponding view object
// and appends it to the posts container
this.collection.each(this.addPost, this)
// example of how addPost looks
var view = new Streaming.Views.StreamPost({model: post});
this.$('#posts-container').append(view.render().el);
The event listener 'click .like-icon': 'post_liked' should on the new component view Streaming.Views.StreamsIndex, instantiated in the addPost above. With this, you don't have to use the ugly current_target = $(event.currentTarget) hack. You always do this.model.get('id') to get the id of the post. The thumb rule he is to not use jQuery or any other form of raw DOM manipulation when using Backbone. That is what views & templates are for! Adjust your template by putting a little logic (as little as possible) to show something if post is liked, and show something else if post is not liked yet. The job of deciding whether a post is liked or not, is to be done by the Post model. I usually write wrapper methods in views that call relevant methods on the model.
Instead of using custom events like post_likeSuccess, update the state of your model and re-render your view. If you updated your templates like I mentioned above, then re-render would take care of all the DOM manipulation you are doing.

Backbone Model not compatible with underscore and ASP.NET MVC Web API Controller?

This is a two stage problem when working with backbone.js and a web api controller.
I have a simple web api controller that returns a JSON string, in fiddler the result looks like this:
{
"$type": "MvcApplication.Models.Article, MvcApplication",
"Id": "1",
"Heading":"The heading"
}
I use the following code to fetch a user from my web api
var user = new Usermodel({ id: "1" });
user.fetch({
success: function (u) {
console.log(u.toJSON());
}
});
now my backbone user object looks like this
{
id: "1",
{
"$type": "MvcApplication.Models.Article, MvcApplication",
"Id": "1",
"Heading": "The heading"
}
}
When I try to bind this backbone model object to my view template that looks like this
<form>
<input type="text" value="<%=Heading%>" />
<input type="submit" value="Save" />
</form>
i get, Heading is undefined but when I use id it binds just fine? It seems like underscore does not like the backbone model object and just want a plain JSON object just like the one I get from my web api?
The second problem with this is that when I save my model with user.save({ Heading: "my new heading }); the payload to my web api is the backbone model which is completely wrong because my api expects a user object like this to be sent to the server:
{
"$type": "MvcApplication.Models.Article, MvcApplication",
"Id": "1",
"Heading":"The heading"
}
and not the backbone model with the real object wrapped inside. Is it possible to solve so that underscore can handle backbone models and tell backbone to only send the payload that my end point expects?
You may be able to solve the problem by following these steps:
In addition to using fiddler to inspect your response, look at the response on the network tab of Chrome Developer Tools. If the response does not look like this, then your web api is not returning a valid json response, the problem is most likely within your web api. You need to get/provide more information about your web api to solve the problem. Verify that the response looks like this:
After verifying that the response from the web api is correct, check out the following jsfiddle I modified:
http://jsfiddle.net/J83aU/23/
Fix your client side code referencing the example I have provided.
Properly instantiate the Backbone objects.
Call the view.render function at the correct step, after the response is received from the server.
Make sure that the main content div is actually rendered before creating a view which depends on it for the 'view.el' property.
Declare the 'view.el' property properly, with a string rather than jQuery object.
Use development Backbone and underscore to enable debugging, an important concept when learning to use open source frameworks such as Backbone.
Use jsfiddle's echo/json api to mock a valid ajax json response, exactly as described in step 1.
The following json example you submitted is not even valid json, if you update your question with valid json example, it would be easier to solve the problem. It is unlikely that Backbone created this non-json structure and more likely that you have submitted it here incorrectly.
{
id: "1",
{
"$type": "MvcApplication.Models.Article, MvcApplication",
"Id": "1",
"Heading": "The heading"
}
}
Finally, try to provide a screenshot of the http headers or something for the problem that is occurring when you call model.save().
Read over the Backbone documentation for model.save() and make sure you are doing everything just as the example provided.
You may be able to workaround Backbone's funky save function by forcing your attributes into POST parameters using ajax options:
$.fn.serializeObject = function(){
var o = {};
var a = this.serializeArray();
$.each(a, function() {
if (o[this.name] !== undefined) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
});
return o;
};
var saveView = Backbone.View.extend({
events:{
'click #saveSubmitButton':'submit'
},
submit:function (event) {
event.preventDefault();
var view = this,
attributes = $('#saveForm').serializeObject();
this.model.save(attributes, {
data:attributes,
processData:true,
success:function (model) {
//....
}
});
},
render:function () {
//.......
}
});
The attributes property of your model should be unaltered. Send those to your template call:
var MyModel = Backbone.Model.extend();
var newModel = new MyModel({
"$type": "MvcApplication.Models.Article, MvcApplication",
"Heading":"The heading"
});
var html = _.template(templateVar, newModel.attributes);
In your templateVar, which is your templated markup, you should be able to reference $type and Heading directly.
If you have a look at the jsFiddle through a debugger like Firebug you can see that the way you construct the model's URL is not working out, because the forward slash gets encoded. Can you try to modify your model declaration to this:
var Usermodel = Backbone.Model.extend({
url: function () {
return '/api/page/articles/' + this.get('id');
}
});
var user = new Usermodel({
id: '85'
});
And see if you still get the same JSON. Basically if you don't have a Backbone.sync override you are using built-in retrieval that for one shouldn't produce invalid JSON.

Resources