Where to put javascript events in AMD modules? - javascript

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.

Related

Angular 2+ Window attribute undefined

I'm using an external library that attaches itself to the global window object (window['lib']) once the library's javascript file is loaded by the browser. I'm trying to invoke code using this library whenever a component is loaded, however everytime I try to access the object it is undefined (due to the library not having been loaded). I have tried every lifecycle hook I can think of, however nothing appears to wait for the DOM to be fully ready. For example, I want to do something like this:
ngOnInit() {
window['lib'].doStuff(); // <-- window['lib'] is undefined
}
If I wrap it in a timeout, then it becomes available. However, this looks like code smell and do not want to approach it this way:
ngOnInit() {
setTimeout(function() {
window['lib'].doStuff(); // <-- this works
});
}
What is the best / suggested / "most angular way" to approach this problem? Thanks!
Angular Lifecycle Hook: ngOnInit()
Initialize the directive/component after Angular first displays the
data-bound properties and sets the directive/component's input
properties.
Called once, after the first ngOnChanges().
This is a common problem with Angular. Older methodologies like this one that uses a global variable on the window object will piss off the way Angular loads the application and also TypeScript (during development). The reason why you have to do window['lib'] and not window.lib is because TypeScript doesn't know anything about the types of window.lib so window['lib'] is a way to force it to work.
The other part is that depending on what type of compilation you're using (AOT vs JIT), that library you're loading may or may not be ready yet (also depends on how you're loading that script/module into the application). As Commercial Suicide mentioned, you could try some of the other Angular Lifecycle Hooks but more than likely, you're going to end up settling on setTimeout. You actually don't even need to define the timeout period or you can pass 0ms (as you've done in your code). Angular just wants you to hold off on calling that function until the DOM it's finished rendering.
I personally can't wait until all the jQuery-like libraries are converted into proper ES6 modules. The days of just throwing a script tag at the bottom of the body are long gone.
setTimeout
How to get a reference to the window object in
Angular
Relevant Thread

toggle issue for timeline [duplicate]

I'm looking through the excellent peepcode demo code from the backbone.js screencasts. In it, the backbone code is all enclosed in an anonymous function that is passed the jQuery object:
(function($) {
// Backbone code in here
})(jQuery);
In my own backbone code, I've just wrapped all my code in the jQuery DOM 'ready' event:
$(function(){
// Backbone code in here
});
What's the point/advantage of the first approach? Doing it this way creates an anonymous function that is then executed immediately with the jQuery object being passed as the function argument, effectively ensuring that $ is the jQuery object. Is this the only point - to guarantee that jQuery is bound to '$' or are there other reasons to do this?
The two blocks of code you have shown are dramatically different in when and why they execute. They are not exclusive of each other. They do not serve the same purpose.
JavaScript Modules
(function($) {
// Backbone code in here
})(jQuery);
This is a "JavaScript Module" pattern, implemented with an immediately invoking function.
http://addyosmani.com/resources/essentialjsdesignpatterns/book/#modulepatternjavascript
http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth
The purpose of this code is to provide "modularity", privacy and encapsulation for your code.
The implementation of this is a function that is immediately invoked by the calling (jQuery) parenthesis. The purpose of passing jQuery in to the parenthesis is to provide local scoping to the global variable. This helps reduce the amount of overhead of looking up the $ variable, and allows better compression / optimization for minifiers in some cases.
Immediately invoking functions are executed, well, immediately. As soon as the function definition is complete, the function is executed.
jQuery's "DOMReady" function
This is an alias to jQuery's "DOMReady" function: http://api.jquery.com/ready/
$(function(){
// Backbone code in here
});
jQuery's "DOMReady" function executes when the DOM is ready to be manipulated by your JavaScript code.
Modules vs DOMReady In Backbone Code
It's bad form to define your Backbone code inside of jQuery's DOMReady function, and potentially damaging to your application performance. This function does not get called until the DOM has loaded and is ready to be manipulated. That means you're waiting until the browser has parsed the DOM at least once before you are defining your objects.
It's a better idea to define your Backbone objects outside of a DOMReady function. I, among many others, prefer to do this inside of a JavaScript Module pattern so that I can provide encapsulation and privacy for my code. I tend to use the "Revealing Module" pattern (see the first link above) to provide access to the bits that I need outside of my module.
By defining your objects outside of the DOMReady function, and providing some way to reference them, you are allowing the browser to get a head start on processing your JavaScript, potentially speeding up the user experience. It also makes the code more flexible as you can move things around without having to worry about creating more DOMREady functions when you do move things.
You're likely going to use a DOMReady function, still, even if you define your Backbone objects somewhere else. The reason is that many Backbone apps need to manipulate the DOM in some manner. To do this, you need to wait until the DOM is ready, therefore you need to use the DOMReady function to start your application after it has been defined.
You can find plenty of examples of this around the web, but here's a very basic implementation, using both a Module and the DOMReady function:
// Define "MyApp" as a revealing module
MyApp = (function(Backbone, $){
var View = Backbone.View.extend({
// do stuff here
});
return {
init: function(){
var view = new View();
$("#some-div").html(view.render().el);
}
};
})(Backbone, jQuery);
// Run "MyApp" in DOMReady
$(function(){
MyApp.init();
});
As a minor sidenote, sending in $ as an argument to an anonymous function makes $ local to that function which has a small positive performance implication if the $ function is called a lot. This is because javascript searches the local scope for variables first and then traverses down all the way to the window scope (where $ usually lives).
It ensures you can always use $ inside that closure even if $.noConflict() was used.
Without this closure you'd be supposed to use jQuery instead of $ the whole time.
It is to avoid a potential conflict of the $ variable.
If something else defines a variable named $, your plugin may use the wrong definition
Refer to http://docs.jquery.com/Plugins/Authoring#Getting_Started for more details
Use both.
The self invoking function in which you pass in jQuery to prevent library conflicts, and to just make sure jQuery is available as you would expect with $.
And the .ready() shortcut method as required to run javascript only after DOM has loaded:
(function($) {
$(function(){
//add code here that needs to wait for page to be loaded
});
//and rest of code here
})(jQuery);

Backbone view click event not firing

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

onrender vs init in Ractive.js

I recently started trying out Ractive.js. I was particularly interested in its components. One of the things I noticed immediately is that many of the examples are using the init option. However when I try to use the init in my code I get a deprecation notice and it then recommends using onrender instead. onrender however had far fewer examples than init and some of the functions such as this.find weren't available inside onrender. I looked through the Github issues but couldn't find any reasoning behind this change or what the suggested path forward for selecting elements specific to a component was.
I created a test pen to try creating a recursive component with the new API but I had to resort to using jQuery and an undocumented fragment api to select specific DOM nodes I needed to manipulate. I feel this is against how Ractive expects you to do things, but I couldn't figure out what was expected of me with the existing documentation.
What's the major differences between the init and onrender options and how does onrender expect you to handle manipulating specific elements and their events within a component?
You can use this.find() inside onrender (if you can't for some reason, you've found a bug!).
We split init into oninit and onrender a while back for a number of reasons. Those examples you mention are out of date - are they somewhere on ractivejs.org? If so we should fix them. You can find more information about lifecycle events here on the docs, but the basic difference is this:
init was called on initial render (assuming the component was rendered, i.e. never in node.js, if you were doing server-side rendering)
oninit is called on creation, immediately before rendering. It is called once and only once for any Ractive instance, whether or not it gets rendered. It's therefore a good place to set up event handlers etc. The opposite of oninit is onteardown, so you can use that handler to do any cleanup if necessary (or use this.on('teardown'...) inside oninit).
onrender is called whenever a component is rendered. This could happen more than once (if you unrendered, then re-rendered, etc) or not at all. If you need to store DOM references etc, this is the place. The opposite of onrender is onunrender.
I did a fork of your codepen, replacing the jQuery stuff with more idiomatic Ractive code to show how you'd do it without storing DOM references.

dojo style developement

I'm having difficulty wrapping my head around dojo style of coding. The reason I am drawn to it is because of its class style coding. I have done AS developement and some java so it makes sense to me to be drawn to it. I have done some jquery style DOM work but I require a more framework based setup for a project I'm starting.
My question is should I be creating everything as classes with the declare and then requiring them when needed. Or could I write closure type functions with namespaces just like regular javascript modules. I'm confused.
Example I want to have a group of methods that take care of managing data. Then I want to have another collection of methods that handle special ajax calls. Would I create a class with declare for each of these groups of methods, in separate js files. Then in my app.js which is my application class where I'm handling the initialization of all my classes, would I require both those classes before dojo.ready(){}
then once the ready method is called I can start to use those classes.
Can someone set me straight here before I dojo out.
Does require make a load request for that js file and if so do you always have to use the ready method. If so is it best to require a bunch of your classes up front at the start of your application initialization.
Technically for what you're wanting to do, you could go either way - using dojo.declare or simply creating an object with function members. I'd be inclined to do the latter, since dojo.declare's elaborate inheritance considerations will be total overkill that you won't be making use of in this case, and it doesn't generally make sense to be instantiating anything when you just want to group some utility methods together.
For modules that simply group utility methods together, I'd be inclined to do something along these lines:
dojo.provide('my.utils');
my.utils = {
doSomething: function(){
/* do something... */
},
doSomethingElse: function(){
/* do something else... */
}
};
RE loading, if I'm reading you right, then yes, you have the right idea. In your web page, you would dojo.require(...) the modules your page requires (perhaps just one, if you have all your other dependencies further required within it). Then, any code in the page that expects these modules to be loaded should be within a function passed to dojo.ready. This ensures that even in cases where modules are asynchronously loaded (i.e. using the cross-domain loader), your code will still work. dojo.ready specifically waits for (1) the DOM to be ready and (2) all dojo.required modules up to that point to be loaded.
Note that within modules themselves, you do NOT need to enclose code in dojo.ready for the sake of waiting for dojo.required modules to load; this is figured out by the loader automatically. (However, if some logic in your module needs to wait for the DOM to be ready, you would still rely upon dojo.ready.)
I've written more about dojo.ready in the past; maybe it'll be a helpful read: http://kennethfranqueiro.com/2010/08/dojo-required-reading/

Categories

Resources