Fetching items with Backbone seems to ignore the id passed in - backbone.js

I have these routes in my webservice and I can hit either of them directly through the browser and I return the correct value.
app.get('/repairs', repair.findAll);
app.get('/repairs/:id', repair.findById);
When I ask Backbone to do this I am unexpectedly getting a call to
app.get('/repairs', repair.findAll);
when I expect it to reach
app.get('/repairs/:id', repair.findById);
The piece of code that appears to be calling "/repairs" rather than "/repairs/:id" is
var EditRepair = Backbone.View.extend({
el : '.page',
render : function(options) {
var scope = this;
var repair = new Repair({id: options.id});
//This has the correct id
console.log(options.id);
//I would expect this to call /repairs/12344312
//However it calls /repairs
repair.fetch({
success : function(repair){
var template = _.template($('#edit-repair-template').html(), {repair : repair});
scope.$el.html(template);
}
});
}
});
var Repair = Backbone.Model.extend({
urlRoot : 'repairs'
});
var Router = Backbone.Router.extend({
routes: {
'edit/:id' : 'editRepair'
}
});
var editRepair = new EditRepair();
var router = new Router();
router.on('route:editRepair', function(id) {
console.log('Edit Id : ' + id);
editRepair.render({id:id});
});
The options.id can be console.logged and shows the correct id of the item. I've had a few issues so far with the difference between _id in mongodb and id in backbone which I have worked around but for the life of me I cannot see why this is issuing a call to repairs and not repairs/id.
Any help appreciated.

My fault, I had an ajax prefilter that was encoding the uri components.
This was messing up the requests being issued.
$.ajaxPrefilter( function( options, originalOptions, jqXHR ) {
options.url = "http://localhost:3000/" + encodeURIComponent( options.url );
console.log(options.url);
});
Changed to
$.ajaxPrefilter( function( options, originalOptions, jqXHR ) {
options.url = "http://localhost:3000/" + options.url;
console.log(options.url);
});

Related

Auto update backbone collection

I'm having a problem re rendering a simple collection in backbone, the render event is never fired from the listeners... I'm not sure of where is the mistake, could please someone help me?
File with models:
window.MetricDevice = Backbone.Model.extend({
defaults: {
ip: null,
framesReceived: null,
framesOutOfOrder: null,
framesLost: null
}
});
window.MetricDevicesCollection = Backbone.Collection.extend({
model: MetricDevice,
value: null,
url: function(){
return hackBase + "/wm/iptv/metric/devices/json";
},
initialize:function () {
this.fetch({ reset: true });
console.log("data fetched");
},
});
Render page:
window.MetricItemView = Backbone.View.extend({
events: {
"click input[type=button]" : "removeDevice",
},
initialize:function(){
this.template = _.template(tpl.get('metric-devices-item'));
this.render();
},
removeDevice:function(){
$.ajax({
url:hackBase + '/wm/iptv/metric/disable/' + this.model.get("ip") + '/0/json',
dataType:"json",
success:function (data) {
if ( data.return == 1 ){
alert(data.error);
}else{
alert("Metric disabled in " + this.model.get("ip"));
}
},
});
},
render:function(){
var ip = this.model.get("ip");
console.log("rendering item in view " + ip);
},
});
window.MetricView = Backbone.View.extend({
events: {
"click input[type=button]" : "add",
"click input[type=img]" : "updateAll",
},
clicked:function(e){
},
updateAll:function(e){
this.render();
},
initialize:function () {
this.template = _.template(tpl.get('metric-devices-list'));
this.model.bind("change", function(){
console.log("metricView data change detected");
this.render();
});
this.model.bind("reset", this.render());
},
add:function(e){
if($(e.currentTarget).attr("name") == "add" ){
var ip = document.getElementById('vaddress').value;
var threshold = document.getElementById('vthreshold').value;
$.ajax({
url:hackBase + '/wm/iptv/metric/enable/' + ip + '/' + threshold + '/json',
dataType:"json",
success:function (data) {
if ( data.return == 1 ){
alert(data.error);
}else{
alert("Metric enabled in device");
}
},
});
}else if($(e.currentTarget).attr("name") == "cancel"){
document.getElementById('vaddress').value = "";
document.getElementById('vthreshold').value = "";
}
},
render:function (eventName) {
$(this.el).html(this.template());
var list = $(this.el).find('#tableData');
console.log("On render!");
var subviews = [];
console.log("looping on models");
_.each(this.model.models, function (sw) {
console.log("model loop " + sw.get("ip"));
var m = new MetricItemView({model:sw, tagName: 'tbody', el: $(this.el).find('#tableData')});
list.append(m.template(sw.toJSON()));
}, this);
return this;
},
});
The problem is that the render method in MetricView is called just when the page is loaded for the first time, and after this I've the impression that the JSON stay cached, and the content just change if I close the browser clean the cache and run again...
The console output is:
On render! metricView.js:86
looping on models metricView.js:88
model loop 10.0.0.1 metricView.js:92
rendering item in view 10.0.0.1 metricView.js:26
And I'm instantiating MetricView like this
var metricdevices = new MetricDevicesCollection();
$('#content').html(new MetricView({model:metricdevices}).render().el);
Am i forgetting something?
The problem you are facing is that you are not using Backbone models and collection at their full potential. When you are calling manual $.ajax(...), no Backbone event will fire. Here are a couple suggestions that would make your code integrate with Backbone.
First, you should instantiate your view with the proper reserved keyword: collection
var metricdevices = new MetricDevicesCollection();
$('#content').html(new MetricView({ collection : metricdevices }).render().el);
Backbone events in collection are intended to work with precise REST API practices. A Model that belongs to a Collection will inherit it's url parameter. It expects your REST API to map to the following scheme:
model.save() --> model.id is present ? PUT collection.url/model.id : POST collection.url
model.delete() --> DELETE collection.url/model.id
model.fetch() --> GET collection.url/model.id
The idea is that you can manipulate models individually and use collection to fetch all the relevant models when needed. Your API does not seem to be adapted for that kind of workflow.
A monky patch that would keep your data updated is to trigger a fetch of the collection when an operation succeeds.
var collection = this.collection;
$.ajax({
//...
success: {
collection.fetch();
}
}
Since you are already listening to the Backbone events, it will trigger the reset event and render the view. Note that this is not a good way of doing things. What you should do is refactor your server access points to conform to standard good REST practice if you have access to it. If you don't, use proper Object Oriented patterns and implement model behavior in the model. For example:
window.MetricDevice = Backbone.Model.extend({
defaults: {
ip: null,
framesReceived: null,
framesOutOfOrder: null,
framesLost: null
},
enable : function() {
var device = this,
ip = this.ip,
treshold = this.treshold;
$.ajax({
url:hackBase + '/wm/iptv/metric/enable/' + ip + '/' + threshold + '/json',
dataType:"json",
success:function (data) {
if ( data.return == 1 ){
alert(data.error);
} else {
device.trigger('change');
alert("Metric enabled in device");
}
}
});
return this;
}
});
And then you can properly call the object from a view:
var ip = document.getElementById('vaddress').value;
var threshold = document.getElementById('vthreshold').value;
var metricDevice = new MetricDevice({ ip : ip, treshold : treshold });
this.collection.add(metricDevice.enable());

How To Change Backbone Dynamic URL with Backbone Events/Vent?

Edited This Below
In this image below I have two main regions.
One for the user list on the left: allusersRegion
And another for the the right side where a layout is displayed, which contains unique attributes to the user that was clicked in the allusersRegion and a list of articles by the user: middleCoreRegion
**If you noticed the middleCoreRegion is showing all articles by all users..This is wrong and I am trying to show all articles of the individual user (in this case. "kev")
I tried to see if my problem was with my JSON api (served via node/rest/mongoose) or with my underscore templates, but if it displays both list then I suppose I need to filter from inside backbone.
At first I tried using a Marionette.vent to simply change the url, but somhow I can't get the _id name into the url: function(), it says undefined...
var someuser = this.model.get("_id");
myApp.vent.trigger("showarticles", someuser);
I add a listener in the backbone collection on the same page:
myApp.vent.on("showarticles", someuser);
**The Edit (A Different Way of Doing this) Here is my code
var usertab = Poplive.module('usertab', {
startWithParent: true,
});
usertab.addInitializer(function() {
User = Backbone.Model.extend({});
UniqueArticle = Backbone.Model.extend({});
//Collections
Users = Backbone.Collection.extend({
model: User,
url: '/api/user2'
});
UniqueArticles = Backbone.Collection.extend({
model: UniqueArticle,
url: '/api/survey'
});
//Layout
var VisitingLayoutView = Backbone.Marionette.Layout.extend({
template: "#visiting-layout",
regions: {
firstRegion: "#listone",
secondRegion: "#listtwo",
thirdRegion: "#listthree",
playRegion: "#playhere",
articlesRegion: "#articleshere"
}
});
AllUserView = Backbone.Marionette.ItemView.extend({
template: "#tab-alluser-template",
tagName: 'li',
events: {
"click #openprofile" : "OpenProfile"
},
OpenProfile: function(){
console.log("Profile is open for " + this.model.get("username"));
var modelo = this.model.get("_id");
var vlv = new VisitingLayoutView({model: this.model});
Poplive.middleCoreRegion.show(vlv);
var ua = new UniqueArticles();
var uacoll = new UniqueArticlesView({collection: ua});
vlv.articlesRegion.show(uacoll);
}
})
//ItemViews
UniqueArticleView = Backbone.Marionette.ItemView.extend({
template: "#unique-article-template"
});
//CollectionViews
AllUsersView = Backbone.Marionette.CompositeView.extend({
template: "#tab-allusers-template",
itemView: AllUserView
});
UniqueArticlesView = Backbone.Marionette.CollectionView.extend({
template: "#unique-articles-template",
itemView: UniqueArticleView
});
//Render Views
var alluserview = new AllUserView();
var allusersview = new AllUsersView();
//Fetch Collections
var theusers = new Users();
theusers.fetch();
var userscoll = new AllUsersView({collection: theusers});
Poplive.allusersRegion.show(userscoll);
});
Assuming UniqueArticle to be the Backbone Model, for the Model with a specific id to be fetched you would need to define the urlRoot property which will append the id of the model to the request.
So the id attribute will be appended to the end of the request the model from the server when you do a fetch on it
var UniqueArticle = Backbone.Model.extend({
idAttribute : 'someuser',
urlRoot : function(someuser){
return '/api/visitingarticles/'
}
// this would send a request for
// /api/visitingarticles/someId
});
var UniqueArticles = Backbone.Collection.extend({
model: Article,
url : function(someuser){
return '/api/visitingarticles/'
}
// /api/visitingarticles -- All Articles will be fetched
});
I think what you want, is to define url as a function, and have a user attribute on your collection:
var UniqueArticles = Backbone.Collection.extend({
model: Article,
initialize: function(){
var self = this;
myApp.vent.on("showarticles", function(someuser){
self.user = someuser;
self.fetch();
}
},
url : function(){
var fragment = '/api/visitingarticles/';
if(this.user && this.user.id){
return fragment + this.user.id;
}
return fragment;
}
});
(Disclaimer: untested code, but it works in my head :D)
Then each time you trigger the event, the userattribute is updated, the collection is reset with the updated url.
As a side note, you might want to look into using a filtered collection. I've implemented that idea in my book, based on Derick Bailey's code here: http://jsfiddle.net/derickbailey/7tvzF/
Here is my version: https://github.com/davidsulc/marionette-gentle-introduction/blob/master/assets/js/entities/common.js
And an example of its use (lines 38-41): https://github.com/davidsulc/marionette-gentle-introduction/blob/master/assets/js/apps/contacts/list/list_controller.js#L38

Connection between model and collection in backbone and parse.com

i'm trying to connect model and collection using parse.com but i'm confused. I'm tring to fetch by collection using backbone and javascript api parse.com but compare this error:POST https://api.parse.com/1/classes 404 (Not Found).
Model:
var Person = Backbone.Model.extend({
defaults:{
},
initialize:function(){
console.log("inperson");
this.validate();
this.send();
},
validate:function(){
console.log("validate");
},
send:function(){
var user = new Parse.User();
user.set("username", this.get("username"));
user.set("password", this.get("password"));
user.set("email", this.get("email"));
user.signUp(null, {
success: function(user) {
// Hooray! Let them use the app now.
},
error: function(user, error) {
// Show the error message somewhere and let the user try again.
alert("Error: " + error.code + " " + error.message);
}
});
}
});
return Person;
});
Collection:
var Usercollection = Parse.Collection.extend({
model:Person,
initialize:function(){
}
});
return Usercollection;
});
and finally the view that call the colletion and fetch:
var HomeView = Backbone.View.extend({
template: Handlebars.compile(template),
events: {
},
initialize: function() {
console.log("inhomeview");
var amici = new Usercollection();
amici.fetch({
success: function(collection) {
amici.each(function(object) {
console.warn(object);
});
},
error: function(amici, error) {
// The collection could not be retrieved.
}
});
},
render: function() {
}
});
return HomeView;
});
Cant you just swap the backbone collection and model to Parse's ones? (You only used the Parse type of the collection, not the model!)
Try switch that Backbone model to a Parse.Object .
Step by step below:
First of all Lets create a new app on Parse.com, mine is called FunkyAppartments.
Insert the script tag for loading Parse javascript lib into index.html or whathever:
<script src="http://www.parsecdn.com/js/parse-1.5.0.min.js"></script>
Switch the backbone model and collection to use parse types instead (and rename the fetch method if you have extended backbones, since we do not want to overide the one of parse):
//var Appartment = Backbone.Model.extend(); Backbone wo. Parse.com
var Appartment = Parse.Object.extend("Appartment");
//var Appartments = Backbone.Collection.extend({ Backbone wo. Parse.com
var Appartments = Parse.Collection.extend({
model: Appartment,
loadAppartments: function(callback){
debugger;
this.query = new Parse.Query(Appartment);
this.fetch();
}
});
I added a debugger tag in the load appartments so that developer tools breaks in the middle of the controller, here I have access to the Appartment private type of the controller, hence i can store some data on the parse server and verify by pasting the below in the developer tools console.
var testAppartment = new Appartment();
testAppartment.save({name: "foobars"}).then(function(object) {
alert("yay! it worked");
});
Yei, the data shows up in the parse.com UI for the app we just added there. And more importantly it shows up in our frontend. That was easy!
UPDATE: PROBLEMS W BACKBONE 1.2.1, MARIONETTE 2.4.2, UNDERSCORE 1.8.3
I noticed that I actually had been using old versions of marionette, backbone and underscore.js. An initial update appeared to break the application.
After some research i found that it was the parse part that did not return objects that would successfully render. Hence I changed the collection type back to an extension of: Backbone.collection instead of Parse.collection.
I also had to override the query method, since the objects would not save on the correct id, updating an object resulted in a new object being added instead of an old one being updated.
var Apartment = Parse.Object.extend('Appartment');
var Apartments = Backbone.Collection.extend({
model: Apartment,
query: new Parse.Query(Apartment),
initialize: function(){
MyApp.vent.on('search:param', function(param){self.search(param); });
var self = this;
this.query.find({
success: function(results){
self.reset();
results.forEach(function(result){
result.attributes.id__ = result.id
var ap = new Apartment(result.attributes);
self.add(ap);
});
}
});
}
});
I added an attribute: id__ to hold the parse id (naming it just id did not work since it backbone interfered with it, making it disappear).
Finally in saving the model to parse i utilized id__ as id in the save call:
var ApartmentEditView = Backbone.Marionette.ItemView.extend({
template: "#apartment-edit-template",
className: "apartmentDetail",
events: {
"click .store": "storeEdit",
"click .close": "closeEdit"
},
storeEdit: function(){
var priceNum = Number($('#price_field').val().replace(/\s/g, ''));
this.model.set({
id: this.model.attributes.id__,
name:$('#name_field').val(),
price:priceNum,
description:$('#desc_field').val(),
url:$('#url_field').val()
});
this.model.save();
this.closeEdit();
},
closeEdit: function(){
var detailView = new ApartmentDetailView({model: this.model});
MyApp.Subletting.layout.details.show(detailView);
}
});
Now the object is updated correctly in the database.

Backbone.js - custom collection not working

In the backbone.js, for the purpose of filtering the data, i am fetching by click the element.
and i am saving the collection as newcollection. but i unable to get any data.
what is wrong with my code...
my code :
taskListPhraseI.collection = Backbone.Collection.extend({ // collection fetching
model:taskListPhraseI.model,
url : 'data/data.json',
});
taskListPhraseI.allView = Backbone.View.extend({
el:$('.boardHolder'),
events:{
'click span.green' : 'filterIt'
},
initialize:function(){
var that = this;_.bindAll(this);
this.collection = new taskListPhraseI.collection(); //initial stage i am fetching
this.collection.fetch({success:that.render});
this.on('change:filterType', this.setNewType); //on click trigger my custom method to get the collection again
//this.on('reset:filterType', this.setNewType);
},
setNewType:function(){
var newCollection = new taskListPhraseI.collection(); // my custom collection
newCollection.fetch(); // fetching
this.collection.reset(newCollection,{ silent: true }) // triggering rest
var filterType = this.filterType,
filtered = _.filter(this.collection.models, function (item) {
return item.get("dueDays") === filterType;
});
console.log(newCollection.models); // not working... why?
console.log(this.collection.models);// works
this.collection.reset(filtered);
},
or the way i am doing wrong.. to filter the collection
any one guide me a correct way of process...
thanks in advance
fetch is async. Execute your code after collection will be fetched
newCollection.fetch({context:this}).done(function() {
// your code
})
Plus this is not correct reset method usage:
this.collection.reset(newCollection,{ silent: true })
Use this way:
this.collection.reset(newCollection.toJSON(), {silent:true})
EDIT (Added example)
HTML
<button>change filter</button>
JS
var url1 = 'https://api.twitter.com/1/statuses/user_timeline.json?screen_name=vpetrychuk&count=9'
var url2 = 'https://api.twitter.com/1/statuses/user_timeline.json?screen_name=derickbailey&count=9'
var collection = new (Backbone.Collection.extend({
url : url1,
resetWithFilter : function(key, value) {
var query = {};
query[key] = value;
this.reset(this.where(query));
}
}));
// fetch initial data
collection.fetch({dataType:'jsonp'});
$(':button').on('click', function() {
// change url and fetch another data
collection.url = url2;
collection.fetch({dataType:'jsonp'}).done(function(response) {
console.log('items count before filter:', collection.length);
// now reset collection with selected filter
collection.resetWithFilter('id_str', '294429621640912896');
console.log('items count after filter:', collection.length)
});
});
Fiddle: http://jsfiddle.net/vpetrychuk/N4ZKm/

Fetch backbone collection fail from django-tastypie

I'm using backbone-tastypie from https://github.com/PaulUithol/backbone-tastypie, and I can't fetch a collection data.
Thats my code:
var User = Backbone.Model.extend({
url: '/api/v1/user'
});
var HoraExtra = Backbone.Model.extend({
url: '/api/v1/horasextra/'
});
var HorasExtra = Backbone.Collection.extend({
url: '/api/v1/horasextra/',
model: HoraExtra
});
var Horas = new HorasExtra();
var activeUser = new User();
var HorasExtraView = Backbone.View.extend({
initialize: function() {
_.bindAll(this, "render");
},
render: function() {
var plantilla = Handlebars.compile($("#horas_extra_template").html());
var html = plantilla(Horas);
this.$el.html(html);
console.log(JSON.stringify(Horas));
}
});
var HorasExtraWidget = new HorasExtraView({el: $('#base')});
Horas.fetch({
data: {
"usuario__id": 2,
"hor_com__month": 11
}
});
HorasExtraWidget.render();
And that's the result of console.log(JSON.stringify(Horas):
[]
Many thanks
I would suggest to you that you take tastypie, backbone and build your own simple project in 1-2 days.
Then you will get to know basic issues of coupling those 2 frameworks.
Without that knowledge it is pointless you try out other people's projects and then wonder "what isn't working".
And from my personal experience, both tastypie and javascript are pretty straightforward and are easy to couple.
And I am NOT an expert.
couple of points: try explicitly fetching models with fetch, manipulate fetched model from success callback, and watch your model url's, forward slashes in them etc.
Seems your collection is empty.
See in console if there is a GET call to server.
If not, you're not actually fetching anything from the server.
I used https://github.com/amccloud/backbone-tastypie and then your code should look like this, I guess. Not tested.
var HorasCollection = Backbone.Tastypie.Collection.extend({
url: '/api/v1/horasextra/'
});
var HorasExtraView = Backbone.View.extend({
el: $('#base'),
entries: new HorasCollection(),
render: function() {
var that = this;
this.entries.fetch({
success: function(entries){
console.log("Entries", entries.models);
// var t = _.template(template, {hello: 'world'});
// that.$el.html(t);
},
error: function(model, response){
console.log("Error", response);
}
});
}
});
If you're trying to create a table for your data, try my jquery plugin :)
https://github.com/sspross/tastybackbonepie

Resources