I'm getting this error, when I want to initialize the view from router class.
Error is:
Uncaught TypeError: Object # has no method '_ensureElement'
BlogFormView:
App.BlogFormView = Backbone.View.extend({
el: ".data-form",
initialize: function(){
this.template = _.template($("#blog_form_template").html());
this.render();
},
render: function(){
this.$el.html(this.template({blog: this.model.toJSON()}));
return this;
},
events: {
"click .submit-blog" : "submitForm"
},
submitForm: function(ev){
}
});
Router:
var blog = new App.Blog();
var blogFormView = App.BlogFormView({model: blog});
You are missing new keyword in router code:
var blogFormView = new App.BlogFormView({model: blog});
Also, it usually isn't best idea to call render inside the initialize method. I personally would just call render inside the router code.
Related
I am trying to make ajax request to server. I am using fetch() method on collection. It successfully retrieves data from server after monitoring google chrome network tab.
Here is scenario.
I have Model below
var Model = Backbone.Model.extend({
urlRoot: '/contacts',
});
collection below
var Collection = Backbone.Collection.extend({
model: Model,
url: '/contacts'
});
ViewOne below
var ViewOne = Backbone.View.extend({
initialize: function () {
var collection = new Collection().fetch(); // also tried with these options { wait:true, reset: true, async: false }
new ViewTwo({ collection: collection });
}
});
Below is ViewTwo
var ViewTwo = Backbone.View.extend({
initialize: function () {
this.collection.each(this.render, this); // Uncaught TypeError: undefined is not a function
},
render: function () {
console.log(this.model);
}
});
As you can see I am getting Uncaught TypeError: undefined is not a function error.
this.render exist so that is not a problem. The problem is each function on this.collection. How can I solve this proble?
simply create new view in success callback of fetch
var ViewOne = Backbone.View.extend({
initialize: function () {
var collection = new Collection();
collection.fetch({
success:function(){
new ViewTwo({ collection: collection });
}
});
}
});
Backbone API is not fluent, which means that Collection.fetch does not return itself, it returns the Ajax object used in fetch.
Try
var ViewOne = Backbone.View.extend({
initialize: function () {
var collection = new Collection();
collection.fetch();
new ViewTwo({ collection: collection });
}
});
And note that the collection.fetch is asynchronous, this.collection will be probably empty in ViewTwo.initialize. See Backbone.js - data not being populated in collection even though fetch is successful for example.
I have a JSON file, that I need to parse it into collection and render it to HTML pageand then I need to add a button, that will sort this collection and redraw it on page.
That the code, that I made:
That's the part about model, collection and sorting:
var Profile = Backbone.Model.extend();
var ProfileList = Backbone.Collection.extend({
model: Profile,
url: 'profiles.json',
selectedStrategy: "count",
comparator: function (property){
return selectedStrategy.apply(model.get(property));
},
strategies: {
count: function (model) {return model.get("count");},
name: function (model) {return model.get("name");}
},
changeSort: function (sortProperty) {
this.comparator = this.strategies[sortProperty];
},
initialize: function () {
this.changeSort("count");
},
});
It's the View and the Button:
var ProfileView = Backbone.View.extend({
el: "body",
template: _.template($('#profileTemplate').html()),
Sort: null,
initialize: function() {
this.Sort = new ReSortView();
this.bind('all', this.render());
},
render: function() {
_.each(this.model.models, function(profile){
var profileTemplate = this.template(profile.toJSON());
$(this.el).append(profileTemplate);
}, this);
return this;
},
ReSort: function (){
console.log("111");
this.model.changeSort("name");
},
events: {
"click .Sort": "ReSort",
//"click.NSort": "NSort"
},
});
var ReSortView = Backbone.View.extend({
el: $("#Sort")
});
var AppView = Backbone.View.extend({
el: "body",
initialize: function() {
var profiles = new ProfileList();
var profilesView = new ProfileView({
model: profiles
});
profiles.bind('all', function () {
profilesView.render();
});
profiles.fetch({success: function (model,resp) { console.log(resp);}});
}
});
var App = new AppView();
});
The question is why when I run it, everything seems to be ok, but the sorting does't work, and FireBug saying nothing and Button just writing into the consol.
P.S. I'm new in WEB developing and exactly in JS\Backbone.js
Just changing the comparator:
changeSort: function (sortProperty) {
this.comparator = this.strategies[sortProperty];
}
won't re-sort the collection, the collection has no way of know that the comparator has changed unless you tell it. You need to force the issue by calling sort:
changeSort: function (sortProperty) {
this.comparator = this.strategies[sortProperty];
this.sort();
}
And a few other things while I'm here:
Your initial comparator:
comparator: function (property){
return selectedStrategy.apply(model.get(property));
}
is invalid (unless you have a global selectedStrategy defined somewhere), you should probably just leave it out and let initialize set it up by calling changeSort.
this.bind('all', this.render()); does nothing useful, bind wants a function as the second argument but this.render() calls the render method. You probably don't want a this.bind call there at all and if you do, you'd want to say this.bind('all', this.render).
Views handle the collection option similarly to how the handle the model option in their constructor:
There are several special options that, if passed, will be attached directly to the view: model, collection, el, id, className, tagName and attributes.
so, if your view is collection-based, you'd want to say new View({ collection: ... }) and use this.collection instead of this.model to avoid confusion.
Collections have various Underscore functions built-in so don't say:
_.each(this.model.models, ...
when you can say this instead:
this.collection.each(...
View's have a jQuery wrapped version of el built in so you can use this.$el instead of $(this.el) (which rebuilds the jQuery wrapper each time you call it).
You are calling the changeSort method on the model but that method is on your collection (as it should be)
I'm new to backbone.js and handlebars and I'm having a problem getting my template to render out the data.
Here is my collection and model data from tagfeed.js module:
// Create a new module.
var Tagfeed = app.module();
// Default model.
Tagfeed.Model = Backbone.Model.extend({
defaults : {
name : '',
image : ''
}
});
// Default collection.
Tagfeed.Collection = Backbone.Collection.extend({
model : Tagfeed.Model,
url : Api_get('api/call')
});
Tagfeed.TagView = Backbone.LayoutView.extend({
template: "tagfeed/feed",
initialize: function() {
this.model.bind("change", this.render, this);
},
render: function(template, context) {
return Handlebars.compile(template)(context);
}
});
Then in my router I have:
define([
// Application.
"app",
// Attach some modules
"modules/tagfeed"
],
function(app, Tagfeed) {
// Defining the application router, you can attach sub routers here.
var Router = Backbone.Router.extend({
routes: {
"index.html": "index"
},
index: function() {
var collection = new Tagfeed.Collection();
app.useLayout('main', {
views: {
".feed": new Tagfeed.TagView({
collection: collection,
model: Tagfeed.Model,
render: function(template, context) {
return Handlebars.compile(template)(context);
}
})
}
});
}
});
return Router;
});
THis successfully makes a call to the api, makes a call to get my main template, and makes the call to get the feed template HTML. If I don't include that render(template, context) function, then it renders on the page as the straight up HTML that I have in the feed template with the {{ name }} still included. however when its included, I get the error
TypeError: this._input.match is not a function
[Break On This Error]
match = this._input.match(this.rules[rules[i]]);
and if I examine the variables that get passed into the appLayout views render function for feed, I see that the template var is a function, and the context var is undefined, then it throws that error.
Any ideas what I'm doing wrong? I know I have at least one problem here, probably more.
Since you're using requirejs, you can use the text module to externalise your templates or better still pre-compile them and include them in your view. Check out http://berzniz.com/post/24743062344/handling-handlebars-js-like-a-pro
E.g. using pre-compiled templates
// router.js
define(['views/tag_feed', 'templates/feed'], function(TagFeedView) {
var AppRouter = Backbone.Router.extend({
// ...
});
})
// tag_feed.js
define(['collections/tag_feed'], function() {
return Backbone.View.extend({
// ...
render: function() {
this.$el.html(
Handlebars.templates.feed({
name: '...'
})
);
}
});
})
For reference I've created simple boilerplate for a backbone/require/handlebars setup https://github.com/nec286/backbone-requirejs-handlebars
I'm trying to create a Spine's Controller and init() it,
jQuery(function($) {
window.Tests = Spine.Controller.create({
init: function() {
console.log('Tests created!');
}
});
window.App = Tests.init();
});
but I have an error
Uncaught TypeError: Object function result() {
return result.super.constructor.apply(this, arguments);
} has no method 'init'
Spine is included before my Controller's file.
Help me to solve it, why Controller.create() (and Controller.sub()) returns function and not an object?
I've tried to make it like in example:
jQuery(function($){
window.App = Spine.Controller.create({
el: $("body"),
elements: {
"#sidebar": "sidebarEl",
"#contacts": "contactsEl"
},
init: function(){
this.sidebar = Sidebar.init({el: this.sidebarEl});
this.contact = Contacts.init({el: this.contactsEl});
Contact.fetch();
}
}).init();
});
I solved it by doing
new Tests();
I have problems when i try to fetch data to my Backbone model from the server. You get a response in JSON from from the server which I think looks to be right formatted. Can you see anything wrong with it? It looks like:
[{"id":"1","name":"Fawad Hassan","email":"fawad#test.com"},{"id":"2","name":"Bill
Gates","email":"bill#test.com"},{"id":"3","name":"Steve
Jobs","email":"steve#test.com"},{"id":"4","name":"Naveed
Ahmad","email":"naveed#test.com"},{"id":"5","name":"Mr Zee","email":"zee#test.com"}]
My code for the Backbone project looks like this, and I can't really find the problem there either.
window.AppModel = Backbone.Model.extend({
url: function() {
return 'http://dev.local/ci/index.php/site/userpost';
}
});
window.AppContr = Backbone.Collection.extend({
model: AppModel,
initialize: function() {
this.model = new AppModel;
}
});
window.App = new AppContr({name: "Markus"});
window.AppView = Backbone.View.extend({
el: $("#content"),
initialize: function() {
this.render();
},
render: function() {
console.log(App.model.toJSON());
}
});
App.model.fetch();
window.View = new AppView;
You are doing a fetch on a Model, but returning Collection in response. That is main problem.
Second problem is that you are calling render on AppView totally random, i.e. it does not have anything to do with model or collection. Maybe there would be nothing in model when you render view. You should bind rendering to collection or model with bind. Than whenever you call fetch your view will re-render, which is one of main benefits of Backbone :)
Here comes the code :)
window.Person = Backbone.Model.extend({});
window.Addressbook = Backbone.Collection.extend({
url: 'http://dev.local/ci/index.php/site/userpost',// declare url in collection
model: Person
}
window.Addresses = new AddressBook();
window.AppView = Backbone.View.extend({
el: $('#content'),
initialize: function() {
Addresses.bind('reset', this.render); // bind rendering to Addresses.fetch()
},
render: function(){
console.log(Addresses.toJSON());
}
});
window.appview = new AppView();
Addresses.fetch();