Backbone.js. prevent GET - backbone.js

Im working on an application where i have found this weird problem. When i init my Model both GET and POST triggers. Its only supposed to trigger a POST, since i run Model.save();
What can be the problem here?
Here is the Model:
PostModel = Backbone.Model.extend({
url: function() {
return "/api?action=answerQuestion"+"&question_id="+this.get('questionId')+"&option_id=" + this.get('optionId')+"&type=" + this.get('role');
},
defaults: {
questionId: 0,
optionId: 0,
role: 0
}
});
Here is where the model gets created. (on a click event).
events: {
'click li': 'answerQuestion'
},
answerQuestion: function(event) {
event.preventDefault();
$('li').each(function() {
$(this).addClass('inactive');
});
$(event.currentTarget).removeClass('inactive').addClass('active');
var currentQuestion = 1;
var answer = parseInt($(event.currentTarget).find('a').attr('data-answer'));
var role = 1;
var postModel = new PostModel();
postModel.set({
questionId: currentQuestion,
optionId: answer,
role: role
});
postModel.save();
}

Related

posting data with backbone form post

i am unable to get form post to work. below is snippet for form view with event handling but I am unable to see form data printed in console from getFormData. I am not sure if this is right approach to form handling but was just trying it out reading stuff from net.
define(['backbone', 'handlebars', 'jquery', 'events', 'models/article'], function(Backbone, Handlebars, $, Events, Article) {
var ArticleFormView = Backbone.View.extend({
events: {
"submit": "createArticle"
},
tagName: "form",
id: "article-form",
className: "articleform",
initialize: function() {
this.model = new Article();
this.render();
},
render: function() {
var template = $("#createarticletemplate").html();
console.log("template=" + template);
var compiled = Handlebars.compile(template);
var html = compiled(this.model.attributes);//passed when we do new View()
console.log("compiled template=" + template);
this.$el.html(html);
return this;
},
createArticle: function(e) {
e.preventDefault();
console.log("createArticle event happened" + $(this.el).parent().html());
var data = this.getFormData( $(this.el).parent() );
console.log(JSON.stringify(data));
this.model.save(data, {
success: function(model, response, options) {
console.log("create article success");
Events.trigger("router:navigate", "#");
},
error: function(model, response, options) {
return console.log("create article failure:" + response.responseText);
}
});
},
//Auxiliar function
getFormData: function(form) {
console.log(form);
var unindexed_array = form.serializeArray();
console.log(unindexed_array.length);
var indexed_array = {};
$.map(unindexed_array, function(n, i){
console.log("array:" + n);
indexed_array[n['name']] = n['value'];
});
return indexed_array;
},
}); //artifleformview
return ArticleFormView;
});
snippet of routes/index.js
exports.articles.createone = function(req, res) {
console.log(req.body);
//res.json(req.body);
db.articles.insert(req.body);
}
article.js model:
define(['backbone'], function(Backbone) {
var Article = Backbone.Model.extend({
url: "/article",
idAttribute: "_id"
});
return Article;
});
console log:
createArticle event happened<form id="article-form" class="articleform"><label>title </label><input type="text" name="title"><p></p><label>body </label><input type="text" name="body"><p></p><label>category </label><input type="text" name="category"><p></p><input type="submit" value="create article"></form> articleform.js:27
[div.form, prevObject: m.fn.init[1], context: form#article-form.articleform, jquery: "1.11.1", constructor: function, selector: ""…]
articleform.js:44
0 articleform.js:46
{} articleform.js:29
create article success articleform.js:33
index called on router
Adding createArticle error log:
POST http://localhost:3000/article net::ERR_EMPTY_RESPONSE jquery.js:4
send jquery.js:4
m.extend.ajax jquery.js:4
e.ajax backbone.js:1
e.sync backbone.js:1
i.extend.sync backbone.js:1
i.extend.save backbone.js:1
Backbone.View.extend.createArticle articleform.js:33
m.event.dispatch jquery.js:3
r.handle
If some one wants more code let me know I can put all code here but its too many files so better would be some link I attach if needed.
Above log for error prints in console thought the article is getting persisted fine.
Regards,
Miten.
Looks like you need to replace
var data = this.getFormData( $(this.el).parent() );
with
var data = this.getFormData(this.$('form'));
or
var data = this.getFormData(this.$el.closest('form'));

how to listen to array submodel's value change

I have a model define like below.
var A = Backbone.Model.extends({
initialize: function(){
this.on('change:users.name',this.onUserNameChanged)
},
onUserNameChanged: function(){ alert('name changed')
});
var a = new A ({
id:1,
name:'test',
users:[
{
id:2,
name:'u1'
},
{
id:3,
name:'u4'
}
]
})
I want add event on the each user name change in Model define.
I have no idea to do this.It's seems hard to me.
It seems that you can't get change events by manipulating Backbone model array members as is described here:
backbone-js-set-model-array-property
Your best option is to set the whole array and listen to a change:users event or even better - come up with a different model that has a Backbone collection for users that will get the event when manipulated:
var A = Backbone.Model.extend({
initialize: function (options) {
_.extend(this, options);
}
});
var Users = Backbone.Collection.extend({
initialize: function () {
this.on('change:name', function () {
alert('name changed');
});
}
});
var a = new A({
id: 1,
name: 'test',
users: new Users([{
id: 2,
name: 'u1'
}, {
id: 3,
name: 'u4'
}])
});
a.users.get('2').set('name', 'u2');

Backbone ID double-increment and wrong start

I have a model that looks like this:
var TodosModel = Backbone.Model.extend({
defaults: {
id: null,
content: 'Something Todo',
completed: false
},
url: function() { return 'api/'+this.id; }
});
I'm adding models via:
var todoID = _.uniqueId();
var todoContent = this.newTodoField.val();
var todoCompleted = false;
// Ensure there's something to save
if(todoContent.length>0){
var _this = this;
// Create model
var todo = new TodosModel({
id: todoID,
content: todoContent,
completed: todoCompleted
});
todo.save({}, {
wait: true,
success: function(model, response) {
// Let the events deal with rendering...
_this.collection.add(model);
},
error: function(model, response) {
console.log('Could not create todo');
}
});
}
The problem I'm having is that for some reason every id is double incremented - so if I start with no elements I get 1,3,5,7...
Which holds alright, except if I reload and those ID's are brought in from the API, and then the next generated _.uniqueID is based on the count rendered out.
Any help would be greatly appreciated, here's the full code: http://sandbox.fluidbyte.org/todos/js/todos.js

Backbone.js and Bootstrap Typeahead - rendering after async fetch

I took the base of this code from a gist. It initially worked perfectly when I first fetch()ed the collection and then in render() called tw-bootstap's .typeahead().
However, I have put in a keypress event to try and restrict the size of the data returned by fetch(). The collection data is returned and it is filtered through prepData() fine and arrives at render(). The typeahead is not working, however at that stage. It may be that the backbone event is overriding render at that point?
// typeahead on the numbers
var Bootstrap = {};
Bootstrap.Typeahead = Backbone.View.extend({
el: '#autocompleteN',
tagName: 'input',
attributes: {"data-provide": "typeahead"},
initialize: function(options){
if(!this.collection) {
return null;
}
//this.collection.on("reset", this.prepData, this);
},
events: {
"keypress": "setSearch"
},
setSearch: _.throttle(function(e) {
var that=this;
var d = e.currentTarget.value;
// strip spaces and remove non-numerics
d = d.replace(/ /g,'');
d = d.replace(/[^0-9]/g, '');
// if it's longer than 2, call a fetch;
if(d.length > 2) {
$.when( app.searchNums.fetch({url: 'api/index.php/search/num/'+d}) ).then(function() {
//console.dir("success");
that.prepData();
});
}
}, 1000),
prepData: function() {
//console.dir("prepData called");
var prepare = _.pluck(this.collection.models, 'attributes');
this.property = this.options.property || _.keys(prepare[0])[0];
this.items = this.options.items;
this.data = _.pluck(prepare, this.property);
this.render();
},
render: function() {
var that = this;
that.$el.typeahead({
source: that.data,
//source: ['PHP', 'MySQL', 'SQL', 'PostgreSQL', 'HTML', 'CSS', 'HTML5', 'CSS3', 'JSON'],
items: that.items,
onselect: function( data ) {
// render the results view here
}
});
return this;
}
});
var bui = new Bootstrap.Typeahead({
collection: app.searchNums,
items: 5
});
Why dont you just set minLength on the typeahead, it looks like that is what you are trying to do?

Backbone performing a GET immediately after a POST

I'm experimenting for the first time with backbone.js and I have a very simple Grails application with a single domain called Book. Things seem to be working well however, I've noticed that when I POST the data from the form to the server backbone then does a GET to the server with the ID of the new record. However, the POST returns the results as JSON and populates the table accordingly. I'm not sure I understand the need for the GET following the POST or how to stop this from happening.
$(function() {
// Model
window.Book = Backbone.Model.extend({
url: function() {
return this.id ? '/BackboneTest/books/' + this.id : '/BackboneTest/books.json';
},
defaults: { book: {
title: 'None entered',
description: 'None entered',
isbn: 'None entered'
}},
initialize: function() {
// can be used to initialize model attributes
}
});
// Collection
window.BookCollection = Backbone.Collection.extend({
model: Book,
url: '/BackboneTest/books.json'
});
window.Books = new BookCollection;
//View
window.BookView = Backbone.View.extend({
tagName: 'tr',
events: {
// can be used for handling events on the template
},
initialize: function() {
//this.render();
},
render: function() {
var book = this.model.toJSON();
//Template stuff
$(this.el).html(ich.book_template(book));
return this;
}
});
// Application View
window.AppView = Backbone.View.extend({
el: $('#book_app'),
events: {
"submit form":"createBook"
},
initialize: function() {
_.bindAll(this, 'addOne', 'addAll');
Books.bind('add', this.addOne);
Books.bind('refresh', this.addAll);
Books.bind('all', this.render);
Books.fetch();
},
addOne: function(book) {
var view = new BookView({model:book});
this.$('#book_table').append(view.render().el);
},
addAll: function() {
Books.each(this.addOne);
},
newAttributes: function(event) {
return { book: {
title: $('#title').val(),
description: $('#description').val(),
isbn: $('#isbn').val()
} }
},
createBook: function(e) {
e.preventDefault();
var params = this.newAttributes(e);
Books.create(params)
//TODO clear form fields
}
});
// Start the backbone app
window.App = new AppView;
});
I've determined that the cause of this was server side. Because of some scaffolded code that got generated for testing purposes, on the save, there was an additional redirect which resulted in a 302. This caused the GET after the POST. Once I cleaned up the server side code, I only get the POST, as expected.
Backbone usesPOST as a factory (getting the id from the server) with:
a payload request { title: 'None entered' }
a response { id: 12, title: 'None entered' }
It seems that your code trigger a GET action after the POST success. The code Books.bind('all', this.render) do not seems to be related to anything. It is not binded like add and there is no such method in the View.

Resources