How to Remove Specific Query Filter From url in nextJS Application - url

i'm trying to add product filtering functionality like this: filter product image and after add filter i have to clear filter as user wish like this one: clear filter image if anyone give me real world idea with some more details or short word it can help me be more proffessional.

Well let's assume your filters data is like this:
[
{
catrgory: "Color",
values: [
"Silver",
"Black",
...
]
},
{
catrgory: "Material",
values: [
"Acrylic",
"Rayon",
...
]
},
...
]
You should have 2 states in your component. One for holding the filters data and another for holding the selected filters.
Fetch your filters data from the server once. (Or use a local file).
Each time the user selects a filter, you should add it to your selected filters data.
Each time the user remove a filter, you should remove it from your selected filters data.
Hope it helps (It's just a guide not the whole solution):
const MyComponent = () => {
const [filtersData, setFiltersData] = useState([]);
const [selectedFilters, setSelectedFilters] = useState([]);
useEffect(() => {
// fetch data from the server
}, []);
const handleSelectFilter = (category, value) => {
const newSelectedFilters = [...selectedFilters];
let filter = newSelectedFilters.find((selectedFilter) => selectedFilter.category === category);
if(filter) {
filter.values.push(value);
} else {
filter = {
catrgoty: category,
values: [value]
}
newSelectedFilters.push(filter);
}
setSelectedFilters(newSelectedFilters);
}
const handleDeleteFilter = (category, value) => {
let newSelectedFilters = [...selectedFilters];
const index = newSelectedFilters.findIndex((selectedFilter) => selectedFilter.category === category);
newSelectedFilters = newSelectedFilters.splice(index, 1);
setSelectedFilters(newSelectedFilters);
}
return (
<div>
{
filtersData.map((filterItem, index) => {
return (
<div key={index}>
<div>{filterItem.category}</div>
{
filterItem.values.map((value) => {
return (
<div key={value} onClick={() => handleSelectFilter(filterItem.category, value)}>{value}</div>
)
})
}
</div>
)
})
}
{
selectedFilters.map((selectedFilter, index) => {
return (
<div key={index}>
<div>{selectedFilter.category}</div>
{
selectedFilter.values.map((value) => {
return (
<div key={value} onClick={() => handleDeleteFilter(filterItem.category, value)}>{value}</div>
)
})
}
</div>
)
})
}
</div>
);
}

Related

I'd like for my streams in RxJS to wait for all items in the stream to complete before moving on, but cant figure out how

I'm using RxJS in combination with Neo4J and NestJS.
Every step needs to be fully completed in order for the next step to be able to process successfully, so ideally i'd like to replicate a Promise.all() for each of the following steps found in the chunk of code below.
Problem is, that tap doesnt seem to allow me to wait for all promises to complete, and I'm really not sure how to achive this.
loadBooks() {
const booksObservable =
this.booksService.getBooksFromAPI();
booksObservable
.pipe(
mergeMap((response) => response.data),
tap((protocol) => {
return from(
this.booksService.find(book.name).then((bookNode) => {
if (!bookNode) {
return from(
this.bookService.create({
name: book.name
}),
);
}
}),
);
}),
groupBy((book) => book.category),
tap((categoryName) => {
return from(
this.bookCategoryService
.find(categoryName.key)
.then((categoryNode) => {
if (!categoryNode) {
return from(
this.bookCategoryService.create({
name: categoryName.key,
}),
);
}
}),
);
}),
mergeMap((group) => group),
tap((book) => {
let bookId: string;
let categoryId: string;
return from(
this.bookService
.find(book.name)
.then(async (bookNode) => {
if (!bookNode) {
throw new NotFoundError(
`Could not find book by name ${book.name}`,
);
}
bookId = bookNode.getId();
await this.bookCategoryService
.find(book.category)
.then(async (categoryNode) => {
if (!categoryNode) {
throw new NotFoundError(
`Could not find category by name ${protocol.category}`,
);
}
categoryId = categoryNode.getId();
await this.bookService.relateToCategory(
bookId,
categoryId,
);
});
}),
);
}),
)
.subscribe(() => {
return 'done';
});
How do i make it so that the operations in the tap fully complete for each and every item in the stream, before moving on tho the next function in the pipe?
Thanks!

How to update the state when deleting an item using React-Redux and Rails api?

I am trying to create a delete functionality in my application which seems to be deleting the object from the backend just fine, but not in the frontend.
Here is how I structured my project:
in actions/deleteJournal.js
export const deleteJournal = (journal) => {
return dispatch => {
fetch(`http://localhost:3001/journals/${journal.id}` , {
method: "DELETE" })
.then(resp => resp.json())
.then(journal => { dispatch({ type: "DELETE_JOURNAL", journal })
})
}
}
in reducers/journalsReducer.js
const initialState = {
journals: [],
loading: true
}
const journalsReducer = (state=initialState, action) => {
switch(action.type) {
case "LOADING":
return {
...state,
loading: true
}
case "SET_JOURNALS":
return {
...state,
loading: false,
journals: action.journals
}
case "ADD_JOURNAL":
return {
...state,
journals: [...state.journals, action.journal],
}
case 'DELETE_JOURNAL':
return {
journals: state.journals.filter(todo => todo.id !== action.id),
...state
}
default:
return state;
}
}
export default journalsReducer
in components/List.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
import Journal from './Journal'
import { deleteJournal } from '../actions/getJournals'
class ListFiltered extends Component {
render() {
const journals = this.props.journals.map( journal => journal.locationId === this.props.locationId && <Journal key={journal.id} title={journal.title} content={journal.content} id={journal.id} deleteJournal={this.props.deleteJournal} />)
return (
<div>
{journals}
<p></p>
</div>
)
}
}
const mapStateToProps = state => {
return {
journals: state.journals
}
}
export default connect(mapStateToProps, {deleteJournal})(ListFiltered)
in components/Journal.js
class Journal extends Component {
render() {
const { id, title, content } = this.props;
return (
<div>
<ul>
<li>
<h3>{ title } </h3>
<p> { content }</p> <button onClick={() => this.props.deleteJournal(this.props) }>Delete</button>
</li>
</ul>
</div>
)
}
}
export default Journal
So this seems to be giving me an error " Uncaught (in promise) SyntaxError: Unexpected end of JSON input
at deleteJournal.js:48"
I checked my server and it seems to be deleting it from there but nothing in the frontend and when I refresh the item is deleted.
What can I do so it automatically deletes the item from the frontend?
I suspect the issue may be with your line
.then(resp => resp.json())
What is the body of the response when you issue the DELETE request? If it's not a valid JSON object, then JavaScript would throw the error you're seeing.
As you're not using the parsed JSON at all, you could conceivably remove that line:
export const deleteJournal = (journal) => {
return dispatch => {
fetch(`http://localhost:3001/journals/${journal.id}` , {
method: "DELETE" })
.then(journal => { dispatch({ type: "DELETE_JOURNAL", journal })
})
}
}
...but you might want to have some of checking that the DELETE method was executed successfully.
How you do that's up to you and your Rails API, but I'd typically expect to see the response have an HTTP status in the 2xx range when something is successfully deleted, and in the 4xx range if something couldn't be deleted (e.g., if the ID wasn't recognised, or there were dependency issues which meant the deletion request was rejected). That feels outside the scope of this question though.

Ng2-Charts Unexpected change chart's color when data is changed

In my project I use ng2-charts. All works fine and chart is shown as expected (data, labels, chart's colors), but when data is changed then color of chart become grey by default. May someone help to correct problem with chart's color?
Here is my code:
import { ChartDataSets } from 'chart.js';
import { Color, Label } from 'ng2-charts';
...
export class JuridicalBidPrimaryComponent extends BidComponent {
lineChartData: ChartDataSets[];
lineChartLabels: Label[];
lineChartLegend = true;
lineChartType = 'line';
lineChartColors: Color[] = [
{
backgroundColor: 'rgba(148,159,177,0.2)',
borderColor: 'rgba(148,159,177,1)'
},
{
backgroundColor: 'rgba(77,83,96,0.2)',
borderColor: 'rgba(77,83,96,1)'
}];
options: any = {
legend: { position: 'bottom' }
}
constructor(
...//inject services
) {
super();
this.initData();
};
initData(): void {
this.lineChartData = [];
this.lineChartLabels = [];
if (this.cabinetId)
this.getData(this.year);
}
getData(year: number) {
this.isLoading = true;
var limitPromise = this.juridicalLimitService.getPrimary(this.cabinetId, year).catch(error => {
this.notificationService.error(error);
return Observable.throw(error);
});
var analyticsPromise = this.juridicalAnalyticsService.getUsedEnergy(this.cabinetId, year).catch(error => {
this.notificationService.error(error);
return Observable.throw(error);
});
forkJoin([limitPromise, analyticsPromise]).subscribe(data => {
this.limits = data[0];
this.lineChartLabels = data[1].map(e => e.Period);
this.lineChartData.push(
{
data: data[1].map(e => e.Limit),
label: 'Bid'
},
{
data: data[1].map(e => e.Used),
label: 'Used'
}
);
this.isLoading = false;
}, error => {
this.isLoading = false;
});
}
}
export abstract class BidComponent {
cabinetId: number;
isLoading: boolean = false;
#Input("periods") periods: BaseDictionary[];
#Input("cabinetId") set CabinetId(cabinetId: number) {
this.cabinetId = cabinetId;
this.initData();
}
abstract initData(): void;
}
As you can see this component is partial and I use setter to listen of cabinetId changes.
Here is html part:
...
<canvas baseChart width="400" height="150"
[options]="options"
[datasets]="lineChartData"
[labels]="lineChartLabels"
[legend]="lineChartLegend"
[chartType]="lineChartType"
[colors]="lineChartColors"></canvas>
...
And I use this component as:
<app-juridical-bid-primary [cabinetId]="cabinetId"></app-juridical-bid-primary>
I find similar question similar question, but, unfortunately, don't understand answer
After some hours of code testing I find answer. It is needed to correct code from question:
...
import * as _ from 'lodash'; //-- useful library
export class JuridicalBidPrimaryComponent extends BidComponent {
lineChartData: ChartDataSets[] = [];
lineChartLabels: Label[] = [];
...
initData(): void {
/*this.lineChartData = [];
this.lineChartLabels = [];*/ //-- this lines is needed to remove
if (this.cabinetId)
this.getData(this.year);
}
getData(year: number) {
...
forkJoin([limitPromise, analyticsPromise]).subscribe(data => {
this.limits = data[0];
this.lineChartLabels.length = 0;
this.lineChartLabels.push(...data[1].map(e => e.Period));
if (_.isEmpty(this.lineChartData)) {
//-- If data array is empty, then we add data series
this.lineChartData.push(
{
data: data[1].map(e => e.Limit),
label: 'Замовлені величини'
},
{
data: data[1].map(e => e.Used),
label: 'Використано'
}
);
} else {
//-- If we have already added data series then we only change data in data series
this.lineChartData[0].data = data[1].map(e => e.Limit);
this.lineChartData[1].data = data[1].map(e => e.Used);
}
this.isLoading = false;
}, error => {
this.isLoading = false;
});
}
}
As I understand ng2-charts, if we clean dataset (lineChartData) and add new data then the library understand this as create new series and don't use primary settings for the ones. So we have to use previous created series.
I hope it will be useful for anyone who will have such problem as I have.

Relay uses initial variable during setVariables transition, not "last" variable

I have a page where a bunch of file ids get loaded from localStorage, then when the component mounts / receives new props, it calls setVariables. While this works and the new variables are set, the results from the initial variables is used during the transition, which causes an odd flickering result.
Why would Relay give me something different during the transition at all? My expectation would be that this.props.viewer.files.hits would be the same as the previous call while setVariables is doing its thing, not the result from using the initial variables.
const enhance = compose(
lifecycle({
componentDidMount() {
const { files, relay } = this.props
if (files.length) {
relay.setVariables(getCartFilterVariables(files))
}
},
}),
shouldUpdate((props, nextProps) => {
if (props.files.length !== nextProps.files.length && nextProps.files.length) {
props.relay.setVariables(getCartFilterVariables(nextProps.files))
}
return true
})
)
export { CartPage }
export default Relay.createContainer(
connect(state => state.cart)(enhance(CartPage)), {
initialVariables: {
first: 20,
offset: 0,
filters: {},
getFiles: false,
sort: '',
},
fragments: {
viewer: () => Relay.QL`
fragment on Root {
summary {
aggregations(filters: $filters) {
project__project_id {
buckets {
case_count
doc_count
file_size
key
}
}
fs { value }
}
}
files {
hits(first: $first, offset: $offset, filters: $filters, sort: $sort) {
${FileTable.getFragment('hits')}
}
}
}
`,
},
}
)
Ah I finally figured this out. prepareParams was changing the value
export const prepareViewerParams = (params, { location: { query } }) => ({
offset: parseIntParam(query.offset, 0),
first: parseIntParam(query.first, 20),
filters: parseJsonParam(query.filters, null), <-- setting filters variable
sort: query.sort || '',
})
const CartRoute = h(Route, {
path: '/cart',
component: CartPage,
prepareParams: prepareViewerParams, <--updating variable
queries: viewerQuery,
})

Define fragments based on fetched data

Can anyone explain how I can define fragments on Relay container based on data which currently fetched in component? Consider this example:
const TreeNodeContainer = Relay.createContainer(TreeNode, {
fragments: {
node: (variables) => Relay.QL`
fragment on TreeNode {
id,
expanded,
title,
children(first: 1000) #include(if: $expanded) {
edges {
node {
id,
${TreeNodeContainer.getFragment('node').if('how I can get component props or "expanded" value here?')}
}
}
}
}
`,
},
});
TreeNode component is simple enough - it has mutation which changed 'expanded' field on server.
class TreeNode extends React.Component {
handleClick() {
Relay.Store.update(new ExpandNodeMutation({node: this.props.node}));
}
render() {
var node = this.props.node;
var variables = this.props.relay.variables;
return (
<div className="node">
<div className="title" onClick={this.handleClick.bind(this)}> {node.title}</div>
<div className="children" style={{paddingLeft: 20}}>
{node.expanded && node.children && node.children.edges.map((edge)=> {
return <TreeNodeContainer node={edge.node} key={edge.node.id}/>
})}
</div>
</div>
)
}
}
Use .if(variables.expanded):
const TreeNodeContainer = Relay.createContainer(TreeNode, {
fragments: {
node: variables => Relay.QL`
fragment on TreeNode {
# ...
children(first: 1000) #include(if: $expanded) {
# ...
${TreeNodeContainer.getFragment('node').if(variables.expanded)}

Resources