I want the user to have the option to add more stepTypes, stepCodes and properties. He can add an stepCode with an existing StepType, or with a different stepType, so, the object would like similar to this:
You see? In the stepType called 'guide', I have 2 stepCodes (G019, G040). In the stepType called 'success', I have just one (S003), and so on. Since I'm newbie with js and even more with objects, I'd like you guys to help me creating a function that checks if the stepType already exists, and then adds another stepCode to it (with its properties). And, if it doesn't exist yet, I want this function to create this new stepType, with the stepCode and its properties.
Currently, my code looks like this:
const checkStep = () => {
if (!Object.keys(procedures).length) {
let proc =
{[key]:
{
[stepType]: {
[stepCode]: {
[language]: stepText,
timeout,
nextInstruction,
}
}
}
}
setProcedures(proc)
}
else{
Object.entries(procedures).forEach((p, k) =>{
...
})
}
}
I call this function everytime the user clicks the "Add another step" button. The first part checks if the object already exists, and, if it doesn't, it creates the object with its key and so on (this part is working). What I don't know how to do is the ELSE part. I think we have to check if the stepType already exists in the object called procedures, but I don't know how to do it. I don't know how to put the stepCode and properties inside the existing object(procedures) either. Maybe I create a variable and do like: setProcedures (...procedures, variable). I don't want to lose the content I have in the procedure, just to add more content to it in the way I explained you.
P.S.: All the variables (stepType, stepCode, language, stepText, timeout, nextInstruction) are an useState. When the user writes anything in the input text field, I set the specific variable with the e.target.value.
Thank you.
You can do the loop on the each keys and if it matches then add to existing data or create a new stepType and add.
var newStepType = "test", stepCode="test1", language ="en", stepText="hello", timeout=9, nextInstruction="new ins";
Object.keys(procedure.DOF0014).forEach(key => {
//if newStepType matches insert stepCode. eg: stepType is "guide"
if(key === newStepType) {
procedure.DOF0014[key] = { ...procedure.DOF0014[key], ...{[stepCode]: {[language]: stepText,timeout,nextInstruction}}}
}else{
procedure.DOF0014 = {...procedure.DOF0014, ...{[newStepType]:{[stepCode]: {[language]: stepText,timeout,nextInstruction}}}};
}
});
Try this. I didnt tested code. But hope it works. I am sharing the idea how to do.
Object.keys(procedure).forEach(codes => {
Object.keys(procedure[codes]).forEach(key => {
if(key === newStepType) {
procedure[codes][key] = { ...procedure[codes][key], ...{[stepCode]: {[language]: stepText,timeout,nextInstruction}}}
}else{
procedure[codes] = {...procedure[codes], ...{[newStepType]:{[stepCode]: {[language]: stepText,timeout,nextInstruction}}}};
}
});
})
Related
I have tried to make an function with a onclick that when you click it, it will change the value from 'suspended' in true (this is about suspending a website user account)
suspended = false;
type user = User['suspended'];
function blokkeerFunctie() {
// get user & user element
document.getElementById('userInfo') && document.getElementById('blokkeren');
// blocks user when clicked
if (document.getElementById('blokkeer')?.addEventListener('click', blokkeerFunctie)) {
type user = !User['suspended'];
} else {
// deblocks user when clicked
document.getElementById('deblokkeer')?.addEventListener('click', blokkeerFunctie);
type user = User['suspended'];
}
console.log('blokkeerFunctie');
}
blokkeerFunctie();
I thought with !User i will reverse the boolean value from false in true, but that code isn't even read. ▼
'user' is declared but never used.ts(6196)
You shouldn't put event listeners in your conditional if/else in this way. Here's how I would approach what you're trying to accomplish. You will need to add types to these variables, but you'll get the basic logic here.
let User = {
suspended: true
};
let button = document.querySelector('#suspender');
function setSuspendButton() {
button.innerText = User['suspended'] ? 'Unsuspend' : 'Suspend';
}
window.addEventListener('load', () => {
button.addEventListener('click', blokkeerFunctie)
setSuspendButton();
})
function blokkeerFunctie() {
User['suspended'] = !User['suspended'];
setSuspendButton();
}
<button id='suspender'></button>
type user = creates a new type, not a value. It's unused in each branch of the if because you just create a new type, named user which shadows the type of the same name from the parent scope, which is never used by anything.
Furthermore, this line does nothing:
document.getElementById('userInfo') && document.getElementById('blokkeren');
This line gets up to two references to DOM elements, but doesn't save them anywhere, or perform any logic on them.
I think you want something more like this?
const user = {
name: 'Joe',
suspended: false
}
function blokkeerFunctie() {
// block/deblocks user when clicked
if (document.getElementById('blokkeer')?.addEventListener('click', blokkeerFunctie)) {
user.suspended = !user.suspended // toggle `suspended` back and forth
}
console.log('blokkeerFunctie');
}
blokkeerFunctie();
Working example
Currently, I have a select element in my html which has a ngModel to the object details:
[ngModel]="details?.publicInformation?.firstname"
However, publicInformation may not exist in that object, or if it does, maybe firstname does not exist. No matter the case, in the end, I want to create the following:
[ngModel]="details?.publicInformation?.firstname" (ngModelChange)="details['publicInformation']['firstname'] = $event"
Basically, if the select is triggered, even if neither of publicInformation nor firstname exist, I would like to create them inside details and store the value from the select.
The issue is that I am getting
Cannot set property 'firstname' of undefined
Can someone explain what I am doing wrong here and how can I achieve the result I desire?
You need to initialize details and publicInformation to empty object
public details = {publicInformation : {}};
You should do that when you load the form data.
For example, you might have something like this:
ngOnInit() {
this._someService.loadForm().then((formData: FormData) => {
this.details = formData;
});
}
Then, you could modify that to fill in the missing empty properties you need:
ngOnInit() {
this._someService.loadForm().then((formData: FormData) => {
this.details = formData || {};
if (!this.details.publicInformation) {
this.details.publicInformation = { firstname: '' };
} else if (!this.details.publicInformation.firstname) {
this.details.publicInformation.firstname = '';
}
});
}
However, it would be better to place this logic in the services, so that they are responsible for adding all the necessary empty properties to the data they load, or if you are using Redux, then it should go into the reducers.
So I'm trying to figure out how to use kind of an all-encompassing function to reduce bloat in my application. I've got a bunch of dialog windows that are handled via state, similar to this:
toggleSettingsDialogue = () => {
this.setState({settingsOpen: !this.state.settingsOpen});
}
I'm trying to reduce this function, which is repeated for each additional dialog, into one. My thought is to pass in two parameters - one for the dialog that's meant to be opened, and another that defines the state of that dialog - either true or false.
The issue is, I'm stuck on figuring out how to check if the first parameter passed (i.e. the name of the dialog window in state) exists or not.
Let's say we've got a state with...
state = {
diagSettingsOpen: false,
diagAddItemOpen: false
}
How would I check to see if any string passed as a parameter inside the function is actually there or not, and subsequently use that key to set state if it matches?
toggleSettingsDialogue = key => {
if(key in this.state)
this.setState(({[key]: val}) => ({[key]: !val}));
}
Here's how you can check the same -
let state = {
diagSettingsOpen: false,
diagAddItemOpen: false
}
function setState(stateName, value) {
if (state.hasOwnProperty(stateName)) {
state[stateName] = value;
} else {
console.log("invalid state");
}
}
setState("diagSettingsOpen" ,true);
console.log(state);
setState("diagSettingsClose" ,true);
I have this load-more listener on a button that calls the functions and it works fine.
let moviesPage = 1;
let seriesPage = 1;
document.getElementById('load-more').addEventListener('click', () => {
if (document.querySelector('#movies.active-link')) {
moviesPage++;
getMovies(moviesPage);
//getMovies(genreId, moviesPage);
} else if (document.querySelector('#series.active-link')) {
seriesPage++;
getSeries(seriesPage);
}
});
Now I have another listener on a list of links that calls the following code. It takes the genreId from the event parameter to sent as an argument to the api call. Also works fine so far.
document.querySelector('.dropdown-menu').addEventListener('click',
getByGenre);
function getByGenre (e) {
const genreId = e.target.dataset.genre;
movie.movieGenre(genreId)
.then(movieGenreRes => {
ui.printMovieByGenre(movieGenreRes);
})
.catch(err => console.log(err));
};
What I want to do is to call getByGenre from the load-more listener while passing also the moviesPage argument as you can see on the commented code so it can also be passed to the api call.
What would be the best way to do that? I've looked into .call() and .bind() but I'm not sure if it's the right direction to look at or even how to implement it in this situation.
Short Answer
Kludge: Global State
The simplest, though not the most elegant, way for you to solve this problem right now is by using some global state.
Take a global selection object that holds the selected genreId. Make sure you declare the object literal before using it anywhere.
So, your code might look something like so:
var selection = { };
document.querySelector('.dropdown-menu').addEventListener('click',
getByGenre);
function getByGenre (e) {
const genreId = e.target.dataset.genre;
selection.genreId = genreId;
movie.movieGenre(...);
};
...
let moviesPage = 1;
let seriesPage = 1;
document.getElementById('load-more').addEventListener('click', () => {
if (document.querySelector('#movies.active-link')) {
...
if (selection.genreId !== undefined) {
getMovies(selection.genreId, moviesPage);
}
} else if (...)) {
...
}
});
Closure
A more elegant way for you to accomplish this is by using a closure, but for that I have to know your code structure a bit more. For now, global state like the above will work for you.
Longer Answer
Your concerns have not been separated. You are mixing up more than one concern in your objects.
For e.g. to load more movies, in your load-more listener, you call a function named getMovies. However, from within the .dropdown-menu listener, you call into a movie object's method via the getByGenre method.
Ideally, you want to keep your UI concerns (such as selecting elements by using a query selector or reading data from elements) separate from your actual business objects. So, a more extensible model would have been like below:
var movies = {
get: function(howMany) {
if (howMany === undefined) {
howMany = defaultNumberOfMoviesToGetPerCall;
}
if (movies.genreId !== undefined) {
// get only those movies of the selected genre
} else {
// get all kinds of movies
}
},
genreId : undefined,
defaultNumberOfMoviesToGetPerCall: 25
};
document.get...('.load-more').addEventListener('whatever', (e) => {
var moviesArray = movies.get();
// do UI things with the moviesArray
});
document.get...('.dropdown-menu').addEventListener('whatever', (e) => {
movies.genreId = e.target.dataset.genreId;
var moviesArray = movies.get();
// do UI things with the moviesArray
});
What I'm trying to do is the following:
Check if a record with a filter criteria exists
If it does, do nothing
If it does not, create it with some default settings.
Now I could do it with 2 queries:
function ensureDocumentExists(connection, criteria, defaults) {
return r.table('tbl')
.filter(criteria)
.coerceTo('array') // please correct me if there's a better way
.run(connection)
.then(([record]) => {
if (record) {
return Promise.resolve() // Record exists, we are good
} else {
return r.table('tbl') // Record is not there we create it
.insert(defaults)
.run(connection)
}
})
}
But the fact that r.branch and r.replace exists, suggest me that this would be possible in a single run. Is it? I was thinking something like this:
function ensureDocumentExists(connection, criteria, defaults) {
return r.table('tbl')
.filter(criteria)
.replace(doc => r.branch(
r.exists(doc), // If doc exists (I'm just making this up)
doc, // Don't touch it
defaults // Else create defaults
)).run(connection)
}
But I'm not sure if replace is the right method for this, and also no idea how to check if the given row exists.
Figured it out:
function ensureDocumentExists(connection, criteria, defaults) {
return r.table('tbl')
.filter(criteria)
.isEmpty() // isEmpty for the rescue
.do(empty => r.branch(
empty, // equivalent of if(empty)
r.table('tbl').insert(defaults), // insert defaults
null // else return whatever
).run(connection)
})
}