Issue Where Array is Limited to One Item At Any Given Time - javascript

I am running into an issue in a rather simple function that builds an array - and I'm drawing a blank on what the issue is. It's likely something simple - but I'm just not seeing it.
Here's the function:
private onSelection(selection)
{
if (selection)
{
const selectionsArray = [];
selectionsArray.push(selection);
console.log(selectionsArray);
console.log(selectionsArray.length);
return selectionsArray;
}
}
And the "selection" is being passed via a checkbox, like so:
<md-checkbox (click)="onSelection('A')">A</md-checkbox>
<md-checkbox (click)="onSelection('B')">B</md-checkbox>
<md-checkbox (click)="onSelection('C')">C</md-checkbox>
Right now regardless of what's clicked on in the checkboxes, my array is always just one item, and this is confirmed when I console out the length - which is always 1. What am I missing here? Why won't the array build into 2 and 3 items as multiple selections are pushed?

Your onSelection handler always reset the existing array before adding
the selected element. so you need to initialize your array outside of
your onSelection function.
suggestion:-
you can remove the if (selection) condition cause in your existing code, it'll always be true but you should check if a selected element is already in your selectionsArray before adding and in this way you won't get any duplicate entry in your selectionsArray. btw you should remove the const declaration of your selectionsArray, it doesn't make any sense.
const selectionsArray = [];
private onSelection(selection)
{
if (selection)
{
selectionsArray.push(selection);
console.log(selectionsArray);
console.log(selectionsArray.length);
return selectionsArray;
}
}

Related

Is My Approach to the Todo App Delete Function Wrong?

I am learning React and just created a simple todo app using only React. My todo app has the standard structure of having a text input and an "ADD" button next to it. The user would type their todo in the input and every time they click on the "ADD" button next to it, a new ordered list of their inputs would appear underneath the input and "ADD" button.
The user can also delete a todo entry by clicking on the entries individually, like this:
To accomplish this behaviour of deleting entries, I used this delete function:
delete(elem) {
for (var i = 0; i < this.state.listArray.length; i++) {
if (this.state.listArray[i] === elem) {
this.state.listArray.splice(i, 1);
this.setState({
listArray: this.state.listArray
});
break;
}
}
}
My todo app works exactly the way that I want it to work, but as I look at other people's more conventional approach to this delete function, they either just simply use the splice method or the filter method.
For the splice method approach, they apparently just simply "remove" the unwanted entry from the listArray when the user clicks the particular entry. This does not work for me as using this method results in all my entries getting deleted except for the entry that I clicked on, which is the one that I want to delete.
On the other hand, the filter method approach apparently works by comparing the elem, which is the data passed from a child component, with each element in the listArray, and if the element in the for loop does not equal to the elem, then it would be passed onto a new array. This new array would be the one to not be deleted. This approach works better than the simple splice approach, however, one problem that I had encountered with this approach is that if I have more than one entry of the same value, for example, "Feed the dog". I only want one of the "Feed the dog" entries to be deleted, but it deletes both of them.
I thought of an approach to tackle this problem, eventually coming up with the current version of my code, which uses the splice method, but the splice method is used before I set it in the state. As evident here:
this.state.listArray.splice(i, 1);
this.setState({
listArray: this.state.listArray
});
My question can be broken down into three subquestions:
Considering that React states should be immutable, is the first line of the code above mutating my state? Is this approach not okay?
I thought that all React states were only possible to be changed inside a "setState" function, but my first line of code from above is not inside a setState function, yet it changed the state of listArray. How is this possible?
If my approach is mutating the state and is not ideal, how would you go about making the delete function so that it only deletes one entry and not more than one if there are multiple similar entries?
Yes, splice affects the array it acts on so don't use in this way. Instead you need to create a new array of the correct elements:
this.setState({
listArray: this.state.listArray.filter((el, idx) => idx !== i);
});
If you want to remove only the first instance, maybe couple with a findIndex (although indexOf would work in your example as well) first:
delete(elem) {
const idxToFilter = this.state.listArray.findIndex(el => el === elem);
if (idxToFilter < 0) {
return;
}
this.setState({
listArray: this.state.listArray.filter((el, idx) => idx !== idxToFilter);
});
}
This creates a new array without modifying the old which will cause anything that reacts to listArray changing to be notified since the reference has changed.

javascript remove previous filter on an object before applying new one

I have a series of buttons that apply filters to an object using a function similar to this:
isNew(type) {
//need to reset filter first
this.results = this.results.filter(function(type) {
return type.has_user_viewed === true
})
console.log('isNew');
}
The problem I have is if one filter is applied by the user clicking, and then another filter applied, the filtered array is filtered again. What I need to do with the above is reset the object to it's original state before applying a new filter. Not sure how to "reset" a filter here?
Just save the unfiltered data to a variable. filter creates a new array object (shallow copying elements) each time, so your code overwrites the original data with the filtered data:
this.data = [...whatever the source is]
isNew(type) {
//need to reset filter first
this.results = this.data.filter(function(type) {
return type.has_user_viewed === true
})
console.log('isNew');
}
You'll probably need to store the result of the filter somewhere other than this.results if you want to access the original list of results for additional filtering later. Note that this change will likely force you to change other code.
An example of what I'm recommending:
this.filteredResults = this.results.filter(function(type) {
return type.has_user_viewed === true
})
I would suggest that you google immutability, as understanding it will help you greatly when you need to solve similar problems in the future.

How to get checkbox value from localStorage

There is a page with a lot of different checkbox questions which then get submitted and populate the next page, this page however gets refreshed and the already annoyed potential client needs to go back and fill out the form again.
Now I have localstorage set up so he doesn't need to reselect all the checkbox again, he just needs to resubmit the form and his back in action.
How does one keep the values populated on the problem page so this fella doesn't have to go back to resubmit?
//SIZE SAVE
function save() {
localStorage.setItem('100', checkbox.checked);
var checkbox = document.getElementById('100');
localStorage.setItem('200', checkbox.checked);
var checkbox = document.getElementById('200');
//SIZE LOAD
function load() {
var checked = JSON.parse(localStorage.getItem('100'));
document.getElementById("100").checked = checked;
var checked = JSON.parse(localStorage.getItem('200'));
document.getElementById("200").checked = checked;
//THIS PAGE NEEDS THE CHECKMARK
echo get_site_url().
"/the/checkmark/selected/was/".$_POST['check_group'].
"/.png";
}
I think is much simple for now and especially for the feature if you write some code to make the management for all checkboxes form your form.
First of all it will be best if you group all your checkboxes into a single place.
Into a function like this you can declare all your checkbox selectors you want to save into the localStoarge (now you don't need to make variables for each selector into multiple places into your code)
function getCheckboxItems() {
return ['100', '200']
.map(function(selector) {
return {
selector: selector,
element: document.getElementById(selector)
}`enter code here`
});
}
Then to make things much simpler you can store all the values from the checkbox into a single object instead of save the result in multiple keys, in this way is much simpler to make management (let's say you want to erase all values or to update only a part)
The following function will take as argument all checkbox items from the function above, the point is the function above will return an array with the checkbox id and the checkbox element, than you just reduce all that array into this function into an single object containing all the ids and values, after this you just store the object into the localStorage
function serializeCheckboxes(elements) {
var container = elements.reduce(function (accumulator, item) {
accumulator[item.selector] = item.element.checked;
return accumulator;
}, {})
localStorage.setItem('container', JSON.stringify(container));
}
function save() {
var elements = getCheckboxItems();
serializeCheckboxes(elements);
}
After this you need another function who will read all the values from the localStorge and place them into your checkbox "checked" state
function readCheckboxes() {
var storage = localStorage.getItem('container'), //Your key
container = (storage) ? JSON.parse(storage) : {};
Object.keys(container).forEach(function(key) {
var element = document.getElementById(key);
if(element) {
element.checked = container[key];
}
});
}
This is just a simple service who can manage your problem but I think, for any additional changes you can customize this solution much simpler instead of keeping all into multiple variables, also if you add more checkbox elements into your application with this solution you just add the corresponding id into the array from the first function.
A live example here:
https://jsbin.com/xejibihiso/edit?html,js,output
localStorage has two main functions, getItem and setItem. For setItem you pass in a key and a value. If you write to that key again, it will rewrite that value. So in your case, if a box is checked you would do
localStorage.setItem("checkbox_value", true)
and when it is unchecked you would pass in false instead. To get the value you can look at using jQuery like so:
$(checkbox).is(':checked')
and use a simple if-else clause to pass in true or false. then when you reload your page, on $(document).ready() you can get the values using
localStorage.getItem(key)
and use JavaScript to set the check boxes values.
localStorage only allows you to store strings. What you can do is use a loop to create a string that has all the check boxes values separated by some delimiter. So, for example, if there are four check boxes with values true false false true your string would be "true\nfalse\nfalse\ntrue" where \n is the delimiter. then you can store that string in localStorage and when you retrieve it you can put all the values into an array like so:
array = localStorage.getItem(key).split('\n').
Then you can populate your check boxes with that newly retrieved array. Ask if anything needs clarification.

ES6 class getter, temporary return or alternative solution

I am trying to solve a problem I am seeing when rendering a list of items in my ui that is coming out of a es6 class I have created. The model is working great, however I am using animations that are listening to (in react) mount, onEnter, and onLeave of the items.
When I apply my filters and sorting via the model and spit back the new list of items via the getter, the animations do not apply to some items because the list is just being re sorted, not necessarily changed.
So my getter just grabs this.products of the class and returns it and applies a sort order to it. And if filters are applied (which are tracked by this._checkedList in the class), the this.products is reduced based on which filters are selected then sorted. So that getter looks like so :
get productList() {
if (this._checkedList.length > 0) {
const filteredProducts = _.reduce(this.filterMap, reduceFilters, []);
const deDuped = _.uniq(filteredProducts, 'id');
return this.applySort(deDuped);
}
const deDuped = _.uniq(this.products, 'id');
return this.applySort(deDuped);
}
What I am trying to figure out, is a way to to temporarily send back an empty array while the filters or sorting run. The reason being the ui would receive an empty array (even if for a split second) and react would register the new sorted/filtered list as a new list and fire the enter/leave/mount animations again.
My attempt was to set a local property of the class like -
this._tempReturn = false;
then in the functions where the sort or filter happen, I set it to true, then back to false when the function is done like this -
toggleFilter(args) {
this._tempReturn = true;
...toggle logic
this._tempReturn = false;
}
Then changed the getter to check for that property before i do anything else, and if it's true, send back an empty array -
get productList() {
if (this._tempReturn) {
return [];
}
...
}
However, this does not seem to work. Even putting a console.log in the if (this._tempReturn) { didn't show any logs.
I also tried sending back a new list with lodash's _.cloneDeep like so :
get productList() {
if (this._checkedList.length > 0) {
const filteredProducts = _.reduce(this.filterMap, reduceFilters, []);
const deDuped = _.uniq(filteredProducts, 'id');
return _.cloneDeep(this.applySort(deDuped));
}
const deDuped = _.uniq(this.products, 'id');
return _.cloneDeep(this.applySort(deDuped));
}
this did not work either. So it seems the empty array return might be a better approach.
I am wondering if there is some way to achieve this - I would like to have the array be return empty for a second perhaps while the filters and sort are applying.
Very stuck on how to achieve, perhaps I am even looking at this problem from the wrong angle and there is a much better way to solve this. Any advice would be welcomed, thanks for reading!
In order to force a re-render of items in a list when updating them you just need to make sure that each items has a unique key property.
Instead of rendering the list, then rendering it as empty, then re-rendering a changed list make sure each child has a unique key. Changing the key property on a child in an array will always cause it to re-render.

IN CQ, how to set value of all the items in Panel to blank

In ExtJS panel I need to set value of all items (e.g. textfield, pathfield) to blank. I don't want to set value of each individual item to blank but of whole panel in one go.
I am able to get list of items
function getAllChildren (panel) {
/*Get children of passed panel or an empty array if it doesn't have thems.*/
var children = panel.items ? panel.items.items : [];
/*For each child get their children and concatenate to result.*/
CQ.Ext.each(children, function (child) {
children = children.concat(getAllChildren(child));
});
return children;
}
but how to set to blank for whole panel? Please suggest what need to be done in this case.
Actually, it's not possible to do it with one liner - all at the same time. What your method returns is purely an array of objects. In fact if such syntax existed, it would iterate over all fields anyway.
Though clearing all fields, having the method you've proposed is very trivial to do. Just iterate over them all and call reset method. Mind some (especially custom) widgets might not handle it.
var fields = getAllChildren(panel);
CQ.Ext.each(fields, function(field) {
if (child.reset) {
child.reset();
}
});
You've got similar loop in your getAllChildren code - you might reset field at the same place.
The method is defined in Field type which is usually a supertype of each dialog widget. You can read more here.

Categories