I'm working on converting a single-page app to backbone.JS. The View below uses the body tag as it's tagName - ie, I want the view to occupy the entire content of the page. I don't want to use container divs or other hacks.
var ThingView = Backbone.View.extend({
tagName : "body",
...
// Show the HTML for the view
render : function() {
console.log('Displaying thing')
$(this.el).append('<h1>test</h1>');
console.log('finished')
console.log($(this.el))
return this; // For chaining
When rendering, I see
finished
[
<body>
<h1>test</h1>
</body>
]
But after I inspect the DOM, the body no longer has the text.
tagName indicates what tag Backbone should use to create its el if no el is provided by the constructor. The created element is not automatically inserted into the DOM.
The simplest way is probably to create your view with its el set to body :
new ThingView({el:'body'})
What nikoshr explained is correct. Adding to it, Using tagName is correct. but Ideally you should use el element, which is defined in Backbone.js library as an element (el) of View.
Below is the ideal code you should use while doing this.
<script type="text/javascript">
$(document).ready(function(){
var ThingView = Backbone.View.extend({
el:$("body"),
initialize: function(){
_.bindAll(this,"render");
this.render();
},
render:function(){
this.el.append('<h1>test</h1>');
console.log('finished');
console.log($(this.el).html());
}
});
var ThingView = new ThingView();
});
</script>
Related
$el.find(".detail").append("<div class='noImageDetail'>teest</div>");
But not working. Checked in devtools, not found any bug. How can I use append in Backbone? Also, how can I use if - else?
You don't have any errors on your code as it is, now you need to make sure that exists something inside of your "el" with the "detail" class, example
HTML
<div id="book">
<div class="detail">Book:</div>
</div>
JS
var BookView = Backbone.View.extend({
el: '#book',
initialize: function() {
this.render();
},
render: function() {
this.$el.find(".detail").append("<div class='noImageDetail'>teest</div>");
}
});
var bookView = new BookView();
You need to use this.$el.find('selector') you could also use this.$('selector') which will save you some keystrokes since it's basically and alias to this.$el.find
I wrote an example in this plunker
The if - else is basic JavaScript https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else
Drives me crazy, take a look at this jsfiddle - it is very simplified version of my issue but it's absolutely demonstrative. Like this it works, but if you delete AppView definition
var AppView = Backbone.View.extend({
el: $("body"),
initialize: function() {
console.log('init');
this.render();
},
render: function() {
console.log('render');
this.$el.html("123");
}
});
from "main" script (check yourself - the same definition will be still included in "external resources" section) - it stops working. I've tried to check with console.log if the definition is still available in global namespace, and of course it is. Have no idea.
Update:
And even more - it is not just available - goddamn thing work as we can is in chrome dev console by console.log() but this.$el.html("123"); affect nothing!
If you inspect the bottom-right panel in your fiddle, you can see that the AppView.js file is included in the head of the generated document shown in the iframe.
The View is defined with the el element set to $('body')
el: $("body")
But the body Node does not exist, yet: it is an empty jQuery object.
Inside the jQuery document ready callback, the AppView definition finds the body element as you expect.
I understand your need to move the definition of the View in a separate javascript file.
You can achieve the result in many ways. The most obvious is to include the script element that imports it inside the body element. A nicer way could be to set the el in the initialize method of the AppView definition or, even better, you could pass the element to the constructor, as Kenan explains in his comment below.
I want to make a view for html document inside an iframe. For something like:
<div id="some">
<iframe id="other" />
</div>
I want to dynamically load an html document received from server into this iframe. The problem is, that I want to use Backbone View for that document. If I do something like this:
var projectView = Backbone.View.extend(
{
tagName: 'html',
initialize: function()
{
this.model.bind('sync', this.render, this);
},
render: function()
{
this.$el.html(this.model.get('content')); // content is from model, and it
//receives the html document from server
return this;
}
});
Then, when I do:
var iframe = $('#other')[0].contentWindow.document;
iframeDocument.open();
iframeDocument.write(projectView.render().el);
iframeDocument.close();
It does not work. I tryed a lot of different combinations but no success. If I use document.write() with static html it works fine but how to do with backbone view and dynamic content?
The "el" property is a reference to an HTML object. You're passing it into a document.write() call when that function is actually expecting an HTML string. Which is why static HTML works for you.
So you'd probably want to do something like this:
iframeDocument.open();
iframeDocument.write(projectView.render().el.innerHTML);
iframeDocument.close();
I think your approach is unnecessarily complex and using backbone isn't buying you anything useful. Why don't you just set the iframes src attribute with the model's content URL and be done with it?
render: function()
{
this.$el.attr('src', this.model.url()); //Or whatever you need to get the right URL
return this;
}
There is no reason to use an iframe.
You can just replace the iframe with a div and it will render every time your sync event is triggered.
You can also use on instead of bind for the event.
var projectView = Backbone.View.extend(
{
tagName: 'div',
initialize: function()
{
this.model.on('sync', this.render, this);
},
render: function()
{
this.$el.html(this.model.get('content')); // content is from model, and it
//receives the html document from server
return this;
}
});
and then just put the view into a container
$('#some').html(projectView.el);
you may have to remove some unnecessary content(ie. <head> tags) from the model whenever it syncs
I want to create a dojo widget with a very simple html template:
<div id="contentExternal"></div>
then on the widget load i want to load certain url from external service. Then after the url is loaded I want to put an with src argument equal to this loaded url. I have written:
dojo.provide("dojoModules.ExternalWebsitePane");
dojo.require("dijit._Widget");
dojo.require("dijit._Templated");
dojo.require("dojoModules.ConfigurationPane");
dojo.declare("dojoModules.ExternalWebsitePane", [dijit._Widget, dijit._Templated],
{
templateString: dojo.cache("dojoModules", "templates/ExternalWebsitePane.html"),
widgetsInTemplate: false,
constructor: function() {
}
,
startup: function() {
//Get Config
var serviceParams = new Object();
serviceParams.ServiceType = "GetConfig";
ecm.model.Request.invokePluginService("ExternalWebsitePlugin", "ExternalWebsiteService",
{requestParams: serviceParams, requestCompleteCallback: function(response) {
iframe = new Object();
iframe.src = response.configuration.value;
iframe.class = "iframe1";
var content = document.getElementById('contentExternal');
content.appendChild(iframe);
}});
}
});
But the code execution fails on
var content = document.getElementById('contentExternal');
content.appendChild(iframe);
where it says that content is null. I suspect that the div from html template is not loaded yet. How should i add an element? Or when?
Normally when you have templated widgets you should use Dojo attach points. If you have the following template (for example):
<div data-dojo-attach-point="contentExternalNode"></div>
You can then access that node from a property with the same name as your attach point, for example:
this.contentExternalNode.appendChild(iframe);
I usually do this stuff in the postCreate of the widget (I have no clue if it is already available at startup).
There is an article about writing your own widget (and more info about templated widgets) here.
Note: If you use Dojo 1.6 you need to use the dojoAttachPoint attribute and NOT the data-dojo-attach-point.
It looks like whenever I call view.remove() it not only removes the view from the dom but also the entire body
I'm calling showView after the creation of each view
showView : function(view) {
if (this.currentView) {
this.currentView.remove();
this.currentView.unbind();
if (this.currentView.onClose) {
this.currentView.onClose();
}
}
this.currentView = view;
$('body').html(this.currentView.render().el);
}
Since there is no longer a body element I cannot add another view
Chrome debugger output:
$('html')
<html>
<script id="tinyhippos-injected">…</script>
<head>…</head>
</html>
Once the view.remove() is ran the screen turns white and doesn't repopulate on $('body').html(this.currentView.render().el);
EDIT:
I changed each view from el: $('.body') to el: '.mainContent' and added a in the index.html file.
In the app.showView I add the mainContent div if it has been removed. Better to remove a div than the entire body.
if($('.mainContent').length == 0)
$('body').append('<div class="mainContent"></div>');
SOLUTION:
I needed to override my view remove method. I didn't want to remove the entire el, just the contents: Recreating a removed view in backbone js
Backbone.View.prototype.remove = function() {
this.undelegateEvents();
this.$el.empty();
return this;
};
Since you are adding the view via $('body').html(this.currentView.render().el); you don't need to set the el property in the view.
When you set the el property in the view, you are telling backbone to find that element and use it as the base element. When doing that, you don't need to add it to the page with $('body').html(), you can just call view.render(). But then when you call remove(), it will remove the el you set (in your case 'body' or '.mainContent').
If you don't specify an el, it generates a new element for you. In that case, you do need to add to the page with $('body').html(this.currentView.render().el);, but then when you call remove() it will only remove the generated element.
So, if you just remove el: '.mainContent' from your views, you can avoid having to check for and re-add that element. Also you will not have to override remove.
After removing el: '.mainContent' from the view, you can simply do this:
$('body').html(view1.render().el);
view1.remove();
$('body').html(view2.render().el);