I have to render a series of events occurring based on date connected to a calendar library. I'm using Redux/thunk to bring an index of all event objects to frontend.
I filter the events out based on searchbar value, and the current date selected on calendar as per below.
import moment from 'moment'
function filterEvents (events, searchValue, selectedDay) {
const fixedSearchValue = searchValue.toLowerCase()
const fixedSelectedDate = moment(calendarDateFormatter(selectedDay))
return events.filter(event => {
let eventTitle, eventDate, fixedEventDate, displayEventDate
eventTitle = event.title.toLowerCase()
eventDate = event.start_date
fixedEventDate = moment(eventDate)
displayEventDate = fixedEventDate.format("dddd, MMMM D")
if ((searchValue === "" || eventTitle.includes(fixedSearchValue))
&& fixedEventDate.isAfter(fixedSelectedDate)) {
event["fixedDate"] = fixedEventDate
event["displayDate"] = displayEventDate
return true
} else {
return false
}
})
}
function calendarDateFormatter(selectedDay) {
return selectedDay.year + "-" + selectedDay.month + "-" + selectedDay.day
}
I then sort the resulting events in date order using the following:
function sortEventsByDate (filteredEvents) {
return filteredEvents.sort((event1, event2) => (
dateComparer(event1, event2))
)
}
function dateComparer (event1, event2) {
let eventOneDate, eventTwoDate
eventOneDate = event1.fixedDate
eventTwoDate = event2.fixedDate
return eventOneDate.isAfter(eventTwoDate) ? 1 : -1
}
I now have an array of event items that are sorted based on date/time. Then I tried to extract all unique dates for the current events (in date order), render the date as a parent component that'll list its corresponding events (as child components) that matches the date in time order. Then it'll be the next date with its corresponding events, and so on.
How can I go about this in a clean / scalable / time complexity efficient way? I've tried to create a function that will create an object, iterate through the array of sorted events, use its date as string as keys, and an array of events that match the date key as values, and use this as props to the date parent component. However, the object keys does not save in the same order they were iterated through in the array. This results in unordered date/corresponding events. Please advise!
You can use a combination of Array.filter and Array.map
const getFilteredRenderedNodes = (list) => list
.filter(item => item.date === someDate)
.map(item => (
<div key={item.id}>{item.date}</div>
));
Using Array.filter iterates the array and finds matching items by the criteriaitem.date === someDate. This is a replaceable criteria, but it's what you use to select a subset of items from the list.
Using Array.map returns an array of React.ReactNode, or jsx, which React interprets and renders. The render method includes a key field which must be unique so that React knows which node to update and render and handle events.
You then consume with:
return (
<SomeComponent>
{getFilteredRenderedNodes(myItemList)}
</SomeComponent>
);
Where myItemList is your list of items.
Related
How can I filter table data on the basis of a range of dates?
setting filter to date column here:
const tableInstance = useRef(null);
const filterTable = (dates) => {
if (tableInstance.current) {
tableInstance.current.setFilter('session_date', dates);
}
};
onClick functionality is here:
const handleFilter = () => {
setSessionsData(data);
if (sessionsData) {
const dateArray = getDates(
moment(fromDate).format('L'),
moment(toDate).format('L')
);
filterTable(dateArray);
}
};
Add this filter to your respective column object
{
id: 'your_column_id',
accessor: 'your_accessor',
filter: (rows, id, filterValue) => {
return rows.filter(
(row) =>
filterValue.length <= 0 ||
!filterValue ||
filterValue.includes(row.values[id])
);
}
}
Here the filterValue contains the array containing all the possible matches that are required i.e dateArray (all dates from 'fromDate' to 'toDate') in your case.
If hope u are good with react concept of hooks or if u need help please follow the below link
https://reactjs.org/docs/hooks-reference.html
https://www.digitalocean.com/community/tutorials/how-to-call-web-apis-with-the-useeffect-hook-in-react
Now to your question the approach you must take.
There are two states value which u have put filter on i.e, your date_range.
You can just pass a event on filter click to update the states for date_range
Where you will add hooks to set value in table like given below,
const [list, setList] = useState([]);
useEffect(() => {
fetch('http://localhost:3333/list')
.then(data => {
setList(data.json);
})
})
}, [])
Also, One more thing to keep in mind is not to blindly call API on any state changes i.e, is there any value that have really changed from original one in state here you must know the concept of pure component to prevent you component from blindly calling the API, below is the link to use pure component in react,
https://reactjs.org/docs/react-api.html
https://www.digitalocean.com/community/tutorials/five-ways-to-convert-react-class-components-to-functional-components-with-react-hooks
I have three models (I am using Vue/Vuex-ORM on the frontend). Category, CategoryItem and Item.
I'm able to fetch an array of categories, and within each category is an array of items. An intermediate join model defines the relationships of these two models, and I am able to access as such:
// array of categories, each category has array of items
const categories = Category.query().where('pack_id', this.selectedPack.id).with('items').get();
categories.map(category => {
category.items.forEach(item => {
console.log('item.pivot: ', item.pivot); // pivot refers to join model
// how to order items based on item.pivot?
})
})
Within the .forEach, I can access the join model with item.pivot. What I am looking to do however, is sort each category's items based on item.pivot.position.
I started going down a path where the first line inside of the .map I defined a new empty array, and would then theoretically push in a new value based on whether the position was higher or lower, but I couldn't quite wrap my head around how to accomplish this.
Thanks!
Well just my luck. A half hour after posting this question, I figure it out! Here was what I did, in case anyone is curious.
categories() {
const categories = Category.query().where('pack_id', this.selectedPack.id).with('items').get();
categories.forEach(category => category.items.sort(this.compare));
return cats;
}
compare(a, b) {
let comparison = 0;
if (a.pivot.position > b.pivot.position) comparison = 1;
else if (a.pivot.position < b.pivot.position) comparison = -1;
return comparison;
},
Working with an array of data that we want to be able to sort for display in a component, and it doesn't seem to be sorting or updating the DOM, however I have a working code sample that properly demonstrates the concept, and it should be sorting, but in the angular app, it's simply not getting sorted.
The parent component that houses the original data stores the data on an Input parameter object called Batch, and the array we're sorting is on Batch.Invoices.Results. The event from the child component is fine, and the appropriate data is confirmed to bubble to the parent component.
The function that's supposed to sort the array looks like this:
public OnInvoiceSortChange({orderValue, orderAscending}){
console.log(`Invoice Sorting has been called. Value: ${orderValue} . Ascending? ${orderAscending}`);
console.log(`Before:`);
console.log(this.BatchViewModel.Invoices.Results.map(x => x.VendorName));
const sortingArray = [...this.BatchViewModel.Invoices.Results];
if(orderAscending){
const sorted = sortingArray.sort((a, b) => a[orderValue] > b[orderValue] ? 1 : 0);
this.BatchViewModel.Invoices.Results = sorted;
console.log('Sorted');
console.log(sorted.map(x => x.VendorName));
} else {
const sorted = sortingArray.sort((a, b) => a[orderValue] < b[orderValue] ? 1 : 0);
this.BatchViewModel.Invoices.Results = sorted;
console.log(sorted.map(x => x.VendorName));
}
console.log(`After:`);
console.log(this.BatchViewModel.Invoices.Results.map(x => x.VendorName));
}
All the console logs are for debugger visibility, and the output is this:
Where in my testing file (non-angular) looks like this:(where data is a direct copy of the array from the Angular app.
const ascendingData = [...data];
const descendingData = [...data];
const sortedDescending = descendingData.sort((a, b) => a['VendorName'] < b['VendorName']? 0 : 1)
const sortedAscending = ascendingData.sort((a, b) => a['VendorName'] > b['VendorName']? 0 : 1);
const vendorListAscending = sortedAscending.map(x => x.VendorName);
const vendorListDescending = sortedDescending.map(x => x.VendorName);
console.log(vendorListDescending);
console.log(vendorListAscending);
and the output looks like this:
So I see that the sorting should work, but it's just not happening in Angular.
How can I get the array sorted, and update the DOM as well?
The function you pass to sort is wrong. It is supposed to return a negative value for "less", a positive value for "greater" or zero for "equal". If orderValue is numeric then it's easiest to just return a[orderValue] - b[orderValue], if not then just change your 0 to -1.
(By the way, name orderKey could be a bit clearer maybe?)
I don't think angular has anything to do here, but I cannot tell now why you get different results. Anyway, your sort function is invalid (it states that a equals b, but at the same time b is greater than a), I hope fixing this function helps.
I am creating a function for selecting/deselecting checkboxes representing objects in my array.
Currently I have:
selectAll(allType: string, state) {
this.modalData.columnPermissions.forEach(a =>
a["can" + allType] = state
);
}
(allType allows me to target "canRead" or "canWrite" keys depending on which SELECT ALL the user chooses from the top of 2 columns.)
This is working fine - however a scenario has now been introduced where if an object contains the property IDM=TRUE then "canWrite" should always be FALSE
I'm struggling on how to now adapt my selectAll function to exclude any object with a property of IDM=TRUE on the KEY canWrite
Any help is appreciated
I have resolved this with info from depperm
this.modalData.columnPermissions.forEach(a => {
if (allType === 'Write' && !a.IDM) {
a["can" + allType] = state
} else if (allType === 'Read') {
a["can" + allType] = state
}
})
I am trying to implement a filter function that is able to search in two separate JSON fields when a user types in a search bar. Searching the whole JSON returns errors and if I repeat this function, the two similar functions cancel each other out.
My current filter function:
let filteredOArt = origArt.filter((origAItem) => {
return origAItem.authors.toLowerCase().includes(this.state.search.toLowerCase())
});
I want to be able to have the search look within the "authors" field as well as a "description" field.
Before the React render, I have this function listening to the state:
updateSearch(event) {
this.setState({ search: event.target.value })
}
Then my search function is in an input field in the React return:
<h6>Search by author name: <input type="text" value={this.state.search} onChange={this.updateSearch.bind(this)} /></h6>
You can tweak the function a bit like this
let filteredOArt = origArt.filter((origAItem) => {
return (
(origAItem.authors.toLowerCase().includes(this.state.search.toLowerCase())||
(origAItem.description.toLowerCase().includes(this.state.search.toLowerCase())
)
)
});
You actually can do a filter for both fields.
Given you have your searchValue and your array with the objects you could filter this way:
const filterByAuthorOrDescription = (searchValue, array) =>
array.filter(
item =>
item.authors.toLowerCase().includes(searchValue.toLowerCase()) ||
item.description.toLowerCase().includes(searchValue.toLowerCase())
);
const filtered = filterByAuthorOrDescription(this.state.search, articles);
filtered will now contain an array of objects that contain your searchValue in either description or authors that you can map through.
You could use some to check if the filter is positive for at least one field :
let filteredOArt = origArt.filter(origAItem => ['authors', 'description'].some(field => origAItem.[field].toLowerCase().includes(this.state.search.toLowerCase())))
Just iterate over the different field names you want to use.
Some will return true if any of the fields mentioned contains your string and avoid repetitions in your code.
Long syntax :
origArt.filter(origAItem => {
return ['authors', 'description'].some(field => origAItem.[field].toLowerCase().includes(this.state.search.toLowerCase()))
})