The right way to initialize jquery library in backbone.js - javascript

In jquery if i have a library i can initialise it by doing the following.
<script>
$(document.ready).ready(function(){
$("#accordion").accordion(); // Jquery accordion library
}
</script>
Now in Backbone:
Main.js
var MyView = Backbone.View.extend({
el: '#wrapper',
initialize: function(){
this.render();
},
render: function(){
$("#accordion").accordion();
}
});
var myview = new MyView();
Is this the right way of doing it?.

I think the correct way is to change:
$("#accordion").accordion();
to:
this.$el.find('#accordion').accordion();
From the docs:
If jQuery is included on the page, each view has a $ function that
runs queries scoped within the view's element. If you use this scoped
jQuery function, you don't have to use model ids as part of your query
to pull out specific elements in a list, and can rely much more on
HTML class attributes. It's equivalent to running:
view.$el.find(selector)

It should be
$("#accordion", this.$el).accordion();
You should be aware that using this technique means that the dom element may not be rendered yet, so your plugin internals shouldn't ever be referencing the dom - only the JQuery object you passed in (some poorly written plugins do this). If you find you are having trouble with this, use a javascript setTimeout or an underscore _defer to wait until the object is rendered to the page.
Also. If you question was in regards to your concern for more javascript dom separation (aka the knockout or angular approach) then you should take a look at knockback. It merges backbone models with knockout viewmodels and dom two way binding. JQuery plugins can then be declared as bindings so they then get called directly from your dom templates. No need to worry about dom rendering.

Related

How to add custom javascript function to Durandal

I am currently using the Durandal framework to build a simple site (my first with Durandal) and have a question on how to go about adding a simple javascript function. I want to call the function after the DOM loads, but am not sure how to attach it to the current viewmodel. The problem I am having is the function is being called before the DOM loads and the div ID hasn't been created yet, which in this case is "sb-search".
I then tried adding the function to the viewmodel:
define(["plugins/router"], function (router) {
var vm = {
viewAttached: viewAttached
};
return {
router: router
};
function attached() {
new UISearch(document.getElementById('sb-search'));
}
return vm;
});
but, to no avail.
Any help would be greatly appreciated. Even a simple tutorial on how to "document.write('Hello World')" would be helpful. Thanks!
This is not strictly related, but I'd like to add something to what was said in the comments: you most likely shouldn't scan the global document in the attached handler. When it's called, composed views may not be, well, composed/attached themselves yet, and in general it's a good idea not to make assumptions about the global state. Also, you can gain performance by not scanning the whole DOM.
When calling attached, Durandal passes the root DOM element of the view bound to the view model as the first argument to the function. Use it to restrict search. If it's in a child/composed view, use the compositionComplete handler, called after all composition in complete (the event "bubbles up"). If it's in a parent view, use the second argument passed to these functions. If it really sounds too complicated, consider that your design might be flawed itself, look for MVVM guidance.
For completeness:
The comments mention that
You must export the right function (attached != viewAttached),
If you indeed intended to define an attached handler called by Durandal, know that viewAttached is deprecated in favor of attached.
And I'd also add that you return an anonymous object containing a router property before you return your vm (view model for sure), although that might be a left-over from some tests you did and copy-pasted here by mistake.

backbonejs this.el is undefined for template view

https://gist.github.com/stephenvisser/2711454
I use this gist for navigation.
I have a view as follows. This view is for a dom (#blog-list-container) inside a template. template is loaded with the gist navigation.
app.BlogListView = Backbone.View.extend({
el: '#blog-list-container',
renderBlog: function(item) {
var blogView = new app.BlogShortView({
model: item
});
this.$el.append(blogView.render().el);
},
render: function() {
this.collection.each(function(item) {
this.renderBlog(item);
}, this);
}, ....
I create this view and navigation as follows:
new Navbar({el:$('#nav-item-container')});
new Content({el:$('#container')});
new app.BlogListView();
So the problem is, this.el is undefined inside BlogListView. In my opinion; it doesn't pick up the dom el: #blog-list-container because it is inside the template and loaded after BlogListView is initialized. How can i overcome this?
This is my first backbonejs experiment, i hope this makes sense.
EDIT:
It works fine if i use jquery selector
$('#blog-list-container').append(blogView.render().el);
But i still want to know what is the proper way to set the el property.
As #mu is too short advised, don't use that Backbone.js too, it isn't the best practice. Yes, you are running into the fact that it tries to append the element before it is fully rendered to the DOM.
I have never once used that first options and have never run into these kinds of problems, so I advise against it.
And I believe you left out a #:
$('#blog-list-container').append(blogView.render().el);
Juts add
return this;
at the end of your "render" function.
Your call this.$el.append(blogView.render().el); expects to have element "el" returned and you are not returning anything, (notice .el() part at the end of your call), so jQuery throws "undefined" error because there is nothing to return.

backbone view this.$ is undefined

I am following a backbone.js tutorial and a part of the code isn't working, maybe because Backbone has changed in the meantime or because I'm doing something wrong. This is the render function of my view.
// grab and populate our main template
render: function () {
// once again this is using ICanHaz.js, but you can use whatever
this.el = ich.app(this.model.toJSON());
// store a reference to our movie list
this.movieList = this.$('#movieList');
return this;
},
The element gets appended into the document later in the code. Subsequently, when the code tries to adds elements to this.movieList, Javascript says it's undefined.
I have tried changing this.el = ... to
this.setElement(ich.app(this.model.toJSON()));
and that helps because this.$el is now defined, but if i try this.$el.find(...) it never finds anything, even though through inspection in Chrome it does appear to contain the HTML elements.
I never used ICanHaz but it it works like the other template languages probably returns HTML code. In that case i'd do something like:
render: function(){
this.$el.html(ich.app(this.model.toJSON()));
}
addMovie: function (movie) {
var view = new MovieView({model: movie});
this.$el.find("#movieList").append(view.render().el);
},
Hope this helps
PS: This is the first time I see this.$('something') in a backbone code o_O. Is he storing the reference of JQuery on the view?

Backbone: View Deletion / Removal

I've a question regarding View Deletion / Removal. I'm aware that you can call the remove method on a view object which will remove the DOM element, and any event listeners that have been bound via listenTo. My question is if you need to do more than that. I normally bind some extra variables within these views and I'm wanting to know if I need to nullify those as well.
An example view:
var myView = Backbone.View.extend({
el: '#exampleContainer',
events: {
'click': 'onClick'
},
initialize: function() {
this.exampleString = 'Hello World';
this.$exampleSelector = this.$('#exampleChild');
},
onClick: function(event) {
console.log('Hello World');
}
});
Also, would I be right in assuming that it's not enough to call remove, but I'd also need to nullify the variable pointing at the view?
myView.remove();
myView = null;
AFAIK you should set the variable to null, since JavaScript's garbage collector will only throw away objects that are no longer referenced (or objects that have no route to the root object to be exact). Calling .remove() on the object will not destroy the reference, so it will probably stay in memory.
This post on HTML5Rocks explains what the "Object Graph" is and how JavaScript's garbage collection works. (I think the GC workflow differs from engine to engine, but that's basically how it works)

Backbone view prototype event binding

I'm creating a Backbone.js plugin that offers a basic grid layout given supplied JSON data. My problem is that I'm not sure how to deal with binding events to a View class without altering the plugin itself. And I'd rather not do this -- I'd rather have the user of the plugin be able to extend the view, or alter its prototype to bind custom events.
The View in the plugin is a basic view without any events binded. It also contains some other functions which I've omitted for simplicity.
FlipCard.CardView = Backbone.View.extend({
tagName: 'div',
className: 'card',
// and more
});
I've attempted to use the prototype attribute in my separate app.js file to bind events, but they don't seem to be triggered.
FlipCard.CardView.prototype.events = {
'click .card' : 'alert'
};
FlipCard.CardView.prototype.alert = function(){
alert("hello!");
};
And I'm familiar with the .extend({}) function, but that won't work unless I can somehow inform the plugin to use the extended version of the view... which I'd rather not do.
Any ideas on what I should be doing here?
EDIT: Turns out it was a silly error. Because the view has the class '.card' and I was trying to bind a click event to '.card', it's unnecessary to put in 'click .card'. Instead the event should be:
FlipCard.CardView.prototype.events = {
'click' : 'alert'
};
If someone were to use their plugin, they would extend your FlipCard.CardView in the same way you are extending Backbone.Model
myApp.Views.myCardView = FlipCard.CardView.extend({
events: {
'click .card' : 'alert'
},
alert: function() {
alert("hello!");
}
}
This creates an extended version of your plugin view with events bound to it, and it does not alter the plugin in any way. The user would then instantiate it as normal:
var someView = new myApp.Views.myCardView();

Categories

Resources