backbone-pageable remove() uses invalid url - javascript

I'm relatively new to Backbone and I'm trying to use a PageableCollection in my application.
https://github.com/wyuenho/backbone-pageable
Could someone please point me, what am I doing wrong? I'm using backbone 1.0.0
I have a collection and a model defined like this:
var VoteList = Backbone.PageableCollection.extend({
model: Vote,
url: config.service + 'votes'
});
var Vote = Backbone.Model.extend({
url: function () {
return config.service + 'vote/' + this.id;
}
});
Later in the application:
this.collections.voteList = new VoteList([], {mode: "client", state: {pageSize: 12}});
....
this.collections.voteList.remove(options.model);
PageableCollection.remove() method fires a DELETE event which uses the URL of VoteList collection(?) to access a web service which in turn produces me an error 405 "Method not allowed" as a DELETE method is supposed to have an {id}
#DELETE
#Path("/vote/{id}")
#Consumes({MediaType.APPLICATION_JSON})
public void deleteVoting(#PathParam("id") Integer id) {
log.info("deleting " + id.toString());
}
When I remove pagination just by instantiating normal Backbone.Collection
var VoteList = Backbone.Collection.extend({ ... });
everything works as expected, Backbone uses a model url + id when deleting. So my question is how to make the PageableCollection to behave just in the same way?

This morning got a mail from the plugin author, it appears to be a known bug in Backbone 1.0.0. I guess it will gone away with version 1.0.1
Please see this thread: https://github.com/wyuenho/backbone-pageable/issues/70
To solve problem now I used a latest 'master' branch of Backbone which has this issue fixed:
https://raw.github.com/jashkenas/backbone/master/backbone.js
Alternatively, if you can't for some reason use a development branch, a temporary solution like this will also work:
var Vote = Backbone.Model.extend({
initialize: function () {
this.url = function () {
return config.service + 'vote/' + this.get('id')
}
}
});

Upgrading to backbone 1.1.0 seems to resolve this problem.
I ran into the same exact issue with backbone 1.0.0 collections extended by backbone-pageable.
Heads up! Be sure to keep an eye on the 1.1 upgrade notes since backbone 1.1 isn't fully compatible to 1.0 code, such asoptionspassed to views are no longer automatically available as this.options.

Related

Combining jQuery UI and HotTowel

I'm getting back into web development a bit after having been kind of out of it for the past 10 years or so, and I'm overwhelmed by all the new technologies that I'm having to catch up with, ASP.NET, MVC, jQuery, SPA, Knockout, etc. I don't know the second thing about jQuery and my experience with ASP.NET is very limited. I have a little familiarity with ASP.NET WebForms, but MVC (and the rest) is totally new to me.
After seeing how many technologies there were, and not knowing which route to explore in my new project, I saw that Hot Towel seems to be a template that combines all the latest stuff into one nice package, so I decided to get the Hot Towel template and start an ASP.NET MVC4 SPA project with it.
Now I'm trying to integrate with our in-house UI framework (which has been developing without me over the past few years). I decided to try to update the Details page in the Hot Towel template to have some content. I added a simple <span>, and all's well and good. But if I try to add what I understand to be a jQuery-widget-based component (?), I get nothing. Even for the simplest test of adding content via jQuery, I get nothing:
<section>
<h2 class="page-title" data-bind="text: title"></h2>
<span>Test this</span>
<div id="testDiv"></div>
<script type="text/javascript">
$("#testDiv").append("Testing");
</script>
</section>
I see the span, but not the modified div. And I can't see any of this content in the source ("View source") or the IE9 console (not surprising given the nature of SPA, but what should I do about it?). And the Visual Studio Page Inspector seems to be totally useless (can't get past the splash screen).
What is the proper method of adding elements to the UI under the HotTowel/jQuery/MVC/SPA/KockoutJS/Breeze/Durandal model? All these new frameworks are driving my crazy.
Edit some more details: The jQuery stuff works fine when I move it to the main page of the SPA, but when I have it on the Details "page" it doesn't work. I suspect it has something to do with the SPA nature of this application and how the content of alternate views are delivered not as an entire page, but as updated content for the main page.
Edit after further investigation, I have discovered the existence of a view model named "detail" which is probably related to this detail view code I have posted. This is the code from the view model:
define(['services/logger'], function (logger) {
var title = 'Details';
var vm = {
activate: activate,
title: title
};
return vm;
//#region Internal Methods
function activate() {
logger.log(title + ' View Activated', null, title, true);
return true;
}
//#endregion
});
The script is probably executing but cannot find the div. To correct manipulate div put your jquery code to in a function and trigger that function using attached/compositionComplete callback for duranadal 2.0 or viewAttached callback for durandal 1.x
1.x link - https://github.com/BlueSpire/Durandal/blob/master/docs/1.2/Composition.html.md#view-attached
2.0 link - http://durandaljs.com/documentation/Hooking-Lifecycle-Callbacks/
// in your detail view model, if using durandal 1.x
define(['services/logger'], function (logger) {
var title = 'Details';
var vm = {
activate: activate,
title: title,
viewAttached : function(view){
// view is the root element of your detail view and is passed in
// by durandal
$(view).append("Testing");
}
};
return vm;
//#region Internal Methods
function activate() {
logger.log(title + ' View Activated', null, title, true);
return true;
}
//#endregion
});
// in your detail view model, if using durandal 2.0, you have two options
define(['services/logger'], function (logger) {
var title = 'Details';
var vm = {
activate: activate,
title: title,
attached : function(view, parent){
// view is the root element of your detail view
// and is passed in by durandal
$(view).append("Testing first method");
},
compositionComplete: function(view, parent){
// view is the root element of your detail view
// and is passed in by durandal
$(view).append("Testing second method");
}
};
return vm;
//#region Internal Methods
function activate() {
logger.log(title + ' View Activated', null, title, true);
return true;
}
//#endregion
});

How do Backbone models know which RESTful URL to use?

I'm trying to wrap my mind around Backbone (as my recent flurry of questions indicate...). In particular I'm working through this project:
https://github.com/ccoenraets/nodecellar
http://nodecellar.coenraets.org/#
I want to conceptually understand what happens when I click the "Save" button on a new Wine for example this one:
http://nodecellar.coenraets.org/#wines/506df6b6849a990200000001
I'm thinking it goes something like this:
1) The Backbone winedetails view catches the save button click as an event and launches the "Before Save" method. See below from /public/js/views/winedetails.js.
beforeSave: function () {
var self = this;
var check = this.model.validateAll();
if (check.isValid === false) {
utils.displayValidationErrors(check.messages);
return false;
}
this.saveWine();
return false;
},
saveWine: function () {
var self = this;
console.log('before save');
this.model.save(null, {
success: function (model) {
self.render();
app.navigate('wines/' + model.id, false);
utils.showAlert('Success!', 'Wine saved successfully', 'alert-success');
},
error: function () {
utils.showAlert('Error', 'An error occurred while trying to delete this item', 'alert-error');
}
});
},
In that Save method (the 2nd method) there is a call to this.model.save. SOMEHOW that model save method MUST be making a PUT request to the '/wines' URL as evidenced in the server.js file (This is for a node.js server):
app.get('/wines', wine.findAll);
app.get('/wines/:id', wine.findById);
app.post('/wines', wine.addWine);
app.put('/wines/:id', wine.updateWine);
app.delete('/wines/:id', wine.deleteWine);
From there obviously it runs the addWine method which is defined in the routes/wines.js. What I don't understand is how the MODEL understands which URL to send the request to. I can't find anywhere that links the model.save method with the correct REST API. Does my question make sense?
Wait I might have answered my own question. It must be this line in: /public/js/models/models.js
urlRoot: "/wines"
And then Backbone knows if you are doing an "New" model it must send a POST request. If you are doing an update it must append the :id to the URL and send a PUT request, etc. Is that how it works?
Here is the documentation for the model urlRoot : http://backbonejs.org/#Model-urlRoot
If you have specified the urlRoot on the model, it will use that. If the model is part of a collection, it will reference the url property on the collection.
When saving, Backbone will use PUT for an update and POST for a create. It determines which is should use based on the result of the isNew function. This checks whether the model has an id property.

Backbone infinite loop when creating models

I'm doing something pretty standard, I think.
Model:
app.model.Todo = Backbone.Model.extend({
defaults: {
task: ''
, completed: 0
, attachments: []
, note: ''
}
});
Collection:
var Todos = Backbone.Collection.extend({
model: app.model.Todo
, localStorage: new Store('Todos')
, incomplete: function () {
return this.filter(function (todo) {
return !todo.get('completed')
});
}
, complete: function () {
return this.filter(function (todo) {
return todo.get('completed')
});
}
, comparator: function(todo) {
return todo.get('order');
}
});
app.collection.Todos = new Todos();
Then, if I just do:
app.collection.Todos.create({task: 'hi'});
app.collection.Todos.create({task: 'hi'});
The 2nd one never works. I get an infinite loop (too much recursion on Firefox and stack_overflow on Chrome).
I'm really at a loss. I commented out all events as well.
Appears it spins out of control here in backbone:
// Return a copy of the model's `attributes` object.
toJSON: function(options) {
return _.clone(this.attributes);
},
UPDATE: If I add id: 0 or whatever id to the model the error stops, but if I give it a custom ID (i.e. new Date().getTime() the error happens again. It's like whenever I create a unique item it blows up.
UPDATE 2:
var todo = new gator.model.Todo({task: actionbarVal});
gator.collection.Todos.add(todo);
gator.collection.Todos.sync('create', todo);
Doing the above kinda works, and for what I need it for it works, but it's really bad. It's bad because every single time we do a new add and sync it calls toJSON 1 time for every time add and sync has been called on this page load. So, if you add 3 items, you get 6 toJSON calls (1 for the first, 2 for the second, 3 for the third). Also, it's not as clean. I also noticed in the toJSON call in backbone this.attributes with create was correct the first time. The 2nd time it was like this.attributes == backbone or something. Very, very strange. It had all the methods of Backbone. It was as if clone did a deep clone or something.
You have a mismatch between your version of Backbone (v0.9.9) and the version of the localstorage add-on. Be sure to get the latest version of the localstorage add-on from the Backbone repo and it will fix this problem.
I eventually fixed it by reverting back to 0.9.2 of Backbone, thanks to Derick Bailey. My attempts of using the latest localStorage add-on didn't seem to fix it. Maybe I was using a different source? I was using develop of this:
https://github.com/jeromegn/Backbone.localStorage

Backbone.js relations

I'm having an issue wrapping my head around relational models in Backbone. I've just started using it and I'm tasked with a fairly large application.
The main issue I'm having is that I have a model that should contain a collection.
This is what I have to work with:
modelA
id: _id
url: api/model/:modelA_id
nested:
url: api/:modelA_id/nest
I think I'm making it a bigger deal than I need to, but I just can't seem to wrap my head around how to set this up.
Any help will be most appreciated.
The biggest thing to wrap your head around with Backbone is how to properly use events to deal with basically everything in the app. The other big thing to understand is that there are probably 5 different ways to attack a problem, where none of them are better/worse than the other.
Given that loose structure you've provided, I would do something like:
var YourApp = {
Models : {}
Collections : {}
Views : {}
};
YourApp.Models.First = Backbone.Model.extend({
initialize : function(){
var nestedCollection;
this.url = 'api/model/' + this.id;
nestedCollection = new Backbone.Collection({
url : this.url + '/nest'
});
this.set('nested', nestedCollection);
}
});
new YourApp.Models.First({
id : 23
});

How to build a Collection/Model from nested JSON with Backbone.js

I'm relativly new to Backbone.js
I have a JSON like the picture shows !
I saw some Answers in relation with Backbone-relational, but still dont get the point!
How can i convert this JSON to Backbone.js Collections/Models??
I update with a code but it dont work like expected! i can't see an model when i do :
My Structure is :
[0] : is a collection of models
[clefs] + ... + [Rest] : are collection of models
(clefs) => [0] + ... + [9] : are Models(title contains a string, path too)
Thanks a lot!!
EDIT(10.01.12) :
My Solution :
window.initModel = Backbone.Model.extend({
defaults: {
"title": "",
"path": ""
}
});
window.CustomCollection = Backbone.Collection.extend({
model: initModel
});
window.Init = Backbone.Model.extend({
url : function(){
return "/api/data.json"
},
parse: function(response) {
clefs = new CustomCollection();
clefs.add(response.clefs);
this.set({clefs: clefs});
.....
rests = new CustomCollection();
rests.add(response.rests);
this.set({rests: rests});
}
});
this helped me out too!
I'm at work, so I cannot give you a fully coded answer, but the gist is, you can do the following in your top level models to achieve a nested model hierarchy:
var AmericasNextTopModel = Backbone.Models.extend({
initialize: function(){
this.set({
clefs: new ClefCollection(this.get('clefs')),
accidentals: new AccidentalCollection(this.get('accidentals')),
notes: new NoteCollection(this.get('notes')),
rests: new RestCollection(this.get('rests'))
});
}
});
I do not use backbone-relational so I cannot give you an answer regarding that.
Are you making an online sheet music viewer/editor? :D Cool I'd love to see it when you're done.
The reset method (see 'reset') allows you to pass a JSON array to a collection.
This is the equivalent of a PUT method, replacing the specified collection with the JSON hash.
You can also use the add method to add to an existing collection, or pass the JSON hash into the constructor as you create a new collection.
You'll have to do some basic cleaning up of your array to get it in an appropriate format, and then convert it to JSON
I'm using PHP to grab a feed as JSON since it's on a different domain. I save those results to a JS variable, and then I just had success using this to get it into my Backbone collection...
var feedCollection = new Backbone.Collection();
feedCollection.set(myFeedJSON.nestedObject.nestedArrayIWant);

Categories