Getting my backbone views to load test data? - javascript

This is a new question but runs on from the last one, Dynamically load my 'IDs' into my Backbone Collection?
I now have all my models getting the data from my database. Now I want to be able to load this data into a view. But for some reason I can not get any views to work at all, this is my current view code,
var MyView= Backbone.View.extend({
el: '.page',
render: function() {
this.$el.html('CONTENT HERE FROM BACKBONE');
return this;
}
});
var testView = new MyView({});
$(document).ready(function(){
$('.page').append(testView);
})
Now '.page' is a div tag with that class set up on my page. But will not output my test text above, so what am I doing wrong?
The main aim is to have this view load the data form the model, which is all working fine, but right now I can not even get this simple test to work?
Have I forgotten to do with starting up the view functions with backbone?
All help most welcome.
Glenn.

you can initialize View inside document ready, View automatically call his render function when in initialize state, but in your case since document dose not load completely in initialize state, it fail to render
var testView;
$(document).ready(function(){
testView = new MyView
});

testView is an instance of MyView. You should not be calling append with testView. You need to invoke render method on testView instance. Since MyView el element is .page, content will be append to .page.
Use this:
$(document).ready(function(){
testView.render();
})

Related

How does Backbone View render work?

I have a model's view set up as follows:
var PlayerView = Backbone.View.extend({
el: "div.player",
template: _.template($("#playerTemplate").html()),
render: function() {
this.$el.append(this.template(this.model.toJSON()));
return this;
}
});
and the main view is set up as follows:
var AppView = Backbone.View.extend({
el: "#players",
render: function() {
var pView = new PlayerView({model: playerModel});
this.$el.append(pView.render().$el);
}
});
If you notice the render of AppView, I am rendering the PlayerView first, and then appending its $el to AppView's $el. I was expecting an error situation here, as it would display PlayerView 2 times to main View as follows:
First, in the pView.render() where I put content in pView and
Second, in the same line where I append pView's $el to main view.
But it just appends the pView only once. How does this work?
I am not sure I have explained my question clearly; I can add more context if required.
Assuming div.player exists in DOM as you mentioned in comments,
When you do pView.render(), it adds the template inside it.
Then when you append pView's element (div.player) to AppView's element (#players), entire div.player is moved into #players.
Your code is working the way it should work.
If you intent to create multiple players, You shouldn't use el option in player view, Instead you should decorate the element created by backbone and create multiple instances of player view.

How to check if a backbone view is rendered?

I am trying to append a view to an item in Backbone with a following code:
var viewContainer = this.$el.find('.view-container'),
pageWrap = this.$el.nextAll();
FIX
if (viewContainer.empty()) {
this.myView= new ProductsView();
viewContainer.append(application.myView.render().$el),
console.log(myView);
}
I am appending this view to the viewContainer with a toggle function, however, every time I click on the button, myView is appended again and again to the viewContainer instead of of only once. How do I check if the view is already rendered inside it before appending it? Is there a !this.rendered() equivalent I can use?
I found this thread but it is not helping me in this instance.
UPDATE - FROM console.log(viewContainer)
[div.view-container.product-container.active, div#subjects_menu.view-container.product-container.hidden.active, prevObject: p.fn.p.init[1], context: undefined, selector: ".view-container"]
From the looks of it, you want to make sure ProductsView is not created if it already exists.
Simplest way to do this would be:
if(!this.myView) {
this.myView= new ProductsView();
viewContainer.append(application.myView.render().$el),
}
It is better to work with application state than querying DOM. When you remove product view, simply do this.myView = null afterwards.
The only time you'd want to query DOM to know if a view is rendered is probably when you have to integrate an isolated external application over which you have no control that doesn't trigger any event/provide callbacks etc while rendering.

why <h1> tag is not display using backbone?

I am trying to load one html file using backbone js and require js file .I am able to call initialise function of view but not able to load that html file here is my code
define([
'jquery',
'underscore',
'backbone',
'text!templates/stats.html'
], function ($, _, Backbone, statsTemplate) {
'use strict';
var AppView = Backbone.View.extend({
// Instead of generating a new element, bind to the existing skeleton of
// the App already present in the HTML.
el: '#todoapp',
// Compile our stats template
template: _.template(statsTemplate),
// Delegated events for creating new items, and clearing completed ones.
events: {
},
// At initialization we bind to the relevant events on the `Todos`
// collection, when items are added or changed. Kick things off by
// loading any preexisting todos that might be saved in *localStorage*.
initialize: function () {
alert('-in--')
},
// Re-rendering the App just means refreshing the statistics -- the rest
// of the app doesn't change.
render: function () {
}
// Add a single todo item to the list by creating a view for it, and
// appending its element to the `<ul>`.
});
return AppView;
})
I am getting the alert in initialize function not able to load the html file
here is my code
http://plnkr.co/edit/rhoE1H9A8nfu6II64aEo?p=preview
Thanks for taking the time to set up a working example.
Unfortunately Backbone doesn't give you much for free, so there are a number of manual steps to get this working:
Add a <div id="todoapp"></div> to index.html as you are targeting it with el: '#todoapp' but it doesn't exist.
Doing template: _.template(statsTemplate) will return a compiled version of the template (as a function). You then need to call it like a function, optionally passing in a context so that the template can render dynamic data. e.g. this.template().
The render method won't get called automatically, so when you are ready to render the view (usually instantly but it could be after an AJAX response) you need to call this.render(). In your case straight away in initialize.
Finally in render you can attach the rendered template to the view's element: this.$el.html(this.template());
Updated example: http://plnkr.co/edit/6HyOhuQ7LGL91rS8slJX?p=preview
I recommend you create a BaseView with this common render flow so you don't have to repeat it every time. Also it's a good idea in that BaseView to set up a concept of sub views which clean up properly when the parent's remove is called, and re-render when a parent's render is called.
Here is an example of using a BaseView: http://jsfiddle.net/ferahl/xqxcozff/1/

backbone rendering cascade views

I'm working on a backbone application that cascades views. There's one root view which creates its child views inside its initialize method and call child view rendering inside its own render. It may look like the following:
initialize: function(options) {
console.log('body');
this.template = tpl({});
this.headerView = new HeaderView(options);
this.chartView = new ChartView(options);
this.footerView = new FooterView(options);
},
render: function() {
console.log("body");
this.$el.html(this.template);
this.headerView.setElement(this.$el.find('.header')).render();
this.chartView.setElement(this.$el.find('.chart')).render();
this.footerView.setElement(this.$el.find('.footer')).render();
return this;
}
All child views go the same way - they render themselves inside their render method and call render on their children.
My question is: why does entire page display after the very last view has finished rendering? My page is pretty complicated and it takes 4 seconds to load and I've got a blank page in the meantime. I've got lots of console outputs where I clearly see, that some of the views have already rendered.
I don't understand why such big amounts of HTML are not displayed on the fly. And I don't want it to be like that.
It's because your root view is not yet attached to the dom.
You instantiate the root view, that will created an own el element that is not attached anywhere in the dom (this is legal). You then start rendering child views, that get appended to the root view... but still the root element is missing, so nothing gets displayed on screen. If these views are super complex, this process will take some time. When everything is done, you return from your root render function and the root el is finally attached to the dom, displaying everything.
To solve this, you can have the rendering of the subview happening after the root el has been attached to the dom (then you will see each piece incrementally). I am not saying that this approach is better, just that is a way to solve this performance issue.
One of the approaches could look something like:
RootView = Backbone.View.extend({
// your initialize is here
render: function() {
this.$el.html(this.template);
return this;
},
createHeaderView: function() {
this.$el.find('.header').append(this.headerView.render().el);
}
});
var root = new RootView()
$('body').append(root.render().el)
root.createHeaderView()
....
Here you notice that tha root el is attached to the dom before the subviews are rendered. This will make them appear incrementally.

remove from collection bind to remove from view

I have a backbone collection and when I remove a model from the collection, I want it to remove the item from a list in the view.
My collection is pretty basic
MyApp.Collections.CurrentEvents = Backbone.Collection.extend({
model: MyApp.Models.Event
});
and in my views I have
MyApp.Views.CurrentEventItem = Backbone.View.extend({
el: 'div.current_event',
initialize: function(){
event = this.model;
_.bindAll(this, "remove");
MyApp.CurrentEvents.bind('remove',this.remove); //the collection holding events
this.render();
},
// yeah, yeah, render stuff here
remove: function(){
console.log(this);
$(this.el).unbind();
$(this.el).remove();
}
});
when I remove the model from the collection, it triggers the remove function, but the view is still on the page.
In the console, I can see the model, but I believe the model should have an 'el', but it doesn't.
My container code is
MyApp.Views.CurrentEventsHolder = Backbone.View.extend({
el: 'div#currentHolder',
initialize: function(){
MyApp.CurrentEvents = new MyApp.Collections.CurrentEvents();
MyApp.CurrentEvents.bind('new', this.add);
},
add: function(){
var add_event = new MyApp.Views.CurrentEventItem(added_event);
$('div#currentHolder').append(add_event.el);
}
});
for some reason in the add method I can't seem to use the $(this.el) before the append, though I'm not sure if that is the problem.
PROBLEM: MyApp.CurrentEvents.bind('remove',this.remove);
This triggers the remove() function every time any model is deleted from the collection.
This means that anytime a model is deleted, all the CurrentEventItem view instances will be deleted.
Now, about the view still being on the page:
It must have something to do with the way you appended/added/html-ed the view in the page. Check your other views, maybe if you have a CurrentEventsContainer view of some sort, check your code from there because with your current code, it does delete the view, albeit, all of them though.
RECOMMENDED FIX:
change your bindings to:
this.model.bind('remove',this.remove);
and make sure that when you instantiate it, pass on the model so that each view will have a corresponding model to it like so:
//...
addAllItem: function(){
_.each(this.collection, this.addOneItem())
},
addOneItem: function(model){
var currentEventItem = new MyApp.Views.CurrentEventItem({model:model});
//...more code..
}
//...
This makes things a lot easier to manage in my opinion.
UPDATE (from your updated question)
The reason you aren't able to use this.el is because you haven't passed the right context into the add() function. You need to add _.bindAll(this,'add') in your initialize() function to pass the right context, therefore making your this correct, allowing you to use this.el within the add function. Also, change your code to this:
MyApp.CurrentEvents.bind('new', this.add, this); this passes the right context. Also, why not use add instead as an event?
Continuing what I said in the comments, the way you've implemented this right now will remove all the CurrentEventItem views from the page when any of them is removed from the collection. The reason for this is the following:
MyApp.CurrentEvents.bind('remove',this.remove);
What this essentially says is, "every time the remove event is triggered on the collection, call this.remove." So, every time you instantiate one of these views, you're also telling the collection to remove that view when the collection triggers a remove event. I've created a fiddle to show you the problem.
You're right that Backbone knows which model has been removed from a collection, but you're not taking advantage of that. You can do that like so:
removeFromView: function(model) {
// Check to make sure the model removed was this.model
if (model.cid === this.model.cid) {
$(this.el).unbind();
$(this.el).remove();
}
}
See how this minor adjustment changes the behavior? Check it out in action here.
If you follow this pattern, you should see the proper views being removed.

Categories

Resources