I currently set out my models like this:
var Contact = Backbone.Model.extend({
defaults: {
photo: "img/placeholder.png",
name: "",
address: "",
tel: "",
email: "",
type: ""
}
});
and my collection like so:
var Directory = Backbone.Collection.extend({
model: Contact
});
Then in my main app.js file, I'm doing this to define my routes:
var AppRouter = Backbone.Router.extend({
initialize:function () {
$('#header').html(new HeaderView().render().el);
},
routes: {
"filter/:type": "urlFilter"
},
urlFilter: function (type) {
directory.filterType = type;
directory.trigger("change:filterType");
},
});
I'm getting an error with the code below which tells me 'directory' is undefined.
directory.filterType = type;
directory.trigger("change:filterType");
In my view I new up the collection like so, this.collection = new Directory(contacts);
I'm pretty new to backbone and I'm not sure if Im doing things in the correct order, and that my collection doesn't exist at the time when I'm trying to do my filterType and Trigger or wether I have things completely wrong.
My code can be viewed here, http://dan.ms/backbone
Any pointers gratefully received.
The directory variable seems to be created in a callback to tpl.loadTemplates. The variable is not available in the scope where it's being accessed inside your router. One way to do it would be to pass directory to the AppRouter constructor as a parameter. In AppRouter's constructor, save it as a property, and access it in the urlFilter method via the current instance.
var AppRouter = Backbone.Router.extend({
initialize: function (options) {
this.directory = options.directory;
},
urlFilter: function (type) {
this.directory.set("filterType", type);
}
});
// Initialize with:
var directory = new DirectoryView;
var app = new AppRouter({ directory: directory });
Backbone.history.start();
Related
I have properly coded a simple REST api and several backbone models. My parent model is called Topic and child model called Questions.
I'm trying to call a get method on the REST api and display the received Topic object to the user in a presentable manner. I am receiving the json (can be seen in the network tab on Chrome), but it is not getting sent to the view correctly.
Model:
var Topic = Backbone.Model.extend({
urlRoot: ROOT + '/topic',
idAttribute: 'topicId',
initialize: function () {
this.questions = new Questions([], {parent: this});
},
toJSON: function () {
var json = Backbone.Model.prototype.toJSON.call(this);
json.questions = this.questions.toJSON();
return json;
}
});
var Topics = Backbone.Collection.extend({
model: Topic,
url: ROOT + 'topic',
parse: function (response) {
return response.results;
}
})
REST URL:
http://localhost/Project/index.php/rest/resource/topic/
Backbone View: This is where I think the error is...(console log below prints an empty object)
var TopicListView = Backbone.View.extend({
el: '.page',
render: function () {
var that = this;
var topics = new Topics();
topics.fetch({
success: function (topics) {
console.log(topics);
var template = _.template($('#topic-list-template').html(), {topics: topics.models});
that.$el.html(template);
}
})
}
});
Using the above functions:
var topic = new Topic();
topic.fetch();
topicListView = new TopicListView();
var Router = Backbone.Router.extend({
routes: {
"": "home"
}
});
var router = new Router;
// render topic list for 'home'
router.on('route:home', function () {
topicListView.render();
});
Edit: Solution: Overriding the parse function in the collection proved to be the error. I wonder why...
The argument topics in your success handler is shadowing the variable topics.
The argument contains the parsed JSON response, not the Backbone Collection. You don't need that, so you can remove the argument.
The reference to topics will now be to your Collection, so topics.models will have the value you expect.
topics.fetch({
success: function () { // argument removed here so `topics` is no longer shadowed
var template = _.template($('#topic-list-template').html(), { topics: topics.models });
that.$el.html(template);
}
})
I have a backboneJS app that has a router that looks
var StoreRouter = Backbone.Router.extend({
routes: {
'stores/add/' : 'add',
'stores/edit/:id': 'edit'
},
add: function(){
var addStoresView = new AddStoresView({
el: ".wrapper"
});
},
edit: function(id){
var editStoresView = new EditStoresView({
el: ".wrapper",
model: new Store({ id: id })
});
}
});
var storeRouter = new StoreRouter();
Backbone.history.start({ pushState: true, hashChange: false });
and a model that looks like:
var Store = Backbone.Model.extend({
urlRoot: "/stores/"
});
and then my view looks like:
var EditStoresView = Backbone.View.extend({
...
render: function() {
this.model.fetch({
success : function(model, response, options) {
this.$el.append ( JST['tmpl/' + "edit"] (model.toJSON()) );
}
});
}
I thought that urlRoot when fetched would call /stores/ID_HERE, but right now it doesn't call that, it just calls /stores/, but I'm not sure why and how to fix this?
In devTools, here is the url it's going for:
GET http://localhost/stores/
This might not be the answer since it depends on your real production code.
Normally the code you entered is supposed to work, and I even saw a comment saying that it works in a jsfiddle. A couple of reasons might affect the outcome:
In your code you changed the Backbone.Model.url() function. By default the url function is
url: function() {
var base =
_.result(this, 'urlRoot') ||
_.result(this.collection, 'url') ||
urlError();
if (this.isNew()) return base;
return base.replace(/([^\/])$/, '$1/') + encodeURIComponent(this.id);
},
This is the function to be used by Backbone to generate the URL for model.fetch();.
You added a custom idAttribute when you declared your Store Model to be like the one in your DB. For example your database has a different id than id itself, but in your code you still use new Model({ id: id }); when you really should use new Model({ customId: id });. What happens behind the scenes is that you see in the url() function it checks if the model isNew(). This function actually checks if the id is set, but if it is custom it checks for that:
isNew: function() {
return !this.has(this.idAttribute);
},
You messed up with Backbone.sync ... lots of things can be done with this I will not even start unless I want to make a paper on it. Maybe you followed a tutorial without knowing that it might affect some other code.
You called model.fetch() "a la" $.ajax style:
model.fetch({
data: objectHere,
url: yourUrlHere,
success: function () {},
error: function () {}
});
This overrides the awesomeness of the Backbone automation. (I think sync takes over from here, don't quote me on that).
Reference: Backbone annotated sourcecode
I am trying to load data from an API into a view. However the data doesn't turn up in my view.
I tried getting the collection information in de router, as well as in the model.
However the date won't even console.log the data. Let alone that I can load the data into the view.
I am using require to load the JavaScript files. Can you have a look and see what I am doing wrong here?
I do see this console.log:
console.log("People Collection is initialized");
And I can also see the page loaded and the json. But not the console.log of the data in the url function... In fact I get this error in the console:
Error: A "url" property or function must be specified
In the Backbone Router:
var OF = OF || {};
OF.AdminRouter = Backbone.Router.extend({
routes: {
"users": "goToUsers",
"users/*other": "goToUsers"
},
goToUsers: function() {
require(['./models/users', './views/users_view', './views/menu_view', './collections/user_collection'], function(UsersMdl, UsersView, MenuView, UsersCollection) {
OF.usersView = new OF.UsersView;
OF.usersView.render();
});
}
});
The Collection:
var OF = OF || {};
OF.UsersCollection = Backbone.Collection.extend({
initialize: function() {
console.log("People Collection is initialized");
},
url: function() {
var that = this;
var sendObj = {
"admin": OF.login.attributes.admin,
"session": OF.login.attributes.session
};
$.ajax({
url: 'php/api/users/',
type: "POST",
dataType: "json",
data: sendObj,
success: function(data) {
console.log(data);
},
error: function(data) {
console.log("ERR: " + data);
}
});
},
model: OF.UsersMdl
});
The Model:
var OF = OF || {};
OF.UsersMdl = Backbone.Model.extend({
default: {
username: '',
homefoldersize: '',
email: ''
},
initialize: function(){
//on change functions can be done here
OF.usersCollection = new OF.UsersCollection();
OF.usersCollection.fetch();
},
result: {
success: false,
message: ''
},
validate: function(att) {
}
});
The View:
var OF = OF || {};
OF.UsersView = Backbone.View.extend({
el: '#content',
remove: function() {
this.$el.empty();
this.stopListening();
return this;
},
initialize: function() {
//set the new address variable.
OF.usersMdl = OF.usersMdl || new OF.UsersMdl();
},
render: function() {
/*
//first check if you are allowed to see this page
if (!OF.login || !OF.login.isValid()) {
OF.router.navigate('login', {trigger: true});
return;
}
*/
//save this in that
var that = this;
//when importing of login page (and fill it with info) is done
$.when(OF.template.get('users-usersField', function(data) {
var htmlSource = $(data).html();
var template = Handlebars.compile(htmlSource);
var compiled = template(OF.usersMdl.attributes);
//now place the page
that.$el.html(compiled);
//then start the menu
})).then(function(){
setTimeout(function(){
OF.menuView = new OF.MenuView;
OF.menuView.render();
}, 100);
});
$('#logout').show();
}
});
Thanks.
It seems to call the initialize of the collection twice and then continues to call the json function.
In your model's initialization you have
OF.usersCollection = new OF.UsersCollection();
OF.usersCollection.fetch();
But when you fetch your collection, it's going to initialize models for every result it gets back ... which will then trigger fresh collection fetches.
You don't need to create collections for your models inside your models, especially if the model is being created by the collection. Whenever you add a model to a collection (including when the collection creates the model after a fetch) the collection will associate itself with the model.
The general order of things should be:
You define a url function on your collection which returns the URL where you can get the (raw JSON) models of that collection.
You instantiate that collection, and then call fetch on the instance
The collection makes an AJAX call (which you can affect by overriding fetch or sync) and gets back the raw JSON for all of the models.
The collection instantiates new models for each result it gets back; those models are automatically added to the collection, and their .collection is automatically set to the collection.
Once OF.usersCollection.fetch().done(function() {... you can have your views start doing things, as your collection should now be all set.
So, I'm trying to build routes in my Ember application dynamically with data from an API endpoint, /categories, with Ember Data. In order to do this, I'm adding a didLoad method to my model, which is called by the controller and set to a property of that controller. I map the route to my router, and all that works fine. The real trouble starts when I try to set up a controller with a content property set by data from the server retrieved by findQuery.
This is the error:
TypeError {} "Object /categories/548/feeds has no method 'eachRelationship'"
This is the code:
window.categoryRoutes = [];
App.Categories = DS.Model.extend({
CATEGORYAFFINITY: DS.attr('boolean'),
CATEGORYID: DS.attr('number'),
CATEGORYNAME: DS.attr('string'),
CATEGORYLINK: function () {
var safeUrl = urlsafe(this.get('CATEGORYNAME'));
categoryRoutes.push(safeUrl);
return safeUrl;
}.property('CATEGORYNAME'),
didLoad: function () {
var categoryLink = this.get('CATEGORYLINK');
var categoryId = this.get('CATEGORYID');
App.Router.map(function () {
this.resource(categoryLink, function () {
// some routes
});
});
App[Ember.String.classify(categoryLink) + 'Route'] = Ember.Route.extend({
setupController: function(controller, model) {
// source of error
this.controllerFor(categoryLink).set(
'content',
this.store.findQuery('/categories/' + categoryId + '/feeds', {
appid: 'abc123def456',
lat: 39.75,
long: -105
})
);
}
});
}
});
Any 'halp' is appreciated!
Also, if I'm doing this completely wrong, and there's a more Ember-like way to do this, I'd like to know.
I figured this out. I got this error because I was passing in a string instead of a real 'type' from the App.Helpers object to an extract method in some custom RESTAdapter code I had overridden.
The solution is to pass in the corresponding model helper in App.Helpers using my custom type name.
Something like this in the overridden RESTAdapter.serializer.extractMany method:
var reference = this.extractRecordRepresentation(loader, App.Helpers[root], objects[i]);
I am trying to learn backbone and I was following along the code school backbone.js course to build my own backbone app. So far I have this code but I am having problems with rendering anything.
var PostsApp = new (Backbone.View.extend({
Collections: {},
Models: {},
Views: {},
start: function(bootstrap){
var posts = new PostsApp.Collections.Posts(bootstrap.posts);
var postsView = new PostsApp.Views.Posts({collection: posts});
this.$el.append(postsView.render().el);
}
}))({el : document.body});
PostsApp.Models.Post = Backbone.Model.extend({});
PostsApp.Collections.Posts = Backbone.Collection.extend({});
PostsApp.Views.Post = Backbone.View.extend({
template: _.template("<%= name %>"),
render: function(){
this.$el.html(this.template(this.model.toJSON()));
}
});
PostsApp.Views.Posts = Backbone.View.extend({
render: function(){
this.collection.forEach(this.addOne, this);
},
addOne: function(post){
var postView = new PostsApp.Views.Post({model:post});
this.$el.append(postView.render().el);
}
});
var bootstrap = {
posts: [
{name:"gorkem"},
{name: "janish"}
]
}
$(function(){
PostsApp.start(bootstrap);
});
I am just trying to create a very simple backbone app, CodeSchool is great but it not good at combining the pieces together and when I try to do that myself I am having problems.
So far the error I am getting is "Uncaught TypeError: Cannot read property 'el' of undefined" in the addOne function of the Posts View. Any help would be much appreciated.
edit: The answer below solved my initial problem, but I also set up an express server to send data to the front end with the code :
app.get('/tweet', function(req,res){
res.send([{ name: 'random_name' }, {name: 'diren_gezi'}] );
});
and then I am trying to fetch this data to my collection like this :
var PostsApp = new (Backbone.View.extend({
Collections: {},
Models: {},
Views: {},
start: function(bootstrap){
var posts = new PostsApp.Collections.Posts(bootstrap.posts);
posts.url = '/tweet';
posts.fetch();
var postsView = new PostsApp.Views.Posts({collection: posts});
postsView.render();
this.$el.append(postsView.el);
}
}))({el : document.body});
But in the page the initial data (name: gorkem and name: janish) is displayed instead of the recently fetched data..
This is the problem line (I see it in a few spots).
this.$el.append(postsView.render().el);
Try changing it to
postsView.render();
this.$el.append(postsView.el);
Render function doesn't return a function to self (the object with a reference to el).