What are el and $el in backbone's view? - javascript

I'm trying to avoid wrapping with empty div when render view in backbone.
I do it with the following code
this.$el.replaceWith(this.template(this.model.attributes));
return this;
but I get empty div when I append this view by
$("#product-pannel").append(productsView.render().el);
someone give the solution like this
render: function(){
var html = this.template(this.model.toJSON()));
var newElement = $(html)
this.$el.replaceWith(newElement);
this.setElement(newElement);
return this;
}
but I can't understand why should I do this so complicatedly above
can someone tell me the mystery of el an $el?

el points to the the view element (the one that holds rest of template) and $el is a jQuery object represeting el element So that you don't have to do $(this.el) all the time.
This is clearly mentioned in the documentation.
You can either specify an existing DOM element as the view element using el option, or backbone creates a DOM element for every view. By default this will be a <div>. If you don't want an empty <div>, customize the element backbone creates as the top level element of your view template using options like tagName, attributes etc.
setElement is for dynamically changing the view element to something else... I've rarely (or never) seen it actually being used.

Related

Backbone.js - render a new element to the page

Say you create a new element in your View - i.e. you don't target an existing element on the page with el.
var StudentView = Backbone.View.extend({
tagName: 'article',
className: 'student-name',
template: _.template($('#name-tpl').html()),
render: function(){
var student_tpl = this.template({name: 'Rikard'});
this.$el.html(student_tpl);
}
});
You then instantiate the View and call its render method:
var student_view = new StudentView();
student_view.render();
Your HTML contains the following template:
<div class="target">
<script id="name-tpl" type="text/template">
<%= name %>
</script>
</div>
This does not print the newly created element to the page. If we set el to .target, then it would work, but then it wouldn't print your tag name or class name that you set in the View.
Is there any way to print your newly created element to the page? I thought about using jQuery append, but it seems like there should be a more backbone-oriented solution.
Unless you use the el option to attach the view to an existing element in DOM, backbone.js creates an HTMLElement in memory, detached from DOM. It's your responsibility to attach it to where you want (backbone creates a new element for you, but it doesn't know where in DOM you want it to be added).
If you already have a Backbone.View and you want to set it to a different element, you can use setElement, but in this case as well, it is your responsibility to make sure that the element is part of DOM.
If backbone gave you a way to specify the position of an element in DOM, that'd look something like:
{
tagName: 'article',
elPosition: '#container > .content+div:nth-child(3)'
}
Again, there will be confusion like whether the element should be added before elPosition, or after etc. That looks ugly, no wonder why backbone leaves it to you rather than setting up more rules. In my opinion backbone sets less rules compared to other frameworks and give you more freedom (freedom can be a pain when you don't know what to do with it :)
What we normally do is, have the main parent view point to the containing element in DOM like <body> , using the el option.
Then append the child view's to it like this.$el.appendTo(parentView.$el);

What is the default element for Backbone view?

When I use this
this.setElement($('#some-id'));
in backbone's view, what am I changing the element from? The parent DOM?
Executing new Backbone.View() shows that the default element (the wrapper for the view) is just a <div> that isn't attached to the DOM.
Not setting the el property of a view doesn't necessarily effect how your view is displayed. Ultimately, you will have to append your view to the DOM inside your render function. The el property is just a convenient way to keep track of where your view should go/is.

How to insert a HTML fragment and hold a reference to the outer element?

I've always wondered how this jQuery feature works: $('<span>Hello world</span>')[0]
That is supposed to return a reference to the newly created span element. How can I achieve the same result using the native DOM methods? insertAdjacentHTML? innerHTML? documentFragment?
I need to insert a HTML fragment and hold a reference to the outer element without the need of using createElement/appendChild.
Thanks.
It's possible to create an element, set its innerHTML, and return the first child. The container element is never added to the DOM:
var el = document.createElement('div');
el.innerHTML = '<span>Hello world</span>';
console.log(el.firstChild);
If that's wrapped in a function, I believe the original container will be eligible for garbage collection as soon as the child is appended somewhere else.
jQuery seems to be doing something more sophisticated, checking if the string contains a single tag or not, and creating a fragment for more complicated strings. See the parseHTML method on jQuery's source code.

backbone remove view deletes the el

I am creating a single page application, and I am quite new to backbone. I have a problem with creating multiple views which uses the same wrapper-div.
My setup:
I have added a close function to all views:
Backbone.View.prototype.close = function(){
this.remove();
this.off();
if (this.onClose){
this.onClose();
}
}
I have a wrapper-div where I want to render views, remove them and render new ones. So my SetupView looks like this:
app.SetupView = Backbone.View.extend({
el: '#my_view_wrapper',
...
});
From the function where I swap views I close the current (open) view like this:
var v = this.model.get('view');
v.close();
Question
My problem is that I have multiple view's using the same wrapper-div. But when I close a view, this wrapper-div seems to be removed, and the next view I try to create can't find this div.
I guess there is an easy solution? I want to reuse the same wrapper, and only remove the view inside it, not the wrapper itself.
Just as an addition (for later reference) : another option is to overwrite the subviews remove so that it just empties $el instead of removing it. Eg.
remove: function() {
this.$el.empty().off(); /* off to unbind the events */
this.stopListening();
return this;
}
Personally I prefer this, as it removes the need to insert wrapper elements that have no real use.
In your scenario don't use an existing DOM element as your "el" value. Backbone will create the element for you. When you instantiate your view you can do the following to attach it to your existing wrapping element.
$(viewName.render().el).appendTo('#my_view_wrapper');

Appending to dynamically created divs using Javascript and mootools

Using mootools I have a 'builder' class that manufactures form objects, dynamically creating divs as it does so.
Some of the form objects are made up of several different objects. For example, a selection object features a textbox which filters the contents of the selector and a button to save the selection.
In this case I want the filter box and button to be located in a div which is appended to the div of the overall form object so as to have a 'wrapper'.
However, I'm having a problem appending to the div of the dynamically created form object.
After the dom is loaded, the 'builder' class is called:
window.addEvent('domready', function()
{
builder = new Build();
});
Builder creates a new div as such
var div = document.createElement('div');
var div_id = 'the_div_id_for_my_form_object';
div.setAttribute('id', div_id);
It then creates the form object which takes in the div as one of its parameters
var form_obj = superSelector(div);
Inside the form_obj constructor, this div is saved as a member variable, this.div = div.
The filter textbox is created as well as the button.
Here is where I'm seeing a problem. (since the issue is the same for both the filter textbox and the button, I'll describe only the textbox case)
The div of form_obj is passed to the constructor of the filter textbox.
When the filter textbox is created, it creates a div for itself
var div = document.createElement('div');
var div_id = 'the_div_id_for_my_filter_box';
When I attempt to append this div to the div of form_obj, I get a js error saying that I am attempting to append to 'null'
var filterBox = new Class({
initialize: function(name, form_obj)
{
this.name = name;
this.div = document.createElement('div');
this.div.setAttribute('id', name);
document.getElementById(form_obj.div).appendChild(this.div);
}
Yields:
"Uncaught TypeError: Cannot call method 'appendChild' of null"
I'm not sure how to get around this. I get the sense that the div I want to append to doesn't exist at the time I try to append to it. However I see no way of generating an event which tells me when it does exist so that I can postpone the construction of any 'child' divs until that point
right. several things you do that are not mootoolsy.
var div = document.createElement('div');
var div_id = 'the_div_id_for_my_form_object';
div.setAttribute('id', div_id);
should be:
var div = new Element('div', {
id: 'the_div_id_for_my_form_object'
}); // or even new Element('div#foobar');
div.setAttribute('id', div_id); -> div.set('id', div_id);
then appending to the dom:
document.getElementById(form_obj.div).appendChild(this.div);
why? what are you trying to do? grab an element and add to the div in memory?
document.id(form_obj.div).inject(this.div);
// if this element exist, it will be moved as a child to the new div, not safe
// you really ought to rewrite to:
var el = document.id(form_obj.div);
el && el.inject(this.div);
keep in mind this div is not injected to the dom yet at this point.
and so on. read the manual/api - you can always use native js but that kind of defeats the purpose of using a library that fixes things for you.
On a side note, doing what you are doing is not exactly easy, I am currently working with a friend (well, colleague!) of mine on something of a form-builder (for mootools, AMD) and it does what you will probably want to do, more or less - input types, groups, infinite dependencies triggered by values (on all el types), all sorts of form elements and custom looks / feels, validators, default values, placeholders, custom events.. Model/controller like behaviour, default values, server side data / validation, persisted per input data (sessionStorage / window.name)
gets created with AMD builder manifests that support versioning, pagination and languages over twitter bootstrap markup and elements and a single-page restful app via hashtags... basically, it is really a big task.
if we ever decide to open-source it (and I hope we can), and ppl have interest, who knows - you can pretty much build things like interactive tests, survey monkeys, quick forms, complex forms, whatever with it... its extendible and flexible. hope we finish it....
Try sending the "form_obj" parameter to getElementById instead of the div itself. The error seems to be indicating that the div cannot be found in the DOM by the method currently employed. getElementById takes the id attribute of the target div as a string.
e.g.
document.getElementById(form_obj).appendChild(this.div);
getElementById only takes a string parameter. It's returning null because you're passing in a reference to an element to the method.
I'd suggest you to get rid of getElementById. just use the reference you already have to the div you want to append the element to.
form_obj.div.appendChild(this.div);
Hope it helps.

Categories