How To Refactor If-else Code Segment? - javascript

I'm developing win8(metro style) application with Html5-js-jquery.
I have this code segment;
GetBoutiqueDetail: function (boutiqueId, options) {
if (IsUserLogin()) {
//different job A
} else {
ShowLoginPanel(undefined);
}
},
GetProductDetail: function (boutiqueId, productId, options) {
if (IsUserLogin()) {
//different job B
} else {
ShowLoginPanel(undefined);
}
},
AddBasket: function (productId, productVariantId, quantity, options) {
if (IsUserLogin()) {
//different job C
} else {
ShowLoginPanel(undefined);
}
},....
.And ~20 functions should check if user login or not.
I should call functions like similar to "Library.GetBoutiqueDetail();"
So my question is simple, how can I refactor that code to remove these if-else sections ?

Try something like this:
checkLogin: function( action, actionArgs ) {
if( IsLogin ) {
return action.apply(this, actionArgs );
}
ShowLoginPanel();
},
GetBoutiqueDetail: function (boutiqueId, options) {
//different job A
},
GetProductDetail: function (boutiqueId, productId, options) {
//different job B
},
AddBasket: function (productId, productVariantId, quantity, options) {
//different job C
}

How about an object map for this:
var objMap = {
"GetBoutiqueDetail":fnJobA,
"GetProductDetail":fnJobB,
"AddBasket":fnJobC}
....
}
if (loggedIn) {
objMap[task]();
}
else {
doLogin();
}

You could always wrap the common code into a higher-scope function, and have it invoked from the library functions - e.g:
//Higher scope:
function CheckForLogin(executionFunction)
{
if(IsLogin) {
executionFunction();
} else {
ShowLoginPanel(undefined);
}
};
GetBoutiqueDetail: function (boutiqueId, options) {
CheckForLogin(//different job A)
}
Passing in different job 'N' as an anonymous function to CheckForLogin

In Javascript you can return from a function to end it, so f.ex:
GetProductDetail: function (boutiqueId, productId, options) {
if (!IsLogin) return ShowLoginPanel();
// different job...
}
You will still have some repetitive code though. Another option is to define a higher level function. Something like:
var loginOrAction = function() {
if (!IsLogin) return ShowLoginPanel();
var args = [].slice.call(arguments);
Library[args.shift()].apply(Library, args);
}
loginOrAction('GetBoutiqueDetail', boutiqueId, options);

use the ternary operator
(IsLogin) ? jobA() : ShowLoginPanel(undefined)

Related

How to write a singleton service for a Vue3 component javascript

I have a tooltip control I've written that works very nicely in Vue 3, but I need a mechanism to fire off to all other instances to tell them to close. There are delays on close, so I'm occasionally getting two tooltips to show up at the same time.
This method, which was a crutch I've used in the past, is not allowed by the compiler / build tools. I can full well understand why, but I don't know the right way:
tooltipManager: function() {
if (!window.TooltipManager) {
function tooltipManager() {
let _data = {
tooltipIndex: 0,
callbacks: {}
};
return {
register: function (callback) {
let id = "tooltip_" + _data.tooltipIndex;
_data.tooltipIndex++;
_data.callbacks[id] = callback;
return id;
},
closeOpenPopups: function (id) {
Object.keys(_data.callbacks).forEach(key => {
if (id !== key) {
_data.callbacks[key]();
}
});
},
destroy: function (id) {
delete _data.callbacks[id];
}
};
}
window.TooltipManager = tooltipManager();
}
return window.TooltipManager()
},
The first thing I tried but didn't work was a service which I imported:
export default class TooltipManager {
constructor() {
if(! TooltipManager.instance){
this._data = {
tooltipIndex: 0,
callbacks: {}
};
}
}
register (callback) {
let id = "tooltip_" + this._data.tooltipIndex;
this._data.tooltipIndex++;
this._data.callbacks[id] = callback;
return id;
}
closeOpenPopups(id) {
Object.keys(this._data.callbacks).forEach(key => {
if (id !== key) {
this._data.callbacks[key]();
}
});
}
destroy(id) {
delete this._data.callbacks[id];
}
}
Ok, I was close with the first service. It should be written this way, and I'm going to leave my console.logs in that confirmed that it is indeed a singleton even though it is running on different tooltips.
class TooltipManager {
constructor() {
if(! TooltipManager.instance){
this._data = {
tooltipIndex: 0,
callbacks: {}
};
console.log("got new instance");
} else {
console.log("got old instance");
}
}
register (callback) {
let id = "tooltip_" + this._data.tooltipIndex;
this._data.tooltipIndex++;
this._data.callbacks[id] = callback;
console.log("registered key: " + id);
return id;
}
closeOpenPopups(id) {
Object.keys(this._data.callbacks).forEach(key => {
if (id !== key) {
console.log("closed: " + key);
this._data.callbacks[key]();
}
});
}
destroy(id) {
delete this._data.callbacks[id];
}
}
export default new TooltipManager();
I got the following from console.logs:
got new instance
TooltipManager.js:19 registered key: tooltip_0
TooltipManager.js:19 registered key: tooltip_1
TooltipManager.js:19 registered key: tooltip_2
TooltipManager.js:19 registered key: tooltip_3
TooltipManager.js:26 closed: tooltip_0
TooltipManager.js:26 closed: tooltip_1
TooltipManager.js:26 closed: tooltip_2
TooltipManager.js:26 closed: tooltip_3
And indeed it solved the problem of ghost tooltips when one pops up before the other closes with a delay to prevent bounce.
In the tooltip tool I wrote, which I will later post here as an example of how easy Vue3 Teleport makes something like this to write. I want to test it a little longer before I show it off.
I just need to:
mounted() {
this.data.tooltipId = TooltipManager.register(this.forceHide);
And, which also shows some state data I use to keep track of this:
methods: {
forceHide: function() {
if (this.data.isDisplayed) {
this.data.style = '{top: -1000px, left: -1000px}';
}
this.data.hideRequested = false;
this.data.showRequested = false;
this.data.isDisplayed = false;
},
Now the next thing maybe using Vuex for this, but I may leave this in as an alternative method so it's not dependent on it.

Exported function to pass arguments and a constant to another function

I don't really know how to describe this, but I'll try explain it.
I want to be able to call func1() and func2(), but going through handler() in a module.
I want it in a way where calling module.exported1("foo") will call handler(func1, "foo"), in turn calling func1("foo"). The issue I'm having is that if I export 'exported1' as handler(func1), I can't pass any arguments exported1 was called with (As far as I know). Is there a workaround for this?
NOTE: It is a module, and I need it to be exported without the user needing to provide func1 and func2 to handler().
function func1(args) {
...
}
function func2(args) {
...
}
function handler(func, args) {
return func()
}
module.exports = {
exported1 = handler(func1, ...),
exported2 = handler(func2, ...)
}
Not sure I get why to use this pattern, but I am sure there is more to the code and guess you could do the following:
function func1(args) {
console.info(`func1 ${args}`);
}
function func2(args) {
console.info(`func2 ${args}`);
}
function handler(func, args) {
return func(args);
}
module.exports = {
exported1: (args) => {
return handler(func1, (args));
},
exported2: (args) => {
return handler(func2, (args));
},
};
You just need to export the function:
module.exports = {
exported = handler
}
Or, just:
exports.exported = handler
Now, after import, you can call with parameters:
exported(func1,...)
exported(func2,...)
After reading your edited question, I think you want to do something like this but I'm not pretty sure:
function handler(func) {
// you can replace it with function(args) { instead of arrow function
return (args) => {
return func(args)
}
}
module.exports = {
exported1 = handler(func1),
exported2 = handler(func2)
}
exported1(args)

Convert an ES5 IIFE in ES6, OOP Javascript programming

I Need to refactor an IIFE in ES6. In ES6 let and const have a block scope, so I really need an IIFE in ES6? This is the ES5 version of the code:
var oojs = (function(oojs) {
var createToolbarItems = function(itemElements) {
var items = [];
[].forEach.call(itemElements,
function(el, index, array) {
var item = {
toggleActiveState: function() {
this.activated = !this.activated;
}
};
Object.defineProperties(item, {
el: {
value: el
},
enabled: {
get: function() {
return !this.el.classList.contains('disabled');
},
set: function(value) {
if (value) {
this.el.classList.remove('disabled');
} else {
this.el.classList.add('disabled');
}
}
},
activated: {
get: function() {
return this.el.classList.contains('active');
},
set: function(value) {
if (value) {
this.el.classList.add('active');
} else {
this.el.classList.remove('active');
}
}
}
});
items.push(item);
});
return items;
};
oojs.createToolbar = function(elementId) {
var element = document.getElementById(elementId);
var items = element.querySelectorAll('.toolbar-item');
return {
items: createToolbarItems(items)
}
};
return oojs;
}(oojs || {}));
What is the best way to translate this code in ES6? I tried many solution but I miss something, and I get an error: oojs is not defined.
Maybe I can use a Class instead? As you can see from the code I'm writing a Toolbar API in a OOP way (I think...)
Thanks for any help
EDIT: Thanks to georg, I try to refactor my code using classes. This is the new ES6 version:
class Toolbar {
constructor(elementId) {
this.elementId = elementId;
}
get items() {
const element = document.getElementById(this.elementId);
return element.querySelectorAll(".toolbar-item");
}
createToolbarItems() {
return [...this.items].map(el => new ToolbarItem(el));
}
}
class ToolbarItem {
constructor(el) {
this.el = el;
}
get enabled() {
return !this.el.classList.contains('disabled');
}
set enabled(value) {
if (value) {
this.el.classList.remove('disabled');
} else {
this.el.classList.add('disabled');
}
}
get activated() {
return this.el.classList.contains('active');
}
set activated(value) {
if (value) {
this.el.classList.add('active');
} else {
this.el.classList.remove('active');
}
}
toggleActiveState() {
this.activated = !this.activated;
}
}
// const toolbar = new Toolbar('myToolbar');
// const toolbarItems = toolbar.createToolbarItems();
EDIT: please check if is the right way to write this code, I'm pretty new to ES6
Thanks again
You can start by factoring out the toolbar item code (var item and below):
class ToolbarItem
{
constructor(element) {
....
}
}
Now, decide if you want to keep enabled and activated as properties or refactor them to explicit methods like isEnabled and setEnabled. In the former case it would be,
class ToolbarItem {
get enabled() {
...
}
set enabled(value) {
...
}
}
while ordinary methods can be defined like this:
class ToolbarItem {
isEnabled() {
...
}
setEnabled(value) {
...
}
}
Once you get this sorted out, replace your item initialization code with items.push(new ToolbarItem(el)) and test.
Hope this helps you getting started, good luck!

Passing arguments while running lodash flow asynchronously

Given the code below, how can I pass id to the applySaveAsync function?
var then = _.curry(function (f, thenable) {
return thenable.then(f);
});
var validateAsync = _.flow(
function () { return _(someCondition).showError(ERROR_01).value(); },
then(function () { return _(anotherCondition).showError(ERROR_02).value(); })
);
var save = _.flow(
validateAsync,
then(applySaveAsync),
then(saveCompleted)
);
function applySaveAsync(id) {
// Saving...
}
save(22); // Calling save function with some id.
I can get the id on the validateAsync function, but I cannot return it back since validateAsync should return a promise.
Any way to achieve that?
The simplest choice would be not to use _.flow for the definition of validateAsync.
Since validateAsync does not take parameters nor has a result, you should just change the definition of save to not use _.flow:
function save(id) {
return validateAsync()
.then(function(){ return applySaveAsync(id) })
.then(saveCompleted)
}
We could also change validateAsync to pass through the id:
function validateAsync(id) {
return _(someCondition).showError(ERROR_01).value()
.then(function () { return _(anotherCondition).showError(ERROR_02).value(); })
.then(_.constant(id));
}
and even do that while still using _.flow
var validateAsync = _.flow(
function(id) { return _(someCondition).showError(ERROR_01).value().then(_.constant(id)); },
then(function(id) { return _(anotherCondition).showError(ERROR_02).value().then(_.constant(id)); })
);
but I would advise against that since validateAsync is not supposed to be a function that does takes parameters.
Let's write a wrapper function for such instead to let us do the pass-around in a functional way:
function pass(fn) {
return function(id) {
return fn().then(function() {
return id;
});
}
}
(if you prefer, you can try to compose that from then, _.constant and more)
so that one can write
var save = _.flow(
wrap(validateAsync),
then(applySaveAsync),
then(saveCompleted)
);
I found this package useful for you. In Async cases, you can use this package.
Although flow is one of the best implementations for declarative programming, it doesn't support modern JS programming style.
import { Conductor } from '#puzzleio/conductor';
const conductor = Conductor.createDefault();
const myAsyncWorkflow = conductor
.add(validateAsync)
.if({
check: item => item.isValid === true,
handler: item => console.log('Item is valid')
},
{
// else block
handler: item => console.log('Validation failed')
});
myAsyncWorkflow.run(obj)
.then(() => console.log('Successfully validated'))
.catch(console.error);

Usage of Javascript syntax : method(param1,param2,(something)=>{//some code},param4);

This method syntax seems to be little strange.
Here is the method declaration. Some normal javascript method implementation.
sendHttpRequest: function (aUri, aUploadData, aContentType, aExisting, aSetupChannelFunc, aFailureFunc, aUseStreamLoader = true) {
//method implementation
}
This is the actual usage. There what does mean by using ()=>{//some statements} as a parameter?
function someFunction(){
this.calendar.sendHttpRequest(requestUri, queryXml, MIME_TEXT_XML, null, (channel) => {
//some statements
return this;
}, () => {
if (this.setA && this.setB) {
//some statements
}
}, false);
//someFunction's statements
}

Categories