Here is the code:
var view = Backbone.View.extend({
el:'#comment',
template:'',
initialize:function(){
this._getTemplate();
this.render();
},
event:{
'click #submit_btn' : 'submit'
},
_getTemplate:function(){
$.ajax({
...
});
},
render:function(){
this.$el.html(this.template);
},
submit:function(event){
alert('click');
}
});
I use ajax to get html template from server and it works well. I have no problem in loading template.
Here is the div which I wanna insert the template into.
<div id="comment">...</div>
Here is the template. I just show the simple structure.
<div>...</div>
<button id="submit_btn">submit</button>
<div>...</div>
Can someone help me to solve it?
The event not firing because when the event is attempted to bind to the DOM element (i.e. at the time of the View construction), the DOM element does not exist. From the Backbone docs:
Backbone will automatically attach the event listeners at instantiation time, right before invoking initialize.
Since the #submit_btn DOM element matcher returns nothing, no event is bound.
Your technique of fetching the template within the initialize() method will be problematic. In most OO-based development patterns, the Constructor of the object should not do a lot of work, and the returned instance should be fully initialized when the Constructor completes. Backbone Views are no different in this respect. Your Constructor as written here attempts to do quite a bit of work, but there is no benefit to fetching the template as a part of the object creation here. In fact there is some harm too: what if you end up creating more than one instance of your View? You would end up fetching the template more than once, even though the contents would be the exact same.
Forcing an XHR of any kind to be synchronous is also going to be problematic. During a synchronous request the browser's main thread is busy waiting for the response. In this example if the template ever takes longer than a few hundred milliseconds to arrive, your application will appear "frozen" to the user. This provides a very poor user experience. Asynchronous code may feel like it's more complex, but if you are building a JS-based application of even minimal complexity you will be required to deal with it. Since Javascript is (mostly) single-threaded, asynchronous execution becomes an essential tool of any application.
A better solution is to either embed the template into the hosting HTML page content, or use a JS modularization technique (RequireJS, CommonJS, others) to effectively package the template up with the module that needs it.
It looks like the event string is set up correctly and the render function is doing the right thing. The one issue I see is that the 'initialized' function should be 'initialize' as per the Backbone View Extend documentation.
Reference: http://backbonejs.org/#View-extend
Related
I find the usage of the different elements of backbonejs quite ambiguous. From my undestanding, this was the intention as backbone didn't want to be a framework, but more of a set of tools/objects.
I understand most parts of backbonejs, but i'm still questioning what is the correct usage for a view's initialize() and render() calls. In other words, what logic should be placed within each.
Could someone explain what is best practice or what is considered correct usage of these calls within a view?
This is a pretty far-ranging question, but I'll give it a try. The key takeway here is that there is no such thing as "the correct usage" for these calls. Backbone is flexible on purpose and meant to adapt to your needs. That's why it still has its place in a world of more sophisticated, but "opinionated" frameworks (which tell you that it's "my way or the highway").
Backbone does not use any magic with respect to rendering. The render() method is a no-op and not even invoked automatically for you. Its existence is nothing more than a hint how things are usually done.
The initialize() method, however, is invoked automatically when the view is instantiated. Backbone guarantees that at that point, the top-level DOM element of the view (the el) has already been created and is ready to have stuff attached to it.
But again, that's pretty minimal: the top-level element is there, but not yet attached to the DOM (unless you passed options.el to the constructor, setting the el to an existing element). Inserting the el into the DOM is your job, too.
So you are free to decide how to wire things up. What I typically do is this:
Most of my views have a template, which is commonly assigned to the template property of the view. (Again, just a convention, no magic involved. You could name it foo.) That template is compiled in initialize().
In initialize(), I set up the relationship with a model or a collection which the view is supposed to represent. Ie, the view observes model/collection events and is made to call render when the data has changed. (You don't have to use a Backbone entity as your data source, of course ... could be anything).
In render(), the data is fed into the template, and the result becomes the innerHTML of the el. Afterwards, I make sure the el is inserted into the DOM if it isn't already.
The first call to render() should happen when the data is ready. The right moment depends on the app. Perhaps render() is the last call in initialize(), or some event will kick it off later on.
If a lot of that sounds like repetitive boilerplate to you, yes, that's exactly what it is. Frameworks like Marionette exist on top of Backbone to take care of that.
But there is a sigificant performance penalty for that framework magic. So when I have to generate a lot of views, I stick to the generic Backbone stuff (and use one of my own components for speeding up the template handling.)
We have a Jura plugin written by ourselves, and in it's vm template at the very end there's the following code:
AJS.$(window).ready(function(){
doSomeThing();
});
Inside of this method we are loading some server side data and initializing internal js objects. For some strange reason this specific method doSomeThing is being called twice. Moreover, vm template is also being called twice, owerwriting first template initialization state (but template may be already initialized and contain some data at this point). I don't get it why it's made this way and how to work around this. If someone faced similar thing before and knows what to deal with it - please respond. Much appreciated.
We've found a reason of such bahavior - it's Backbone. Jira creates an element using Backbone View, which calls AJS.$().ready second time during initialization. We stopped usage of this element after our investigation
When programming in Meteor I often find myself having to sprinkle typechecks or existence checks a bunch when writing Template helpers (at least under a couple very common conditions).
A helper for one template depends on a collection loaded by a different template
Any time a template helper operates on a piece of the DOM that another template is responsible for rendering into existence
For example (in the first case):
Template.example.rendered
rev1 = getRev(revId1)
revText1 = html2plain(rev1.text)
where getRev is doing an operation on the Revisions collection that may or may not be loaded by the time the example template is first rendered. So rev1.text will sometimes throw an exception because getRev ends up returning null or undefined if called before Revisions is loaded.
I then end up having to check a ton of variables/objects throughout my code for existence before using any of their properties just to be safe.
I could imagine using a router to not render my example template until after a different collection is ready (but for nested templates, and Session variable changes this doesn't work so hot).
I could imaging wrapping the helper code in a if (isCollectionReady) which might help but doesn't seem best practice.
The question is: Is there a canonical or best practice way to, identify these situations, code for them, or avoid this altogether?
Meteor is designed such that its templates are reactive, so that in most cases you shouldn't need to do DOM manipulations on them. As the underlying data changes, the templates automatically rerender so that they're always showing the latest data. Take a look at the examples in the Meteor docs: they don't use any DOM manipulation code. The templates put the data into the right places, and that's all they need.
In my experience there are two common reasons to need to put code into rendered:
You're loading a widget that needs to be initialized after the template is rendered and ready, like a <select> replacement.
You're doing animations. (In this case, I usually tell my template to put everything in its proper place but with a CSS class that hides the elements, and then all rendered does is animate the reveals.)
It's usually fine for a template to render before its subscription has loaded; at worst, the template will just render blank and then rerender as the data streams in. Also remember that you can subscribe from client-side code other than a template helper, for example Meteor.startup on the client side. Finally don't forget about the created helper; if you really want to wait until a template is loaded before subscribing, that would be a better place to subscribe than rendered, as it gets called sooner.
What DOM manipulations are you doing and why? Assuming you're not using widgets or animations, chances are that you can achieve what you want by using templates on their own without any additional manipulation code.
I am in the process of converting a massive .js file into AMD modules using requirejs
I realise the concept about returning function and such which are much link classes, however, how do I handle events such as:
$('blah blah').onClick( ...
$('blah blah 2').onChange( ...
Do I just create a module that does not return anything? What is the best way to handle these sorts of things?
If you want to be executed just once, at application initialization, just put it in the body of your module and don't return anything:
define([...], function() {
// All code here will be executed once at initialization.
});
All the body code will be executed once, as long as you import that module. This is a bad idea for jQuery selectors as there is no guarantee that the DOM will be properly loaded when this is evaluated.
If you need to call your code manually, once or more, encapsulate it in an object and you can import and call it when needed:
define([...], function() {
return {
registerEvents : function () {
// All code here will be executed when `module.registerEvents()` is called.
}
};
});
This would be the proper way to register DOM events as it gives you more control in regards of when this is evaluated.
Since selecting an element an attaching a handler with a jQuery selector happens over the whole document, it can technically be put anywhere.
I would say it largely depends on how you're organizing your modules.
If you're splitting them up into MV* components, interaction handlers would go in the V* portions (e.g. a Backbone View).
If you're using some other organizational scheme, generally I'd say put the handlers where they're the most tightly bound. For example, if a module already has a reference to the DOM element you want to bind to, put the handler in with it (and use that specific reference, rather than calling $() to traverse the document, potentially picking up unwanted elements) so that you can unbind the handler at the end of the lifecycle of the element.
I've inherited a rather large Javascript/ExtJS3 code base, and there are many instances of invoking events inside of the overridden initComponent method, after the call to "...superclass.initComponent.apply(this, arguments)". Specific events are being invoked on specific objects in a manner such as the following:
this.filter.on('filterUpdated', function(filter, params)
I've started converting the code to use a pub/sub paradigm instead, to reduce the coupling between objects and their specific event names, but quickly ran into issues when publishing and/or subscribing to events within initComponent (which in ExtJS executes before rendering). I need to fire an "INIT" event from the highest level component when the screen first loads, and I was either getting an error (due to ExtJS "templates" not having been rendered as it turns out), or events not firing at all.
Then I read the following in the ExtJS source for Ext.Component (from which all components extend) and I had an "aha" moment:
if (this.tpl) {
if (!this.tpl.compile) {
this.tpl = new Ext.XTemplate(this.tpl);
}
if (this.data) {
this.tpl[this.tplWriteMode](contentTarget, this.data);
delete this.data;
}
}
this.afterRender(this.container);
When I switched to both publishing the "INIT" event from my topmost component's afterRender method, and subscribing to all events from all other components from their afterRender methods, everything worked as I expected. And now I am just wondering, largely to validate my design....
Is this a generally accepted way of implementing pub/sub in an event driven UI? Regardless of framework even? Namely are the following 2 good principles, or are their other ways?
"Initialization events" should get published after all sub-components have rendered
All sub-components should subscribe to all events (to be on the safe side) after they have rendered
Thanks in advance
You have to balance the overhead of event handling vs. the possibility of missing significant events. In js/DOM land state is mutable.
For your #1 if you can identify a point in time when all your sub-components have rendered and subscribed, firing an init event makes sense.
For #2, it seems safe everyone to listen for events; however it could slow things down. If performance issues are apparent you may have to decide what events you don't care about and avoid subscribing.