Angular Array not updating after maping function - javascript

I have an array of Categories that I filter when a user click on a button that selects a specific categories to view. however after maping the categories array, the categories display the desired result in the console however it seems to get lost somehow and the categories dont update in the DOM?
ngOnInit() {
this.initCategories();
this.shopService.filterCategories.subscribe(
(fCategory: string) => {
const filteredCategories = this.categories.filter(category => {
return category.name !== fCategory;
});
for (const obj of filteredCategories) {
obj.checked = false;
}
const newCategories = [];
this.categories.map(obj => {
filteredCategories.filter(fCat => obj);
newCategories.push(obj);
});
this.categories = newCategories;
console.log(this.categories)
}
);
}
initCategories(){
this.categories = [
{name: 'dress', checked: true, displayName: 'Dresses'},
{name: 'top', checked: true, displayName: 'Shirts'},
{name: 'skirt', checked: true, displayName: 'Skirts/Pants'},
{name: 'purse', checked: true, displayName: 'Purse'},
{name: 'bag', checked: true, displayName: 'Bags'},
];
}
result
[{…}, {…}, {…}, {…}, {…}]
0: {name: "dress", checked: true, displayName: "Dresses"}
1: {name: "top", checked: false, displayName: "Shirts"}
2: {name: "skirt", checked: false, displayName: "Skirts/Pants"}
3: {name: "purse", checked: false, displayName: "Purse"}
4: {name: "bag", checked: false, displayName: "Bags"}
however when I log the categories array in ngAfterViewInit
I get this.
[{…}, {…}, {…}, {…}, {…}]
0: {name: "dress", checked: true, displayName: "Dresses"}
1: {name: "top", checked: true, displayName: "Shirts"}
2: {name: "skirt", checked: true, displayName: "Skirts/Pants"}
3: {name: "purse", checked: true, displayName: "Purse"}
4: {name: "bag", checked: true, displayName: "Bags"}
what I tried
this.shopService.filterCategories.subscribe(
(fCategory: string) => {
const filteredCategories = this.categories.filter(category => {
return category.name !== fCategory;
});
for (const obj of filteredCategories) {
obj.checked = false;
}
let newCategories;
newCategories = [...this.categories.map(obj => {
filteredCategories.filter(fCat => obj);
})];
this.categories = newCategories;
console.log(this.categories)
}
);
}

I think you need to play with
this.categories.map(obj => {
filteredCategories.filter(fCat => obj);
both of them return a new array, they don't touch current one.
Therefore I would assume that filteredCategories.filter at least should be assigned somewhere.
// an empty array
const newCategories = [];
// starting a loop, forEach would fit here better because it doesn't return anything.
this.categories.map(obj => {
// the result of this filter won't be assigned anywhere.
filteredCategories.filter(fCat => obj);
// pushing obj to newCategories for every iteration.
// may be you need to wrap it with `if` based on filter result.
newCategories.push(obj);
});
// newCategories is an array with the same items as this.categories.
// because we simply iterate without any conditions.
console.log(newCategories);
In the update part of your question filter still doesn't do anything.
Its result should be assigned or used in a condition.
newCategories = [...this.categories.map(obj => {
filteredCategories.filter(fCat => obj); // <- should be assigned
})];
if you want to add only filtered only active category.
ngOnInit() {
this.initCategories();
this.shopService.filterCategories.subscribe(
(fCategory: string) => {
const filteredCategories: FilterBarComponent['categories'] = [];
for (const category of this.categories) {
filteredCategories.push({
...category,
checked: category.name === fCategory,
});
}
this.categories = filteredCategories;
this.updateCategories();
}
);
}

Related

Merge arrays of objects by condition

Let's say I have array of objects - original items
originalItems = [
{name:"aaa", expanded: false, visible: true},
{name:"bbb", expanded: false, visible: true},
{name:"ccc", expanded: false, visible: true}
]
then I do some changes on items
currentItems = [
{name:"aaa", expanded: true, visible: false},
{name:"bbb", expanded: false, visible: true},
{name:"ccc", expanded: true, visible: true}
]
Now I need to merge those two arrays,
BUT: in the merged arrays (result), I need that expanded value will be from currentItems, and visible will be from originalItems
something like this
result = [
{name:"aaa", expanded: true, visible: true},
{name:"bbb", expanded: false, visible: true},
{name:"ccc", expanded: true, visible: true}
]
Is there some elegant way to achieve it? Or just go through properties?
You can use .map() on one of the arrays, for example the original one, and transform each object into a new object by spreading the properties of the original object (...) into a new object. You can then set the expanded property to take the value from its corresponding object in the currentItems via the index (i). See example below:
const originalItems = [ {name:"aaa", expanded: false, visible: true}, {name:"bbb", expanded: false, visible: true}, {name:"ccc", expanded: false, visible: true} ];
const currentItems = [ {name:"aaa", expanded: true, visible: false}, {name:"bbb", expanded: false, visible: true}, {name:"ccc", expanded: true, visible: true} ];
const res = originalItems.map((obj, i) => ({
...obj,
expanded: currentItems[i].expanded
}));
console.log(res);
I'd sort and manage arrays like this, for simplicity as well speed. Perhaps more on par with what you were looking to do.
let Items = [
{ name: "aaa", expanded: false, visible: true },
{ name: "bbb", expanded: false, visible: true },
{ name: "ccc", expanded: false, visible: true }
]
function doExpand(name, val) {
let item = getItem(name);
item.expanded = val;
}
function doVisible(name, val) {
let item = getItem(name);
item.visible = val;
}
function addItem(name) {
if (!getItem(name)) {
Items.push({ name: name, expanded: false, visible: false });
} else {
console.log("duplicates found");
}
}
function getItem(name) {
for (let i = 0; i < Items.length; i++) {
if (Items[i].name === name) return Items[i];
}
return false;
}
/* TESTING TIME */
doExpand("aaa", true);
doExpand("aaa", false);
let Item = getItem("bbb");
Item.name = "renamed";
addItem("dogsnacks");
addItem("bacon");
addItem("programming");
addItem("programming");
addItem("programming");
addItem("programming");
addItem("programming");
console.log(Items);
Since you're taking the elements from two different arrays of the same length to create a third, you can create a new array, then loop through the number of objects in the array, constructing your result object based on the desired source list.
let result = [];
for (i = 0; i < originalItems.length; i++) {
let newObj = {
name: originalItems[i].name,
expanded: currentItems[i].expanded,
visible: originalItems[i].visible,
};
result.push(newObj);
}

Use switch case and filter/find/loop through array of objects to filter and find the correct object to update JavaScript

What is the best way for me to filter through the below columns array of objects, select the correct object using my switch method, and set the show property to false (JavaScript)? This is the first time implementing something like this, and I am a a bit uncertain how to tackle this.
Example: if I pass 'Surname' to my showHideSelectedColumn(column) method, it filters the columns array, finds the {name: 'Surname', show: true} object, and sets this specific object's show property to false.
columns = [{name: 'Surname', show: true}, {name: 'Name', show: false}, {name: 'Gender', show: true}, {name: 'Date of Birth', show: false}, {name: 'ID/Passport Number', show: true}, {name: 'Membership Status', show: true}];
showHideSelectedColumn(column) {
switch (column) {
case 'Surname':
// set show = false
break;
case 'Name':
// set show = false
break;
case 'Gender':
// set show = false
break;
case 'Date of Birth':
// set show = false;
break;
case 'ID/Passport Number':
// set show = false
break;
}
Aternative and generic solution using .find method . You need to pass the array and the column name to the method , it will return you the updated array setting show property of that column to be false
var columns = [{name: 'Surname', show: true}, {name: 'Name', show: false}, {name: 'Gender', show: true}, {name: 'Date of Birth', show: false}, {name: 'ID/Passport Number', show: true}, {name: 'Membership Status', show: true}];
function setShow(array, column){
var found = array.find(obj => obj.name===column);
if(found){
found.show = false;
}
return array;
}
console.log(setShow(columns,"Surname"))
Hi you can map your array and if there is match you can toggle it by following example
Array.prototype.toggleProperty = function (property){
return this.map(el=>{
if(el.name===property){
return {...el,show:!el.show}
}
else{
return el
}
})
}
// test
console.log(columns.toggleProperty("Name"))

Counting true occurences in array and binding them to a variable in angular

I have a set of input fields with checkboxes where the user can choose an option. Then it's saved in local storage as true if checked and false if not. The localStorage under key "passengers" looks like this:
0: {name: "", child: false, luggage: true}
1: {name: "", child: true, luggage: false}
2: {name: "", child: false, luggage: true}
3: {name: "", child: true, luggage: false}
I want to count occurrences of true and store as a number in a variable
public luggageCounter: number;
I was trying to use
this.luggageCounter = countTrue([this.passengers[i].luggage]], true)
console.log(this.luggageCounter)
and const countTrue = (arr, val) => arr.reduce((a, v) => (v === val ? a +1 : a), 0)
but both of these solutions don't work. I was also thinking about making it easier and toggling the value from HTML by adding code below with [ngClass] and binding it with method in component.ts but also with no success.
<div>Extra luggage: {{ passengers[i].luggage ? 'Yes' : 'No' }}<div>
Any ideas how to make this simple task work? I'm out of ideas :)
Please try the following solution
const data = [
{ name: "", child: false, luggage: true },
{ name: "", child: true, luggage: false },
{ name: "", child: false, luggage: true },
{ name: "", child: true, luggage: false },
];
const total = data.reduce((previousValue, currentValue) => {
if (currentValue.luggage) {
previousValue += 1;
}
return previousValue;
}, 0);
console.log(total);
See
Array.prototype.reduce()

Add object to nested array

I have initial state as
sites = [
{id, name, vehicles[], drivers[]},
{id, name, vehicles[], drivers[]},
{id, name, vehicles[], drivers[]},
{id, name, vehicles[], drivers[]},
];
I'm trying to add a vehicle to a given site when selected from a list which is in a component SiteVehcleSelection and the method that handles the selection is:
handleVehicleSelection = (event) => {
const vehicle = this.props.vehicles.find((v) => v.id === parseInt(event.target.dataset.id, 10));
this.props.handleVehicleSelection(event, this.state.site.id, {...vehicle});
};
which passes it up to parent SiteList method:
handleVehicleSelection = (event, siteId, vehicle) => {
this.props.dispatch(siteActions.handleVehicleSelect(siteId, vehicle), event.target.checked);
}
called from the SiteList class:
export function handleVehicleSelect(siteId, vehicle, cmd){
return (dispatch) => {
debugger;
return fetch(`${BASE_URL}/accounts/site-vehicle-action/${siteId}/${vehicle.id}/${cmd}`, {
method: 'PUT',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: ''
}).then((res) => {
return res.json();
}).then((json) => {
if (json.msg === true) {
dispatch(vehicleSelect(siteId, vehicle));
}
});
}
}
which dispatches to this:
export function vehicleSelect(siteId, vehicle){
return {type: actionTypes.ADD_VEHICLE_TO_SITE, siteId, vehicle};
}
and my reducer is:
case actionTypes.ADD_VEHICLE_TO_SITE:
debugger;
const siteIndex = state.findIndex((site) => site.id === action.siteId);
console.log(state);
const newState = [...state.slice(0, siteIndex), {...state[siteIndex], vehicles: [...state[siteIndex].vehicles, action.vehicle],}, ...state.slice(siteIndex +1)];
console.log(newState);
return newState;
when I log before and after the changes have taken place, the vehicle has been added to the correct site but it does not show/refresh in the view here is the logging of the state before and after.
Before change :
0: {drivers: Array(0), id: 1, name: "Site One", vehicles: Array(0)}
1: {drivers: Array(0), id: 2, name: "Site Two", vehicles: Array(0)}
2: {drivers: Array(0), id: 3, name: "Site Three", vehicles: Array(0)}
length: 3
__proto__: Array(0)
After change:
0: {drivers: Array(0), id: 1, name: "Site One", vehicles: Array(1)}
1: {drivers: Array(0), id: 2, name: "Site Two", vehicles: Array(0)}
2: {drivers: Array(0), id: 3, name: "Site Three", vehicles: Array(0)}
length: 3
__proto__: Array(0)
can see the first one had the vehicle added correctly and this is the new state but nothing happens on return as if sitesList does not refresh.
Hope this edit helps explain more.
I think below code will shed some light. I assume you have the corresponding indexes.
let state = [
{ id: "id1", name: "name1", items: [{},{}] },
{ id: "id2", name: "name2", items: [{},{}] },
]
function reducer() {
switch("Action") {
case ADD_SITE: { // add new element to state
return [
...state,
payload.site,
]
}
case ADD_SITE_AT_INDEX: { // add new element to state at index: idx
return [
...state.slice(0, idx),
payload.newSite,
...state.slice(idx)
]
}
case ADD_ITEM: { // add new item to site with index: idx
return [
...state.slice(0, idx),
{
...state[idx],
items: [
...state[idx].items,
payload.newItem
],
},
...state.slice(idx+1)
]
}
case ADD_ITEM_AT_INDEX: { // add new item to site with index: idx, at item index: item_idx
return [
...state.slice(0, idx),
{
...state[idx],
items: [
...state[idx].items.slice(0, item_idx),
payload.newItem,
...state[idx].items.slice(item_idx),
],
},
...state.slice(idx+1)
]
}
}
}
let say this is your structure of state with keys (i am giving random name to the keys so i can explain)
{
data:[{id:"some_id",name:"some_name",items1:[{},{}]}]
}
//suppose this is your reducer code.
case ADD_ITEM:
return{
...state,
data: state.data.map(val=>{
if(val.Id===action.payload.Id){
return{
...val,
items1:[...val.items1,action.payload.data]
}
}
return{...val}
})
}
here you'll be sending Id and data from the action like:
{type:"ADD_ITEM",payload:{Id,data}}
where id will be the id of first level object which's array needs to be updated,
and data will be the data you want to add into the array..
If you just want to add an object to an array with a given structure, you can do this:
Structure
[
{ id, name, items1[{object},{object}]
]
copy an existing state and then add a new object to the end of the array.
return {
...state,
items1: state.items1.concat(action.newObject),
};
or with ES6 spread
return {
...state,
items1: [...state.items1, action.newObject],
};
// state: { id, name, items1: [{object},{object},{newObject}] }

Setstate of specific object in array

I have a cats array that I pull from an api
i map over these and render them on a page
each one gets rendered with a like button, when I hit like I want it to like it and when i hit like again, it should unlike it
my initialState is :
state = {
cats: []
};
then once i call the api state looks like this:
cats: [
{url: Array(1), id: Array(1), source_url: Array(1), liked: false}
{url: Array(1), id: Array(1), source_url: Array(1), liked: false}
]
I have a like cat method whereby I find the cat that I liked like this:
var cat = this.state.cats.find(c => c.id[0] === cat.id[0])
considering I have all this information, how do I call setState for that specific cat to change the liked from false to true?
I was thinking something like this:
this.setState(prevState => ({ cats: {
cat: {
...prevState.cat,
liked: !prevState.cat.liked
}
}}))
but it does not know what liked is of undefined
any ideas?
One problem with your approach is that there's no prevState.cat.
Assuming the (un)liked cat is stored in cat:
this.setState(prevState => ({
cats: prevState.cats.map(c => c.id[0] === cat.id[0] ? Object.assign(c, { liked: !c.liked }) : c)
}));
Demo:
var state;
function setState(a) {
state = Object.assign(state, a(state));
}
state = {
cats: [
{url: [0], id: [1], source_url: [0], liked: false},
{url: [0], id: [2], source_url: [0], liked: false}
]
};
var cat = state.cats[1];
setState(prevState => ({
cats: prevState.cats.map(c => c.id[0] === cat.id[0] ? Object.assign(c, { liked: !c.liked }) : c)
}));
console.log(state.cats[1].liked);

Categories