Access attributes of the DOM elements from ember.js views - javascript

How can I access attributes of DOM elements that are being inserted by ember.js view, from within the view itself. Here is a short example. Lets say I have the following cat.handlebars template:
{{#collection contentBinding="App.catsController"}}
<div class="cat" {{bindAttr id="view.content.id"}}></div>
{{/collection}}
which is used in this view:
App.CatView = Ember.View.extend({
templateName: 'cat',
catsBinding: 'App.catsController',
didInsertElement: () ->
#need to get the id of each DIV that is being inserted to add some JavaScript for it
console.log 'context it ', this.$()
})
this.$() returns a very deeply nested object and I can not find any sign of my DIVs in it. Also view.content.id is not defined when I am inside the didInsertElement function.
To reiterate my question, when I am inside a view, how can I add some Javascript code related to some of the DOM elements that are being inserted by the view.

You can use {{view.contentIndex}} instead of {{view.content.id}}. Since you have added a classname cat to your div you can access it by using this.$('#index.cat'), where index should be replaced by the index of the content you want to access.

Related

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

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.

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);

Creating dialog with knockoutjs components

I already use custom binding with knockout for displaying jqueryui dialog, but I would like to use knockout component feature.
So, I would like to write something like:
<window params="isVisible: isVisible">
//there will be some html
</window>
And later somewhere in code:
self.isVisible(true); //That would open window
//or
self.isVisible(false); //That would close window
Problem is that I don't know how to apply $(element).dialog. When I register knockout component, I can only get container element of this component, but not injected element.
ko.components.register('window', {
viewModel: {
createViewModel: function(params, componentInfo) {
// - 'params' is an object whose key/value pairs are the parameters
// passed from the component binding or custom element
// - 'componentInfo.element' is the element the component is being
// injected into. When createViewModel is called, the template has
// already been injected into this element, but isn't yet bound.
// - 'componentInfo.templateNodes' is an array containing any DOM
// nodes that have been supplied to the component. See below.
// Return the desired view model instance, e.g.:
return new MyViewModel(params);
}
},
template: ...
});
So, componentInfo.element is parent node, and if I apply dialog with $(componentInfo.element) I will set as dialog parent node, then my window tag would be:
<div><!-- Dialog will be applyed there-->
<window params="isVisible: isVisible">
//there will be some html
</window>
</div>
I think it will work, but that extra div look unneeded here... Or this is the only way to do job done?
What is knockout.components way to do this? Thanks.
I had a similar requirement when I wanted to build a Knockout component for hosting a dialog window using Bootstrap v2 'modal' functionality. I used an observable boolean value in my page's viewModel, set to false initially. There isn't a simple way to communicate with components after they have been initialized, except by passing in observables via params.
This observable was passed as a param to a <dialog-window> component in the params, e.g.
<dialog-window params="visible: showDialog">[content]</dialog-window>
This then used a special binding handler for the 'modal' dialog, but in your case you could bind it direcly using a data-bind='visible: YourParam' attribute.
The main page could then simply call showDialog(true) to make the dialog visible.
Hope this helps.

Refer to Backbone view's child element

Having just rendered a view like so:
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
},
How can I refer to one of the template's child elements and apply a jQuery function to it?
Backbone Views expose a dollar $ function that will use jQuery under the covers, but within the context of the view itself.
this.$('.child_element_of_my_view_template')
This will work even if the view is detached ($el not in DOM) but will obiouvsly only work by the time the element you want to select exists within the view (appended to $el).
This means that you can safely use it after the first line of your render function.
this.$el is now a regular jQuery element so you can call .children on it:
this.$el.children()
Use whatever jQuery selector you need, or .eq(index) after it

is it possible to avoid adding default view element "div" when using appendHtml() in Marionette?

I am appending manually the view to a dom element in my template with the following code:
appendHtml: function(collectionView, itemView, index){
collectionView.$("ul#search_list_container").append(itemView.el);
}
In my template I have:
<script id='search-list-grid-template' type='text/x-handlebars-template'>
<ul id="search_list_container"></ul>
</script>
Despite I am appending the view to the ul#search_list_container I have the default div wrapping the template:
<div>
<ul id="search_list_container">
<a href="#">
<span id="search_list_item">
id
invoice_number
</span>
</a>
</li>
</ul>
</div>
is there a way to avoid displaying the default tag "div"?, I have no problem with this but this doubt has always come to my mind whenever I come up with this example.
Note: I have an itemView for the ul compositeView, and some other stuff not shown here.
Backbone Views are designed to have their own DOM element stored as the view's el property.
It is not recommended to remove the view's element as suggested by Steven-Farley, because events might be bound to it.
The best way would be to change the tagName property of your collectionView to ul.
See also: Extra divs in itemviews and layouts in Backbone.Marionette
With jQuery you could use .unwrap().
Try this:
Change:
collectionView.$("ul#search_list_container").append(itemView.el);
To:
collectionView.$("ul#search_list_container").append(itemView.el).unwrap();
Couple of things about this Collection View does not need a template, it either renders the empty view
Or The Item View depending on whether the collection has anything or not.
that being said if you dont want the "div" in each item. try
adding
var yourItemView= Backbone.Marionette.ItemView.extend({
tagName: "li",
//OTHER STUFF HERE
});
then remove the wrapping <li> from your item template.
You shouldnt need to modify appendHtml att all for this use case.

Categories