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
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
I'm developing a web community in CakePHP and started to have doubts about using jQuery to provide useful widgets around the views when needed.
For example, I've wrote a jQuery plugin which searches the data inside specific input text, asks to my database and get the results handled with events.
Basically the plugin is perfect for simple applications but it's basically useless for a community where I use it almost in all the views and every time handling it with different events and methods, making it with huge event declarations and very annoying to be debugged.
I thougt to solve the problem by using default events from the widget and add the possibility set additional events specific for the view, but how can i do that?
this is the situation i thought
The Green area of the image is where I'm not sure, where I should put the default events to be retrived every time i need them? After known that then, in the view, I could add some event to the widget to be more easy to use.
For widget I intend every kind of html portion is loaded via javascript and is interactive, maybe an input search which retrieves a list of results or something like that.
My question is how can I set default events in runtime to the widget without copy and paste every time?
And my second question is, how can I add to them specific events for the view only?
Some tutorial somewhere online would be also nice.
My answer requires backbone.js so I am not sure if this will help you.
You could separate your widget into a wrapper and the real widget.
The wrapper could handle events like your close event:
var WidgetWrapper = Backbone.View.extend({
tagName: 'div',
// This should be a mustache template:
template: '<a class="close" href="#">close</a><div class="content"></div>',
events: {
'.close click': 'close',
'.open click' : 'open'
},
close: {
this.$el.hide();
},
open: {
alert('I am open');
}
render: {
this.$el.html(Mustache.to_html(view.template, view.model.toJSON());
}
});
The real widget could render itself inside the wrapper widget and both views could interact with the data model (this.model).
var SpecialWidget = Backbone.View.extend({
tagName: 'div',
// This should also be a mustache template:
template: '<input> open',
events: {
'input change': 'edit'
},
render: function() {
if(!this.wrapper) {
this.wrapper = new WidgetWrapper();
}
// Hand over the model to the wrapper
this.wrapper = this.model;
// Render the wrapper
this.wrapper.render();
// Insert the widget content inside the wrapper
this.$el.empty().append(this.wrapper.$el);
this.$(".content").html(Mustache.to_html(view.template, view.model.toJSON());
},
edit: function() {
alert("Changed");
},
});
This would allow you to separate your events.
You could also do it the other way round and use a wrapper with a sub view.
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);
I'm using Backbone 0.9.2 and I have a mustache template that uses twitter bootstrap and looks something like this:
<div class="modal hide something" id="something-modal">
...
</div>
I tried getting rid of the extra <div> that backbone adds because I want the view to be 1-to-1 as my template. My render function looks something like:
render: function(){
var $content = $(this.template()),
existing_spots = $content.find('.spots-list'),
new_spot;
this.collection.each(function (spot) {
new_sweetspot = new SpotView({ model: spot });
existing_spots.append(new_spot.render().el);
});
$content.find("[rel=tooltip]").tooltip();
this.setElementsBindings($content);
//this.$el.html($content).unwrap('div'); // didn't work!
this.$el.html($content);
console.log(this.$el);
return this;
}
I know that by adding:
tagName: "div",
className: "modal",
I'll get rid of it, but I want the control of the view's elements to be of the template, not of the JS code.
this.SetElement will cause the list NOT to be updated (it'll be empty), this.$el = $content; won't work as well.
There was a good thread on this last week on SO.
Backbone, not "this.el" wrapping
tl;dr you can use setElement, but you really need to know when things happen in backbone to make sure everything is wired up correctly.
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>