Generalizing two functions which act inversely on two properties - javascript

I have these two functions, and I feel there must be an elegant functional way to generalize them.
$scope.togglePick = function(){
let targetPhoto = $scope.photoImport.data[indexService.current];
targetPhoto.pick = !targetPhoto.pick;
if(targetPhoto.reject && targetPhoto.pick) {
targetPhoto.reject = false;
}
}
$scope.toggleReject = function() {
let targetPhoto = $scope.photoImport.data[indexService.current];
targetPhoto.reject = !targetPhoto.reject;
if (targetPhoto.reject && targetPhoto.pick) {
targetPhoto.pick = false;
}
}
I suppose I could pass in string params togglePick = toggle('pick', 'reject') and use [] notation in the toggle function, but this feels like magic strings... even though they're property names, I'm uncertain. I could also have:
function toggle(magicFlag){
let primary = magicFlag ? 'pick' : 'reject';
let secondary = magicFlag ? 'reject' : 'pick';
...
targetPhoto[primary] = !targetPhoto[primary];
...
}
Again, ick, magic flag. Is there a more elegant way to do this than longhand in each function?

The amount of code in these functions and the slight differences makes them fine the way they are. Without working the property string weirdness, I don't see how you really make this generic while remaining readable. Sometimes it's okay not to have the most generic code you possibly can. That line in the sand has to be drawn somewhere.

Related

How to Efficiently Utilize Objects in Conjunction with HTML Elements

I am getting pretty deep into an employee talent management system website and I am finding out really quick why OOP is so talked about. Being able to efficiently utilize it would change that game for me and make my codebase much more maintainable. I am currently working on a dropdown menu section, and while I have implemented OOP, I can tell from a mile away it is not "good code." It gets the job done but is very messy. I will have an example of the Object and Implementation down below. I have tried watching a few videos on implementing OOP in an existing project, but I keep coming up dry. I understand the basic concepts, but I am not sure how to implement these ideas in a real world application.
Here are some sticking points I am having:
I am not confident I instantiated my objects correctly/efficiently. I have a list of toggleable menus, each representing a candidate in the portal. Well, I created an object for each menu using a for loop. This worked, but it felt unintuitive. When I wanted to attach click events to these menus, I had to loop through them a second time to attached to objects methods as click events.
I felt I had a lot of 'if' statements in my Object. This is because I see myself using the object again in the future, but I do not think each menu will have all the same features. For example, this specific menu had a fade in animation when clicked. But, I doubt every toggleable menu will have this feature. I don't know, I just felt it made the code way more difficult to navigate.
I did not like having to loop through my menu elements and attach methods as click events. This made me have to run an additional for loop and felt 'off'. I was tempted to handle all the event attachments inside the object itself, but I wasn't sure if this is bad practice. It seemed like a good idea and would save a lot of code, but I'm not sure.
Long story short, I can see myself using OOP for forms, buttons, toggleable menus, navbars, (ect.) all over the place. The problem is I am not sure how to actually carry out the process of handling HTML elements and converting them to Objects in a clear and concise manner.
I looked into some design patterns on refactoring.guru, but I felt these concepts were past the scope of what I am trying to do. They all felt like more advanced concepts to take on after getting a solid grip on objects. It was helpful and I really liked the builder pattern. I am currently working on a branch which implements the builder pattern into my current scenario.
Any thoughts, advice, or direction?
Here is an example of the Object and it's Implementation:
TOGGLE MENU OBJECT
class ToggleMenu {
constructor(wrapper, menu, openIcon, closeIcon, title, hiddenMenu) {
this.wrapper = wrapper
this.menu = menu
this.openIcon = openIcon
this.closeIcon = closeIcon
this.title = title
this.hiddenMenu = hiddenMenu
this.toggled = false
}
props() {
console.log(this.menu)
console.log(this.openIcon)
console.log(this.closeIcon)
console.log(this.title)
console.log(this.hiddenMenu)
console.log(this.toggled)
}
openHiddenMenu(config) {
if (config == undefined){
config = {}
}
if (config.menuBackGroundColor !== undefined){
this.menu.style.backgroundColor = config.menuBackGroundColor
}
if (config.titleColor !== undefined){
this.title.style.color = config.titleColor
}
if (config.hiddenMenuDisplay !== undefined){
this.hiddenMenu.style.display = config.hiddenMenuDisplay
}
if (config.hiddenMenuAnimation !== undefined){
this.hiddenMenu.style.animationName = config.hiddenMenuAnimation
}
if(config.menuAnimation !== undefined){
this.menu.style.animationName = config.menuAnimation
}
this.openIcon.style.display = 'none'
this.closeIcon.style.display = 'block'
this.toggled = true
}
closeHiddenMenu(config){
if (config == undefined){
config = {}
}
if (config.menuAnimation !== undefined && this.toggled == true){
this.menu.style.animationName = config.menuAnimation
}
this.menu.style.backgroundColor = ''
this.openIcon.style.display = ''
this.closeIcon.style.display = ''
this.title.style.color = ''
this.hiddenMenu.style.display = ''
this.toggled = false
}
}
IMPLEMENTATION
const initCandidateMenus = () => {
let candidateToggleMenus = document.getElementsByClassName('candidate-toggle-menu')
let candidateToggleWrappers = document.getElementsByClassName('candidate-toggle-menu-wrapper')
let hiddenMenus = document.getElementsByClassName('hidden-candidate-menu')
let toggleMenus = []
//collecting toggle menu objects
for (x = 0; x < candidateToggleMenus.length; x++){
let toggleMenu = new ToggleMenu(
candidateToggleWrappers[x],
candidateToggleMenus[x],
candidateToggleMenus[x].getElementsByClassName('candidate-open-icon')[0],
candidateToggleMenus[x].getElementsByClassName('candidate-close-icon')[0],
candidateToggleMenus[x].getElementsByClassName('candidate-name')[0],
hiddenMenus[x],
)
toggleMenus.push(toggleMenu)
}
//looping through toggle menu objects
for (x = 0; x < toggleMenus.length; x++){
let currentMenu = toggleMenus[x]
currentMenu.openIcon.addEventListener('click', () => {
//closing all other toggle menus
for (y = 0; y < toggleMenus.length; y++){
toggleMenus[y].closeHiddenMenu()
}
//opening current menu
currentMenu.openHiddenMenu({
menuBackGroundColor: 'var(--main-clr)',
titleColor: 'var(--white)',
menuAnimation: 'fade-title-color',
hiddenMenuDisplay: 'flex',
hiddenMenuAnimation: 'open-hidden-menu'
})
})
currentMenu.closeIcon.addEventListener('click', () => {
//closing current menu
currentMenu.closeHiddenMenu()
})
}
}
ok this isn’t really an answer, but more of just advice that I need to use code for.
I noticed that you didn’t implement a lot of common features that can reduce lines and characters, so I’ll point them out here
For the class functions, you did this:
openHiddenMenu(config) {
if (config == undefined){
config = {}
}
Which can be simplified to this:
openHiddenMenu(config = {}) {
// Completely remove the entire if statement
Putting an equal sign in the parameter will assign a default value if it isn’t defined.
Another thing is checking if something is undefined:
if (config.menuBackGroundColor !== undefined){
this.menu.style.backgroundColor = config.menuBackGroundColor
}
if (config.titleColor !== undefined){
this.title.style.color = config.titleColor
}
if (config.hiddenMenuDisplay !== undefined){
this.hiddenMenu.style.display = config.hiddenMenuDisplay
}
if (config.hiddenMenuAnimation !== undefined){
this.hiddenMenu.style.animationName = config.hiddenMenuAnimation
}
if(config.menuAnimation !== undefined){
this.menu.style.animationName = config.menuAnimation
}
Instead you could remove the ‘!== undefined’ part, because any value that is ‘falsey’ (0, NULL, undefined, NaN) will also return false. So if it has a value and isnt 0, it will return true.
Plus since it’s only 1 line, you can remove brackets:
if (config.menuBackGroundColor)
this.menu.style.backgroundColor = config.menuBackGroundColor
if (config.titleColor)
this.title.style.color = config.titleColor
if (config.hiddenMenuDisplay)
this.hiddenMenu.style.display = config.hiddenMenuDisplay
if (config.hiddenMenuAnimation)
this.hiddenMenu.style.animationName = config.hiddenMenuAnimation
if(config.menuAnimation)
this.menu.style.animationName = config.menuAnimation
If you wanted to simplify even further, you could use an or operator ||, so if the value is falsey, it will become the other value. This doubles as a default value.
this.menu.style.backgroundColor = config.menuBackGroundColor || // Default value
this.title.style.color = config.titleColor || // Default value
this.hiddenMenu.style.display = config.hiddenMenuDisplay || // Default value
this.hiddenMenu.style.animationName = config.hiddenMenuAnimation || // Default value
this.menu.style.animationName = config.menuAnimation || // Default value
There are some other things you can do, but this answer is insanely long and I’m writing this late at night. Hope this helps though

Javascript function parameters in Acrobat

Hopefully you all don't get pissed at me for such a seemingly simple question..
Basically, I have a PDF form that I'm scripting with javascript.
I have a bunch of check boxes that I would like to set required and/or not required based on other inputs and I'm trying to repeat code as little as possible, especially since there's a ton of inputs.
Right now, the best way I can accomplish what I'm attempting is by setting a function for each instance of inputs as follows:
function setWalkwayNotRequired() {
this.getField("sidewalkAsphalt").required = false;
this.getField("sidewalkConcrete").required = false;
this.getField("sidewalkPavers").required = false;
this.getField("sidewalkCondition").required = false;
}
I would then call this function based on the input of a certain checkbox:
if (this.getField("sidewalkNone").value == "Yes") {
setSidewalkNotRequired();
}
Then all of the above-mentioned fields would be set to not required.
I feel like there should be a way to create a single "setRequired" or "setNotRequired" function to take a parameter of the field in question.
In my mind that would look something like this:
function setRequired(a, b, c, d) {
this.getField(a).required = true;
this.getField(b).required = true;
this.getField(c).required = true;
this.getField(d).required = true;
}
I would then call on that function for all instances, for example, walkways (like that above) or driveways, etc. like so:
if (this.getField("sidewalkNone").value == "Off") {
setRequired('"sidewalkAsphalt"', '"sidewalkConcrete"', '"sidewalkPavers"', '"sidewalkCondition"');
}
Again, in my mind what would then be output based on the above code once the function is called is something like:
if (this.getField("sidewalkNone").value == "Off") {
this.getField("sidewalkAsphalt").required = true;
this.getField("sidewalkConcrete").required = true;
this.getField("sidewalkPavers").required = true;
this.getField("sidewalkCondition").required = true;
}
Doing it the way I did in the first code block would require me to create separate functions for each set of checkboxes, creating a lot of code in an already huge file. The second way would allow me to use 1 function over and over throwing the field names as parameters depending on where I'm at in the PDF.
I'm also not very clear on if it's even legal to declare the parameters as I did with the '"..."' quotes; I did that because I need the double quotes inside the this.getField().
Again, I'm sorry if this is novice, I've just been trying to play with the code for a while now and can't get it to work.
Any input would be amazing.
You could just pass in an Array of field names:
function setRequired( fieldNames, isRequired = true ) {
for( var i = 0; i < fieldNames.length; i++ ) {
var fieldName = fieldNames[i];
this.getField( fieldName ).required = isRequired;
}
}
Usage:
if( this.getField("sidewalkNone").value == "Off" ) {
setRequired( [ "sidewalkAsphalt", "sidewalkConcrete", "sidewalkPavers", "sidewalkCondition" ] );
}
If you use hierarchical naming with dot notation, you can set properties on the parent to affect all children. For example, if you name the fields "sidewalk.Asphalt", "sidewalk.Concrete", and "sidewalk.Pavers"...
this.getField("sidewalk").required = true;
... will set all the children to be required.

Functional programming javascript find in list

I want to get the first element in a array that matchs a function in O(n) using functional (maybe pure) style. Note im using the functional.js library, but its not that important.
Code to REFACTOR to a pure functional way:
var result = fjs.first(function(value){
return value.id == notification.id;
},array);
I want to refactor it by extracting out the .id for both objects and pass in the notification object, right now its in above scope
So i tried to first map then find. But its super ugly (in my opinion).
var equal = fjs.curry(function(arg1, arg2){ // curry allows for partial functions... i think...
return arg1 == arg2
});
var equalNotificationId = fts.equal(notification.id); // partial function of equal for notification.id number
var newIdArray = fts.map(fts.prop("id"),array);
var result = fts.first(equalNotificationId , newIdArray );
Im looking to refactor this functionaly maybe as pure as possible? Is there a better way?
I found a way to refactor it but if anyone has anything else to bring to this ill give them the points.
//example data
var notification = {id:10};
var array = [{id:11},{id:6},{id:10},{id:3},{id:1}];
//equal function
var equal = fts.curry(function(arg1,arg2){
return arg1 == arg2;
});
//returns object in array with id 10.
var result = fjs.first(fts.compose(equal(notification.id),fts.prop("id")),array);

Combine if/else into one statement? (Javascript)

function changeButton () {
if (event.currentTarget.className == 'btnRed') {
event.currentTarget.className = 'btnGreen';
} else {
event.currentTarget.className = 'btnRed';
}
}
Let's say I have the above code. I have seen similar codes written that would combine these two, but I don't really remember how it did it. It was something like className = (btnGreen | btnRed).
I am very sorry for the vagueness of this question.
You can use the ternary operator (condition) ? (true) : (false)
event.currentTarget.className = event.currentTarget.className == 'btnRed' ? 'btnGreen' : 'btnRed';
I would go a little bit further, extract the strings into variables to remove the ability to mistype them across the solution. And refactor event.currentTarget into a variable.
var RED_BUTTON_CLASS = 'btnRed',
GREEN_BUTTON_CLASS = 'btnGreen';
var currentTarget = event.currentTarget;
currentTarget.className = currentTarget.className == RED_BUTTON_CLASS ? GREEN_BUTTON_CLASS : RED_BUTTON_CLASS;
This I feel will just make it easier in the long run, completely optional
Edit
So adding extra information from what Jan said.
var RED_BUTTON_CLASS = 'btnRed',
GREEN_BUTTON_CLASS = 'btnGreen';
These probably describe a state, so you could better name them:
var ERROR_BUTTON_CLASS = 'btnRed',
OK_BUTTON_CLASS = 'btnGreen';

What is this pattern f(a)(b)(c) called?

function setStyle(x) {
with (x.style) {
padding = '10px'
marginBottom = '10px'
width = '100%'
}
return setStyle
}
setStyle(account)(password)(login)
I often use the above pattern, but when I need to talk about it, I have no idea what is it called? How to refer to this kind of approach in the conversation?
UPDATE:
Someone closed this question as duplicate, but I cannot see why is that a duplicate. Other question is about some framework, the poster is trying to understand the code. I do not try to understand the code, I am asking about an English term for a known pattern. Even though the discussed concept is the same, the question is different. It is like saying that "Who was the youngest in Jasckson's Five" and "Whose album is Thriller" both are the same question, only because it happens to be about the same person. But those are different questions, not the same.
This pattern is not "function chaining", because chained (=composed, pipelined) functions work by passing result of one function as an argument to the next one. For example, if you have something like:
str = removeBadChars(str)
str = makeLowerCase(str)
str = convertToURL(str)
you can rewrite this more concisely as
str = chain(removeBadChars, makeLowerCase, convertToURL)(str)
(Exercise: write chain()).
Your pattern doesn't appear to have a name, probably because it's fairly useless. Mr. Crockford coined the term retursion,
but it doesn't seem to be used widely.
Note that this pattern is essentially anti-functional, because in functional programming we prefer to work with pure functions, while a "retursive" function can only do anything useful as a side effect.
UPD: this reasoning applies to functions that just blindly return itself, like in your example, however, the f()()() syntax can also be used with functions that return something else on each invocation. In this case, the "root" function accepts a hidden parameter ("accumulator") used to carry state around. A good example of such approach would be chain() like above, but written in the applicative style: chain(func1)(func2)(func3):
function chain(f, fun) {
fun = fun || function(x) { return x };
if(typeof f != "function")
return fun(f);
return function(x) {
return chain(x, function(y) { return f(fun(y)) })
}
}
// Example:
makeLowerCase = function(str) { return str.toLowerCase() };
removeBadChars = function(str) { return str.replace(/\W/g, "") };
makeURL = function(str) { return "http://" + str };
funcs = chain(makeLowerCase)(removeBadChars)(makeURL);
x = funcs('!!! HELLO ???');
alert(x);
In this case, this syntax is used to implement "partial application", because we return a closure which calls the root function yet again, with a predefined second argument.

Categories