In angular Pushing objects in array getting duplicated - arrays

EventEmitter in service
toshoppinglist = new EventEmitter<Ingredients[]>()
Emitting method
toshoppinglist() {
this.slservice.toshoppinglist.emit(this.item.ingredients);
}
ingredients : Ingredient []
Subscribing to emit and pushing emitted values
this.slservice.toshoppinglist.subscribe(
(ingredients: Ingredients[]) => {
for (let item of ingredients) {
this.ingredients.push(item);
}
}
)
Now, when pushing new values into the array,it's getting duplicated.It's work fine for first pushing,but getting duplicated after that.

Ok, first in my opinion you use wrongly EventEmitter. Eventemitters used only inside DUMB components to raise an event to Smart component. And not inside services. Second, yes it will be duplicated. Imagine you have button which will raise this eventemitter and we emit the same ingredient every time. Inside the subscribe you didnt check that new ingredient are different. And because you use list, it doesnt care if you have duplicates. So a solution is to add to the subscribe a check that it will push only - new- ingredients.

You need to add a check in your subscription function.
currentIngredients: Ingredients[];
this.slservice.toshoppinglist.subscribe(
(ingredients: Ingredients[]) => {
> //here you need the if condition comparing it to a current
> ingredients array to see if it exists or not
for(let item of ingredients) {
if(this.currentIngredients.includes(item) {
// in this case you should increment the count of ingredient
currentIngredients[item].count++;
}
else {
// this should only add non-existing items ingredients list
this.currentIngredients.push(item)
}
}
}

Related

Showing the new row in react-table on the current page

I have been playing with ReactTable v7 for a while and have encountered the following problem: when the table is sorted and uses paginator sometimes adding (or editing) a row causes it to be outside the current page.
You can see the problem here:
https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/material-UI-kitchen-sink
Sort the table by First Name
Press add
Enter a record with First Name "zzzzz"
The record is added but is currently hidden which confuses users.
Is there a "standard" way to fix the issue? Am I missing something?
In v6 I have done a workaround for it:
React.useEffect(() => {
if (editedElementId && reactTable) {
const { data } = reactTable.props;
if (data && data.length > 0) {
const internal = reactTable.getResolvedState();
let position = -1;
internal.sortedData.forEach((row, i) => {
if (row._original.id === editedElementId) position = i;
});
if (position >= 0) {
const pageNumber = Math.floor(position / pageSize);
setPage(pageNumber);
} else {
alert.info("Element not visible");
}
}
}
}, [editedElementId]);
...
<ReactTable
ref={(r) => {setReactTable(r);}}
...
But maybe there is a bulit-in way to achieve it?
There is not currently a way to only sort the elements which are currently being displayed, no.
React Table v7's useSortBy hook sorts the entirety of the input data array, so sorting by First Name descending (A->Z) naturally places 'ZZZZZZ' at the end of the list, which will be hidden due to pagination. The v7 way of doing it would probably be similar to what you are doing already, using the exposed properties from the useTable hook instead of reactTable.resolvedState() and etc. Another way to do it would be to write your own custom sortBy method and only sort the items [0...n] where n is the number of currently displayed items.
As a side note, since the autoResetSortBy property is true by default in the example you linked, the step of sorting on First Name is irrelevant -- since the function which adds a new user to the list mutates the data array, the sorting method is reset. That function appends the new user to the end of the list, so it will always be on a new page, even if the "Rows per page" option is set to "All". Both issues can be fixed by setting autoResetSortBy to false, and changing the pageSize in addUserHandler.

React.js - TODO List striking through task text and sorting not working

I am having a hard time with a React.js project. I have a TODO list and there is a function to mark a task as done. I have a button next to each and every task and if I click on it the following function will be called.
crossLine(key){
const currentTaskArray = [...this.state.task]
const deletedItems = currentTaskArray.filter(deletedItem => deletedItem.key == key);
const taskAfterDeleted = currentTaskArray.filter(deletedTask => deletedTask.key !== key);
this.setState({
task: [taskAfterDeleted,deletedItems]
})
};
I first need to strike this item ( just html strikethrough) so that I know it is done. After that, it should be removed from the array and should be moved to the bottom of the list and the rest of the items should be moved accordingly. The following function does not seem to contain anything inside deletedItems.
you can find the full project at https://codesandbox.io/s/jovial-gauss-s12bf.
.filter() returns an array of the remaining elements. Therefore, taskAfterDeleted and deletedItems are both arrays. You'll need to spread them in your new state:
[...taskAfterDeleted, ...deletedItems]

Ordering time-stamped date in react redux?

I am struggling to find a good way to organise my state in Redux with React. It is a simple Blog with posts. I am getting an array from the api that is already ordered from oldest to newest. Since the app is a bit more complicated, I'd like to store the state in an object with the uuid as keys, so I can access it easily.
{ 
uuid_post1: { ...post1 },
uuid_post2: { ...post2 },
...
}
With the format above it is easy for me to sync the state between API and React without refetching data all the time. But I do need to display the output in an ordered form from newest to oldest.
Is there an easy way to solve this and keep the date info in the object? Is there another good way to organise state for this use case?
Thanks.
In your reducer you'll want to index the posts by ID and also save the sorted IDs. This will let you look up the posts efficiently, and also maintain a list of them in the order that you received them (oldest to newest). You can get them in the reverse order using a selector.
switch (action.type) {
case 'POSTS_RECEIVED':
return {
...state,
orderedPostIDs: posts.map(p => p.id),
postsById: posts.reduce((acc, post) => {
acc[post.id] = post;
}, {});
}
}
With this orderedPostIDs is an array of Post IDs and postsById is an object where the keys are Post IDs and the values are the posts.
function getPostByID(state, postId) {
return state.posts.postsById[id];
}
// Should use reselect here because it's returning a new array with every call
// oldest to newest - post are received from API in this order
function getPostsSortedByDateAscending(state) {
return state.posts.orderedPostIDs.map(id => getPostByID(state, id));
}
// Should use reselect here because it's returning a new array with every call
// newest to oldest
function getPostsSortedByDateDescending(state) {
// copy to new array, because Array.reverse mutates the value
return [].concat(getPostsSortedByDateAscending(state)).reverse();
}
Another approach which makes your state simpler is to only store postsById, as Max commented below. To get the sorted posts, you'll use Object.values() and then sort them as needed.
// oldest to newest - need to use reselect here
function getPostsSortedByDateAscending(state) {
return _.sortBy(Object.values(state.posts.postsById), p => p.date)
}
// oldest to newest - need to use reselect here
function getPostsSortedByDateDescending(state) {
// copy to new array, because Array.reverse mutates the value
return [].concat(getPostsSortedByDateAscending(state)).reverse();
}

Sort an array of objects based on another array of objects in angular 7

I know it's been asked million+1 times. But i've found no help in those questions/answers.
I have 2 arrays of 2 different objects one string property is used to uniquely identify them. This would be the key to sort about, but said object prop names are not equal (accessValue, modifiedOption). But their values are!
Object1: { ... accessValue, ... };
Object2: { ..., modifiedOption, ... };
array1:Object1[];
array2:Object2[];
I'd like to sort array1 based on the object indencies of array2.
So all of array1 items'd be in the same order as array2.
These two arrays are used to model a connected dropdown selection system, which can be added to are removed from. The Addition is screwing me over (lastly added item is appended to the first place and not the last) probably because of filter below?
What I use to add new dropdowns:
addFieldRow() {
this.fieldRows.push(0); // since desired selection is not known yet but need to populate the array that represents the 1st selection so a 2nd main selection dropdown will appear on screen
...
}
public onSelect() {
// if a selection is happened check the values of editOptions (contains data about all main selectable options)
this.fieldRows = this.editOptions.filter(
option => this.selectedOptions.some(el => el.modifiedOption === option.accessValue)
);
this.disableSelected(); // disable already selected items (not related to my issue)
this.optionSelected = true; // this is just for button disabled toggle
}
So either i need to figure out my addRow logic (if it has any flaws) or implement a sorting utility to make sure that the objects of fieldRows are in the same order as selectedOptions' -> since this models the selection directly.
I cannot really add a stackblitz since it's hard to model my current state.
Okay I am a complete idiot!
Since I know the current index (since i am looping through fieldRows).
All I had to do is replace this:
public onSelect() {
this.fieldRows = this.editOptions.filter(
option => this.selectedOptions.some(el => el.modifiedOption === option.accessValue)
);
With this:
public onSelect(index) {
this.fieldRows[index] = this.editOptions.find(option => this.selectedOptions[index].modifiedOption === option.accessValue);
this.disableSelected();
this.optionSelected = true;
}
Now it works correctly.

AngularFire - manage child_added and other events on $asArray

Hello guys !
I wanted to do something pretty simple : display the last 10 posts with the newer at the top, and when someone posts a new one, display a bar "click here to see x new posts" that when clicked displays the new ones.
My problem : when a new posts enters Firebase, it immediately displays on screen (using the ng-repeat on the array of the scope linked to the array from Firebase), and takes the older of the 10 elements out.
$firebase(ref.limitToLast(10)).$asArray().$loaded().then(function(messagesData) { ... }
I can detect the change using
messagesData.$watch(function(data) {
console.log("data changed!", data, messagesData);
if(data.event == "child_added") {
// work here
}
});
But I can't figure out how to do what I'm trying to, nor did I find it in the doc. Thanks for any help !
Okay, so, there is a solution. It's possible not to have everything in sync while still enjoying the use of AngularFire for the current elements. Here is how.
Start by getting the messages you want to display at the beginning :
$firebase(ref.orderByKey().limitToLast(nbMessages)).$asArray().$loaded().then(function(messagesData) { ......
Then link them to another array one by one using foreach :
angular.forEach(messagesData, function(value, key) {
messagesDataReturn[key] = value;
// get any other data you need for that message (user name, etc...
});
This will keep every element iterated in sync. So if there is something like a counter of likes, they will be updated live.
Finally add a watcher :
messagesData.$watch(function(data) {
if(data.event == "child_added") {
var newLine = messagesData.filter(function ( obj ) {
return obj.$id === data.key;
})[0];
// add any needed data for this new line
$rootScope.$broadcast('list:updated', newLine);
}
});
In your controller you just have to listen to the broadcast :
$scope.newData = [];
$scope.$on('list:updated', function (event, data) {
$scope.newData.push(data);
});
You can then use $scope.newData to display the way to show the new messages and onclick merge this array with your main one on the scope, so the new messages appears.
Hope this helps someone !

Resources