Use a variable inside a function in other function - javascript

I have a scope problem, in this code when I call mp3.playlistInfo() ${titlePlayList} is undefined.
How can I use ${titlePlaylist} inside playlistInfo: function()
const mp3 = {
createPlaylist: function() {
let titlePlaylist = prompt("Choose a name: ")
console.log(`Your playlist name is: ${titlePlaylist}`)
},
playlistInfo: function() {
console.log(`Already listening ${titlePlaylist}`)
}
}
mp3.createPlaylist();
mp3.playlistInfo();

The issue is that you haven't declared titlePlaylist anywhere that's accessible for the other function.
You could solve this by just adding it to the mp3 object you created.
I would however suggest using a Class rather than an Object, if you were going to go with this approach.
Simple Object based approach
const mp3 = {
titlePlaylist: null,
createPlaylist: function() {
this.titlePlaylist = prompt("Choose a name: ")
console.log(`Your playlist name is: ${this.titlePlaylist}`)
},
playlistInfo: function() {
console.log(`Already listening ${this.titlePlaylist}`)
}
}
mp3.createPlaylist();
mp3.playlistInfo();
Simple Class based approach
class MP3 {
constructor() {
this.titlePlaylist = prompt("Choose a name: ");
this.playlistInfo();
}
playlistInfo() {
console.log(`Already listening ${this.titlePlaylist}`)
}
}
const mp3 = new MP3();

As mp3 is an object you'll need to set titlePlaylist as a property of mp3. Then in createPlaylist you need to set the returned value from the user to the value of titlePlaylist. You can then access titlePlaylist in playlistInfo, and if you use arrow functions you can skip using this.titlePlaylist:
const mp3 = {
titlePlaylist: "",
createPlaylist: () => {
titlePlaylist = prompt("Choose a name: ");
console.log(`Your playlist name is: ${titlePlaylist}`);
},
playlistInfo: () => {
console.log(`Already listening ${titlePlaylist}`);
},
};
mp3.createPlaylist();
mp3.playlistInfo();
You can also achieve this functionality by taking advantage of closures which allow you to get and set variables scoped within the original function body:
const mp3Closure = () => {
let titlePlaylist = "";
return {
createPlaylist: () => {
titlePlaylist = prompt("Choose a name: ");
console.log(`Your playlist name is: ${titlePlaylist}`);
},
playlistInfo: () => {
console.log(`Already listening ${titlePlaylist}`);
},
};
};
const mp3WithClosure = mp3Closure();
mp3WithClosure.createPlaylist();
mp3WithClosure.playlistInfo();

OOP variant if needed:
class MP3 {
constructor(titlePlaylist) {
this.titlePlaylist = titlePlaylist;
}
titlePlaylist = null;
get titlePlaylist() {
return this.titlePlaylist;
}
set titlePlaylist(value) {
this.titlePlaylist = value;
}
createPlaylist = () => {
this.titlePlaylist = prompt("Choose a name: ");
console.log(`Your playlist name is: ${this.titlePlaylist}`);
};
playlistInfo = () => {
console.log(`Already listening ${this.titlePlaylist}`);
};
}
const mp3 = new MP3("");
mp3.createPlaylist();
mp3.playlistInfo();

Related

Object element returning undefined with addEventListener

I am making a simple form which requests the user to input their project name and their name, along with some other info.
But each type of project has a different page, so to avoid copying and pasting the same getElementById and addEventListener functions in each page, I've made a module with those functions so every page handles it as needed. This is one of the pages:
// (imports)...
let project = {
author: null,
name: null,
mission: {
file: null,
path: null,
},
sd: {
code: null,
path: null,
},
modloaderfolder: null,
};
window.addEventListener("DOMContentLoaded", () => {
project.mission = handleMission();
project.sd = handleSD();
project.modloaderfolder = handleModloader();
project.name = handleName();
project.author = handleAuthor();
// ...
The problem is that the objects' elements are returning undefined in the two following functions:
export function handleName() {
const project = {};
const txt_name = document.getElementById("name");
txt_name.addEventListener("change", () => {
project.name = txt_name.value;
});
return project.name;
}
export function handleAuthor() {
const project = {};
const txt_author = document.getElementById("author");
txt_author.addEventListener("change", () => {
project.author = txt_author.value;
});
return project.author;
}
// Returns undefined
Whats intriguing for me is that some other functions are working as intended, I can't find out why. These are the corretly working functions:
export function handleSD() {
const project = { sd: {} };
const input_sd = document.getElementById("sd");
input_sd.addEventListener("change", () => {
document.getElementById("sel-sd").innerHTML = "";
Array.from(input_sd.files).forEach((file) => {
document
.getElementById("sel-sd")
.insertAdjacentHTML("beforeend", `<option>${file.name}</option>`);
});
if (!input_sd.files[0]) return;
const path = input_sd.files[0].path;
project.sd.path = path.substring(0, path.lastIndexOf("\\")) + "\\";
project.sd.code = project.sd.path.substring(
project.sd.path.lastIndexOf("\\") - 5,
project.sd.path.length - 1
);
});
return project.sd;
}
// This function correctly returns "sd: { path: ..., code: ...}"
What I noticed is that by returning an object, it returns and updates correctly for each change, but while returning an object's element, it aways returns undefined.
which is empty and nothing will change it because copying a primitive value is by value but the object that holds the key of the string, and as soon as the onchange function is activated it changes the value of your key in the object and it automatically changes in your object too because it is copied by Reference.
You can read more about this topic here

node.js edit variable from another file

Hello i have just started to learn node.js today and i am having a problem getting a variable from another file and editing it.
I'm trying to get the variable bots from server.js and edit it in bots.js.
server.js:
var bots = [
{ botID: '16f11103', userID: '12345' },
{ botID: '5657d5e9', userID: '54321' }
];
setInterval(() => { console.log(bots); }, 5000);
module.exports.bots = bots;
bots.js:
var request = require("../server.js");
var unique_id = '16f11103';
if (request.bots.some(e => e.botID === unique_id)) {
request.bots = request.bots.filter(function(e) {
return e.botID != unique_id;
});
}
One way of doing this is to create a function that sets the variable bots.
Note: Instead of using seperate variables and functions i suggest to use something like a class to group all the functionality surrounding the bots.
server.js
var bots = [
{ botID: "16f11103", userID: "12345" },
{ botID: "5657d5e9", userID: "54321" },
];
var setBots = (newBots) => (bots = newBots);
setInterval(() => {
console.log(bots);
}, 5000);
module.exports = { bots, setBots };
bots.js
var { bots, setBots } = require("./server.js");
var unique_id = "16f11103";
if (bots.some((e) => e.botID === unique_id)) {
var newBots = bots.filter(function (e) {
return e.botID != unique_id;
});
setBots(newBots);
}
It's not working because of how variables are modified in JavaScript (Think copy by reference vs value).
When you first export the bots variable in server.js:
module.exports.bots = bots;
module.exports.bots is pointing to the bots variable by reference. If you modify the array directly like module.exports.bots[0] = 1 it will be reflected in the original bots variable as well since the reference hasn't changed.
However, what you're doing is reassigning the module.exports.bots value completely. The Array.prototype.filter() returns a new array without modifying the original.
When you do:
request.bots = request.bots.filter(function(e) {
return e.botID != unique_id;
});
You're making it so that the module.exports.bots variable points to a new array and no longer points to the original bots array. That's why your console.log call is always printing the same array because the original bots variable is still pointing to that array and it was never modified.
If you don't want to modify the array directly you can try this.
server.js
var request = {};
request.bots = [
{ botID: '16f11103', userID: '12345' },
{ botID: '5657d5e9', userID: '54321' }
];
setInterval(() => { console.log(request.bots); }, 5000);
module.exports = request;
bots.js can now be kept exactly the same
var request = require("../server.js");
var unique_id = '16f11103';
if (request.bots.some(e => e.botID === unique_id)) {
request.bots = request.bots.filter(function(e) {
return e.botID != unique_id;
});
}
You should do
var bots = [
{ botID: '16f11103', userID: '12345' },
{ botID: '5657d5e9', userID: '54321' }
];
setInterval(() => { console.log(bots); }, 5000);
module.exports = bots;
instead of
module.exports.bots = bots;
You can also do
exports.bots = bots;
Then use Like this
var bots = require("./server.js");
var unique_id = '16f11103';
if (bots.some(e => e.botID === unique_id)) {
bots = bots.filter(function(e) {
return e.botID != unique_id;
});
}

Dynamically building a complex object

This will be the different for every user based on their roles. I am looking to build an object that can look like this:
let permissions = {
'state': {
'tool': ['subTool1', 'subTool2']
}
}
An example:
roles = ['NY_email_submit', 'NY_email_approve', 'NY_build_submit', 'NY_build_view', 'DC_email_submit']
let permissions = {
'NY': {
'email': ['submit', 'approve'],
'build': ['submit', 'view']
},
'DC': {
'email': ['submit']
}
};
I am looping through a list, named roles, passed in that contains strings broken up like state_tool_subTool.
I would like it to have no duplicates. For example, if the next user role ran through the loop with the object above is NY_build_approve, I would like to simply add approve to the the list at ['build'].
Currently I have this that is not working correctly.
roles.forEach(role => {
role = role.split('_');
let state = role[0];
let tool = role[1];
let subTool = role[2];
if ([state] in permissions) {
permissions[state] = { [`${tool}`]: [subTool] };
} else {
//permissions[state][`${tool}`].push(subTool);
}
});
This should do the trick! You were on the right track, just needed another layer of checks
let permissions = {};
roles = ['NY_email_submit','NY_email_approve','NY_build_submit','NY_build_view', 'DC_email_submit'];
roles.forEach(role => {
let [state, tool, subTool] = role.split('_');
if (state in permissions) {
if (tool in permissions[state]) {
permissions[state][tool].push(subTool)
} else {
permissions[state][tool] = [subTool]
}
} else {
permissions[state] = {[tool]: [subTool]}
}
});
console.log(permissions);
roles = ['NY_email_submit', 'NY_email_approve', 'NY_build_submit', 'NY_build_view', 'DC_email_submit']
let permissions = {};
roles.forEach(role => {
role = role.split('_');
let state = role[0];
let tool = role[1];
let subTool = role[2];
if (!permissions[state]) {
permissions[state] = {[tool] : [subTool]};
} else {
if (permissions[state][tool]) {
if(!permissions[state][tool].includes(subTool)) {
permissions[state][tool] = [...permissions[state][tool], subTool];
}
}
else {
permissions[state][tool] = [subTool];
}
}
});
console.log(permissions);
here is another approach using reduce
roles = ['NY_email_submit', 'NY_email_approve', 'NY_build_submit', 'NY_build_view', 'DC_email_submit']
sp=roles.map(o=>o.split("_")).reduce((acc,curr)=>{
if (!acc[curr[0]]) acc[curr[0]]={...acc[curr[0]],[curr[1]]:[...[curr[2]]]}
else {
if(acc[curr[0]][curr[1]]) {
i=acc[curr[0]][curr[1]]
acc[curr[0]]={...acc[curr[0]],[curr[1]]:[...i,...[curr[2]]]} }
else {acc[curr[0]]={...acc[curr[0]],[curr[1]]:[...[curr[2]]]} }
}
return acc
},{})
console.log(sp)

Converting Calories Tracking application to ES6 Classes

I managed to recreate a MVC calorie tracker app from a course and I am trying to convert it to ES6 classes now.
I am a little stuck in understanding how to call the methods in the Module inside the Controller to return the items I need.
class Item {
constructor() {
this.data = {
items: [{
name: 'Salad',
calories: 200,
id: 0
},
{
name: 'Eggs',
calories: 500,
id: 1
}],
totalCalories: 0,
currentItem: null
}
};
getItems() {
return this.data.items
};
logData = () => {
console.log(data.items);
};
}
class App {
constructor(Item, UI) {
this.Item = Item;
this.UI = UI;
}
init() {
const items = Item.getItems();
UI.populateItemList(items)
}
}
const application = new App(new Item(), new UI())
When I try to call Item.logData() in the console it gives me TypeError: this.data is undefined.
I researched online and it seems that the method I declared is for the constructor only. How would I go about declaring methods that I'll use in the Controller or in any other class, just like I did below by returning a method out of the constructor?
What Im trying to convert initially looks like this:
const ItemCtrl = (function () {
const Item = function (id, name, calories) {
this.name = name;
this.id = id;
this.calories = calories;
}
const data = {
items: StorageCtrl.getStorage(),
totalCalories: 0,
currentItem: null
}
return {
getItems: function () {
return data.items
},
logData: function () {
return data;
}
}
const App = (function (ItemCtrl, StorageCtrl, UICtrl) {
return {
init: function () {
const items = ItemCtrl.getItems();
UICtrl.populateItems(items);
}
}
})(ItemCtrl, StorageCtrl, UICtrl);
App.init();
You need to initialise the controller first:
class App {
constructor(Item, UI) {
this.item = new Item();
this.UI = new UI();
}
init() {
const items = this.item.getItems();
this.UI.populateItemList(items)
}
}

Merge two objects by matching key while following Flow rules

I have created a function as shown below, which should append the array value into the first object.
Let's say we have given data below:
subjects = {
student1: ['Math', 'Science'],
student2: ['Math', 'Physics', 'English'],
};
students = {
student1: {
// other data
subjectsList: [],
},
student2: {
// other data
subjectsList: [],
},
};
Function code below:
const merge = (subjects: Object, students: Object) => {
Object.keys(subjects).forEach((id: Object) => {
const subjectsList = subjects[id];
const student = students[id];
if (student) {
const updatedStudent = {
...student,
subjectsList,
};
students[id] = updatedStudent;
}
});
return students;
};
This would result in a flow error:
Cannot access the computed property using object type [1].
app/reducers/subjects.reducer.js:42:32
42| const student = students[id];
^^
References:
app/reducers/subjects.reducer.js:40:48
40| Object.keys(subjects).forEach((id: Object) => {
^^^^^^ [1]
Object.keys(subjects).forEach((id: Object)
The id in the .forEach((id) => is not an Object, but a String
If you remove the typehinting (or whatever it is called).
const merge = (subjects, students) => {
Object.keys(subjects).forEach((id) => {
const subjectsList = subjects[id];
const student = students[id];
if (stand) {
const updatedStudent = {
...student,
subjectsList,
};
students[id] = updatedStudent;
}
});
return students;
};
I think this will work.

Categories

Resources