The project I'm working on currently has a single bootstrap file that initialises all the javascript objects within the app. similar to the code below
if(document.getElementById('nav')) {
new navigation();
}
if(document.getElementById('search')) {
new search();
}
new carousel();
However my concern is that for whatever reason one of the lines of JS errors all JS code following will not execute and we're almost creating a single point of failure.
I am interested in hearing alternatives and solutions to this problem and any other bootstrap strategies that may help to alleviate this.
Thanks in advance
Check this fiddle.
For rolling your own, if you can use data- html 5 attributes to specify the bootstrap relationships. Then your bootstrap function can iterate through them and spin up the constructors.
You could also have a mapping object for id to constructors. I'll use that example here.
Assume markup similar to this:
<body>
<div id="navigation"></div>
<div id="search"></div>
</body>
Define your bootstrap maps, create a bootstrap method, and invoke it:
// Declare your bindings in an object
var bootstrapBindings = {
"navigation" : NavigationBootstrap,
"search" : SearchBootstrap,
"failure" : null
};
function bootstrap() {
var i, element, instance;
// Bind everything in the bindings
for(i in bootstrapBindings) {
if(bootstrapBindings.hasOwnProperty(i)) {
try {
element = document.getElementById(i);
if(element) {
instance = new bootstrapBindings[i](element);
// Nestable bootstrap calls
instance.bootstrap();
}
} catch(e) {
// Do something with error if you want
alert('Unable to bootstrap: ' + e);
}
}
}
}
bootstrap();
If you know that there is a chance one of your calls may fail then you should have a solution to catch that error as well. Using a try catch block would allow you to catch the exceptions and have your code continue.
try{
if(document.getElementById('nav')) {
new navigation();
}
}catch(e){
//handle exception for navigation fail
}
try{
if(document.getElementById('search')) {
new search();
}
}catch(e){
//handle exception for search fail
}
try{
new carousel();
}catch(e){
//handle exception for carousel fail
}
Generally you dont want to wrap all your code in try catch blocks, but it sounds like you're fairly confident that some of the code will throw an exception.
You could encapsulate the init logic inside separate init functions, and then have a further function invoke them and capture the results. For example:
<body>
<div id=nav></div>
<div id=search></div>
<script>
function initNavigation() {
if (document.getElementById('nav')) {
new navigation();
}
}
function initSearch() {
if (document.getElementById('search')) {
new search();
}
}
function initCarousel() {
new carousel();
}
function initNoError() {
return "ok";
}
/**
* Expects a list of functions to execute, and will capture either the retVal or the error.
*/
function init() {
var results = [],
result;
for (var i = 0; i < arguments.length; i++) {
result = {};
try {
result.retVal = arguments[i]();
} catch (e) {
result.error = e;
}
results.push(result);
}
return results;
}
var initResults = init(initNavigation, initSearch, initCarousel, initNoError);
console.log(initResults);
</script>
</body>
This code will show the following for me. The ReferenceError errors are because I don't have your constructor functions. The TypeError is seemingly because Firefox is returning the <div> of the same id, and this div is of course not a constructor function.
[
Object { error=ReferenceError: navigation is not defined},
Object { error=TypeError: search is not a constructor},
Object { error=ReferenceError: carousel is not defined},
Object { retVal="ok"}
]
Related
I am confused on how to work with module pattern (and design patterns in general) in JavaScript.
I already wrote some functioning code in my application using module pattern that does what I want to, but it doesn't seem to be very modular to me, and I keep having this feeling that I am doing it wrong. I didn't manage to find any concrete and complete application example with any design pattern.
Here is how I work with it :
Let's say I have forms in my application that I'll use for different modules (post a thread, reply to a thread, comment the guests book), with some JavaScript I'll give users some functionalities, as such as popping a smiley bubble and handling insertion of them in my forms, sending data posts to my server code to return the HTML code in order to add the message without reloading the page, I'll do something like that:
let Form = function (selector_form, selector_textarea, selector_emoticonsButton, selector_postButton) {
let form, textarea, emoticonsButton, postButton;
let emoticonsBubble = new EmoticonsBubble()
return {
selectors: function () {
return {
form: function () { return selector_form },
sendButton: function () { return selector_sendButton }
}
}
setElements: function (obj) {
form = $(obj).get(0);
textarea = $(form).find(selector_textarea).get(0);
emoticonsButton = $(form).find(emoticonsButton).get(0);
postButton = $(form).find(selector_postButton).get(0);
emoticonsBubble.setElements(form, emoticonsButton);
},
get: function () {
return {
form: function () { return form },
//...
emoticonsBubble: function () { return emoticonsBubble }
}
},
post: function (moduleId, callback) {
$.ajax({
//parameters
}).done(function (data) {
callback(data);
});
}
}
}
let EmoticonsBubble = function () {
let thisContainerToAppendTo, thisTextarea;
return {
setElements: function (container, textarea) {
thisContainerToAppendTo = container;
thisTextarea = textarea;
},
pop: function () {
this.ajax().pop(function (data) {
$(thisContainerToAppendTo).append(data);
});
}
insert: function (emoticon) {
$(thisTextarea).append(emoticon);
},
ajax: function () {
return {
pop: function (callback) {
$.ajax({
//parameters
}).done(function (data) {
callback(data);
});
}
}
}
}
}
// Events part
let form = new Form('#threadForm', '.textarea', 'button[name="emoticons"]', 'button[name="send"]');
let emoticonsBubble = form.get().emoticonsBubble();
$(form.selectors().form()).on('click', function (e) {
form.setElements(this);
});
$(form.selectors().sendButton()).on('click', function (e) {
let moduleId = // retrieve module id, if it belongs to guests book, thread creation module or reply module
form.post(moduleId, function (data) {
// append data to something
});
});
// etc for emoticons handling
The fact that I have to rewrite the event part for every different form I have in my application while keeping everything the same but variables name, annoys me a lot.
Could you guys tell me how you would handle those functionalities and what may be wrong with my way of coding?
The Module Pattern is about keeping units of code from colliding with other scopes (usually the Global scope).
As we know, in JavaScript, variables defined with:
let and const are scoped to their parent block
var are scoped to their containing function (or Global if not in a
function)
So, if you were to take your Form function:
let Form = function (x,y,z) {
let form, textarea, emoticonsButton, postButton;
let emoticonsBubble = new EmoticonsBubble()
return {
. . .
}
setElements: function (obj) {
. . .
},
get: function () {
. . .
},
post: function (moduleId, callback) {
. . .
}
}
}
The variable Form is Global because there is no containing block. This is a problem because what if there is already another Global called Form (which there very well could be because of the generic nature of the word "Form"). So, this code doesn't cut off your code from being exposed. To use the Module Pattern on it, we'd wrap it with an IIFE (Immediately Invoked Function Expression) and within that IIFE, we'd create a custom namespace in the Global scope that we're sure doesn't exist (thereby avoiding name collisions):
(function(){
// This is going to be exposed as publicly available via the module namespace
function Form(x,y,z) {
. . .
}
// This will remain private within the module
function helper(){
}
// **********************************************************************
let temp = {}; // Create a temporary object to bind only the public API
temp.Form = Form; // Bind the public members to the object
// Expose the module to the Global scope by creating a custom namespace
// and mapping the temp object to it
window.myCustomAPI = temp;
})();
// Now, outside of the module (in some higher scope), your public portions
// of the Module are accessible:
let myForm = new myCustomAPI.Form(arg, arg, arg);
The repetition in your code basically comes from the selection of elements and their helpers, and that can easily be abstracted into a function:
function Elements(selectors, children, options) {
let elements = { ...children };
return {
selectors,
elements,
setElements(obj) {
for(const [name, selector] of Object.entries(selectors))
elements[name] = $(obj).find(selector).get(0);
for(const child of Object.values(child))
child.parent && child.parent(this, obj);
},
...options
}
}
That can then be used as:
function Form(form, textarea, emoticonsButton, postButton) {
const emoticonsBubble = EmoticonsBubble();
return Elements({ form, textarea, emoticonButtons }, { emoticonsBubble }, {
post() {
//...
}
});
}
function EmoticonsBubble() {
return Elements({ /*...*/ }, {}, {
parent(parent, obj) {
this.setElements(parent);
}
});
}
But you are basically reinventing a lot of wheels here, have you thought about using one of the MVCs that are out there (React, Vue, ...) ?
Ok the boilerplate for some common tasks that you have in the event part is driving you crazy right ?
So checking your code you can fix them in many ways.
A. Encapsulate your code in real modules I mean this.
const Form = (function(/*receive here dependencies as arguments */){
// your code module goes here
})(/*inject dependencies here to module*/);
B. You can create a event pattern module, to drive your internal and externals events for module.
C. You know what are the listener that the module needs , so apply them into your module.
That way should be more reusable than now
I am trying to make my code shorter and more optimized, and want to make it look clearer.
So far I did this :
function id(a) {
return document.getElementById(a);
}
function cl(a) {
return document.getElementsByClassName(a);
}
function tg(a) {
return document.getElementsByTagName(a);
}
function qs(a) {
return document.querySelector(a);
}
function qa(a) {
return document.querySelectorAll(a);
}
Now I have the possibility to call qs("#myElement"). Now I want to attach a event to the specified element just like qs("#myElement").addEventListener("click", callBack). It works great for me. But when I try to make this :
function ev(e, call) {
return addEventListener(e, callback);
}
And then try to call qs("#init-scrap").ev("click", someFunction) then it pops up the following error :
Uncaught (in promise) TypeError: qs(...).ev is not a function.. I don't know what is the problem, do I have to try method chaining ? or any other way I can resolve this problem.
Note : I don't want to use any libraries or frameworks liek Jquery etc.
If you wish to use syntax qs("#init-scrap").ev("click", someFunction), you need to wrap object returned by querySelector into another object that has ev function.
class jQueryLite {
constructor(el) {
this.el = el;
}
ev(e, callback) {
this.el.addEventListener(e, callback);
return this;
}
}
qs(a) {
return new jQueryLite(document.querySelector(a));
}
It's called Fluent interface, if you wish to look it up.
Just pass the element/nodelist in as the first argument and attached the listener to it.
function ev(el, e, call) {
return el.addEventListener(e, callback);
}
As an alternative, but not something I would recommend, you could add ev as a new Node prototype function:
function qs(selector) {
return document.querySelector(selector);
}
if (!Node.prototype.ev) {
Node.prototype.ev = function(e, cb) {
return this.addEventListener(e, cb);
};
}
qs('button').ev('click', handleClick);
let count = 0;
function handleClick() {
console.log(count++);
}
<button>Count+=1</button>
Note I've only tested this with document.querySelector. You might have to alter the code to work with document.querySelectorAll etc as they don't return single elements.
There is an error in your ev method. It should be
const ev = document.addEventListener.bind(document);
So instead of creating new functions that wrap the original, you can alias the actual function itself.
You should do the same for your other aliases if you want to go with this approach.
const qs = document.querySelector.bind(document);
const qa = document.querySelectorAll.bind(document);
My final word of advise would be to not alias these methods at all. The abbreviated method names hurt the readability of your code. Readability almost always trumps brevity as it comes to code.
I looked into the previous answers as an inspiration and created my take on it.
Core
const $ = (selector, base = document) => {
return base.querySelector(selector);
};
Node.prototype.on = function(type, listener) {
return this.addEventListener(type, listener);
};
It supports a base value in case you have another element than document but it's optional.
I like $ and on so that's what I use, just like jQuery.
Call it like below
$('button').on('click', (e) => {
console.log(e.currentTarget);
});
I went though all the posts here related to this topic, but couldn't find a working solution. May be something very different in my code.
File 1, RequestFactory.js
function requestFactory() {
this.createRequest = function (reportId) {
var request;
request = new xyzRequestManager.XyzRequest();
return request;
}
return {
RequestFactory: requestFactory
}
}
File 2,request.js
function loadData() {
var request = requestFactory.createRequest(id);
request.loadReport(report);
}
File 3, xyzRequestManager.js
function () {
var xyzRequest = function() {
this.loadReport = function(report) { --some data--}
}
return {
XyzRequest: xyzRequest
}
}
So the call starts from file2, i create the request object by calling requestFactory. There are bunch of other functions written in file 3, which gets called from file 1 in similar fashion, request factory object, and make call to the function.
This gives error as,
Uncaught TypeError: xyzRequestManager.XyzRequest is not a constructor
I have wasted hours on this, and still no clue what or where am I wrong.
Any help would be appreciated.
You're returning an object with a property called XyzRequest, not xyzRequest, see the *** comment:
// Note: This is verbatim from the question other than this comment and
// the *** comment below.. It's not valid syntax on its own (the function
// would need a name), but I assume it's an excerpt from something larger.
function () {
var xyzRequest = function() {
this.loadReport = function(report) { --some data--}
}
return {
XyzRequest: xyzRequest // ***
}
}
So to use it, you need that capital X:
request = new xyzRequestManager.XyzRequest();
// -----------------------------^
I've got a slightly unusual pattern I'm trying to achieve and have not quite figured it out. My goal is to create a function called debugLog as a flexible console.log replacement, which can be called as follows:
debugLog('thing to log #1', 'thing to log #2', objectToLog1, objectToLog2);
^^ the number of params should be arbitrary, just as is possible with console.log
That is what I'll call the "default" functionality. Now I'd also like to add some additional functionality through property functions.
Examples:
debugLog.setDebugFlag(true); // sets the internal this.debugFlag property to true or false depending on param
I'm trying to do this in Node and what I have so far does not quite let me achieve this pattern:
var debugLog = function () {
this.debugFlag = this.debugFlag || true;
if (this.debugFlag) {
console.log.apply(null, arguments);
} else {
// production mode, nothing to log
}
};
debugLog.prototype.setDebugFlag = function (flagBool) {
this.debugFlag = flagBool;
}
module.exports = new debugLog();
This module would be including in a Node app using the standard require pattern:
var debugLog = require('./debugLog.js');
The question is how can I achieve this pattern of a function object with default functionality, but also extended "property" style functions? Please note I am already aware of the typical pattern where ALL functionality comes from function properties (such as debugLog.log() and debugLog.setDebugFlag()). But that patterns does NOT achieve my key goal of a shorthand for the default functionality that simply involves calling the function directly.
Is it even possible to do?
You could do it this way
var debugLog = (function() {
var debugFlag = true;
function log() {
if (debugFlag) {
console.log.apply(null, arguments);
} else {
// production mode, nothing to log
}
};
log.setDebugFlag = function(flag) {
debugFlag = flag;
}
return log;
})();
module.exports = debugLog;
You could use closure, like so:
// debugLog.js
var debugFlag = true;
function debugLog() {
if (debugFlag) {
console.log.apply(null, arguments);
} else {
// production mode, nothing to log
}
}
debugLog.setDebugFlag = function (newFlag) {
debugFlag = newFlag;
}
module.exports = debugLog;
and use it like this:
// otherFile.js
var debugLog = require('./debugLog');
debugLog('Hey!');
debugLog.setDebugFlag(false);
debugLog('This wont appear!');
I have one page with two types of forms. I have a single form of type A at the top, and then I have 1 or more forms of type B below it.
I use the Module pattern + jQuery to wire up all the events on my forms, handle validation, ajax calls, etc.
Is this the preferred/valid way to define a singleton, as in Form A, and a reusable object class, as in Form B? They are very similar and I'm not sure if I need to be using object the prototype property, new, or a different pattern. Everything seems to work for me but I'm afraid I'm missing some key error.
Form A javascript looks like this:
var MyProject.FormA = (function() {
var $_userEntry;
var $_domElementId;
var validate = function() {
if($_userEntry == 0) {
alert('Cannot enter 0!');
}
}
var processUserInput = function() {
$_userEntry = jQuery('inputfield', $_domElementId).val();
validate();
}
return {
initialize: function(domElementId) {
$_domElementId = domElementId;
jQuery($_domElementId).click(function() {
processUserInput();
}
}
}
})();
jQuery(document).ready(function() {
MyProject.FormA.initialize('#form-a');
});
Form B, which is initialized one or many times, is defined like so:
var MyProject.FormB = function() {
var $_userEntry;
var $_domElement;
var validate = function() {
if($_userEntry == 0) {
alert('Cannot enter 0!');
}
}
var processUserInput = function() {
$_userEntry = jQuery('inputfield', $_domElement).val();
validate();
}
return {
initialize: function(domElement) {
$_domElement = domElement;
jQuery($_domElement).click(function() {
processUserInput();
}
}
}
};
jQuery(document).ready(function() {
jQuery(".form-b").each(function() {
MyProject.FormB().initialize(this);
});
});
Both of your modules explicitly return objects which precludes the use of new.
Prototype inheritance isn't really compatible with the method hiding your achieving with this pattern. Sure you could re-write this with a prototype form object with your validate method defined on it, but then this method would be visible and you'd loose the encapsulation.
It's up to you whether you want the low memory footprint and speedy object initialization of prototypes (Shared methods exist only once, Instantiation runs in constant time) or the encapsulation of the module pattern which comes with a slight performance penalty (Multiple defined identical methods, Object instantiation slowed as every method has to be created every time)
In this case I would suggest that the performance difference is insignificant, so pick whatever you like. Personally I would say there is too much duplication between them and I would be inclined to unify them. Do you really need A to be a singleton? What are the dangers of it being accidentally instantiated twice? Seems like this is maybe over-engineering for this problem. If you really must have a singleton I'd wrap the non-singleton (B) class like this:
var getSingleton = function() {
var form = MyProject.FormB();
form.initialize("#form-a");
console.log("This is the original function");
getSingleton = function() {
console.log("this is the replacement function");
return form;
}
return form;
}
I think you just need to write a kind of jQ plugin:
(function($) {
$.fn.formValidator = function() {
return $(this).each(function() {
var $_domElement = $(this);
$_domElement.click(function() {
if($('inputfield', $_domElement).val() == 0) {
alert('Cannot enter 0!');
}
});
});
};
})(jQuery);
In this case you'll extend jQ element methods module and will be able to use it for any amount of elements at the page (for single or multiple elements collection). Also it will be chainable.
Usage:
$('#form-a').formValidator();
$('.form-b').formValidator();
Or
$('#form-a, .form-b').formValidator();
Ofcourse you can use a module to store this function:
ProjectModule.formValidator = function(selector) {
return $(selector).each(function() {
var $_domElement = $(this);
$_domElement.click(function() {
if ($('inputfield', $_domElement).val() == 0) {
alert('Cannot enter 0!');
}
});
});
};
Usage:
ProjectModule.formValidator('#form-a, .form-b');