I'm trying to make a filtering system for my website and was wondering how I could filter a GET request by specifying what to filter from the frontend.
Here is what I'm trying to do:
If someone selects the filter options "SDG 2: Zero Hunger", "1: Discussion Project", and "Demographic", the user will click submit and then only the first card that has all those things will show up on the right of it, not the second one underneath it.
I've tried using URLSearchParams but I haven't been able to get it to work. I'm not sure how to go about this problem as the other stackoverflow forums for similar questions use that. This is the react frontend code I have (the only parts that matter are handleSubmit and componentDidUpdate), right now I just want to console.log the object that I got from the database which I filtered:
import React, { useEffect, useState } from 'react'
class Dropdown extends React.Component {
constructor(props) {
super(props);
this.state = {
sdg: 'SDG 1: No Poverty',
assignment_type: 1,
theme: 'Demographic'
};
this.handleSDGChange = this.handleSDGChange.bind(this);
this.handleAssignmentChange = this.handleAssignmentChange.bind(this);
this.handleThemeChange = this.handleThemeChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
// Handling all 3 input changes
handleSDGChange(event) {
this.setState({sdg: event.target.value});
}
handleAssignmentChange(event) {
this.setState({assignment_type: event.target.value});
}
handleThemeChange(event) {
this.setState({theme: event.target.value});
}
componentDidUpdate() {
const fetchProjects = async () => {
const response = await fetch('/api/projects' + URLSearchParams({ sdg: this.state.sdg})) // Will add other 2 later, testing out 1 first
const json = await response.json() // contains array of projects
if (response.ok) {
console.log(json)
}
else {
console.log(json.error)
}
}
fetchProjects()
}
// Handling all 3 input submissions
handleSubmit(event) {
console.log(this.state.sdg)
alert(this.state.sdg + '--- Assignment Type: ' + this.state.assignment_type + '--- Theme: ' + this.state.theme);
event.preventDefault();
this.componentDidUpdate()
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>SDG:</label>
<select value={this.state.sdg} onChange={this.handleSDGChange}>
{/* <option>Select SDG</option> */}
<option value="SDG 1: No Poverty">SDG 1: No Poverty</option>
<option value="SDG 2: Zero Hunger">SDG 2: Zero Hunger</option>
<option value="SDG 3: Good Health & Well Being">SDG 3: Good Health & Well Being</option>
</select>
<label>Assignment Type:</label>
<select value={this.state.assignment_type} onChange={this.handleAssignmentChange}>
<option value="1">1: Discussion Project</option>
<option value="2">2: PDF Case study</option>
<option value="3">3: Community Project</option>
</select>
<label>Theme:</label>
<select value={this.state.theme} onChange={this.handleThemeChange}>
<option value="Demographic">Demographic</option>
<option value="Economical">Economical</option>
<option value="Socio-cultural">Socio-cultural</option>
<option value="Technological">Technological</option>
<option value="Ecological">Ecological</option>
<option value="Poltical">Poltical</option>
</select>
<input type="submit" value="Submit" />
</form>
);
}
}
export default Dropdown
I'm also unsure how I can access this data in my express.js backend, here is what my GET route looks like:
const getProjects = async (req, res) => {
const projects = await Project.find({}).sort({ createdAt: -1 })
res.status(200).json(projects)
}
How do I send my parameters for the GET request from my class component to the backend which then can query the MongoDB and get only the filtered objects?
Related
Initially I used a GET request to call every single item in my MongoDB database to my frontend but now I'm trying to implement a filter system where users would narrow down the options presented by the database using filters.
Here is the concept of what i'm trying to do, this is what it looks like:
If someone selects the filter options "SDG 2: Zero Hunger", "1: Discussion Project", and "Demographic", the user will click submit and then only the first card that has all those things will show up on the right of it, not the second one underneath it.
I'm struggling as to how I would send the information as to how to filter the database because I get the error Failed to execute 'fetch' on 'Window': Request with GET/HEAD method cannot have body.
How could I code it so that once a user clicks submit, it sends an object containing the filter table data, ex. const filterData = {sdg, assignment_type, theme} (where each refers to its respective thing), to the backend where I perform a GET request to the database in which I use the following code to pull the filtered data:
// filtering a project, calling this everytime filter is changed
const filterProject = async (req, res) => {
const {sdg, assignment_type, theme} = req.body
const filteredProjects = await Project.find({sdg: sdg, assignment_type: assignment_type, theme: theme})
res.status(200).json(filteredProjects)
}
Here is the code for my filtering page right now:
// Filterpage.js
import ProjectDetails from '../ProjectDetails'
import Dropdown from './Dropdown'
import { useEffect, useState } from 'react'
const FilterBody = () => {
const [projects, setProjects] = useState(null)
useEffect(() => {
const fetchProjects = async () => {
const response = await fetch('/api/projects') // Change localhost to server name when deploying
const json = await response.json() // contains array of projects
if (response.ok) {
setProjects(json)
}
}
fetchProjects()
}, [])
return (
<div className="filterHome">
<div className="filterTableContainer">
<div className="filterTableTitle">
Filter Table
</div>
<div className="filterSDGDropDown">
<Dropdown />
</div>
</div>
{/* Lists projects */}
<div>
<div className="projects">
{projects && projects.map((project) => (
<ProjectDetails key={project._id} project={project}/>
))}
</div>
</div>
</div>
)
}
export default FilterBody
Here is the actual filter table, I'm calling this class in Filterpage.js
// Dropdown.js - WHERE THE ACTUAL FILTER TABLE IS
import React, { useEffect, useState } from 'react'
class Dropdown extends React.Component {
constructor(props) {
super(props);
this.state = {
sdg: 'SDG 1: No Poverty',
assignment_type: 1,
theme: 'Demographic'
};
this.handleSDGChange = this.handleSDGChange.bind(this);
this.handleAssignmentChange = this.handleAssignmentChange.bind(this);
this.handleThemeChange = this.handleThemeChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
// Handling all 3 input changes
handleSDGChange(event) {
this.setState({sdg: event.target.value});
}
handleAssignmentChange(event) {
this.setState({assignment_type: event.target.value});
}
handleThemeChange(event) {
this.setState({theme: event.target.value});
}
// Handling all 3 input submissions
handleSubmit(event) {
alert(this.state.sdg + '--- Assignment Type: ' + this.state.assignment_type + '--- Theme: ' + this.state.theme);
event.preventDefault();
// TODO, SEND DATA TO BACKEND TO BE FILTERED
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>SDG:</label>
<select value={this.state.sdg} onChange={this.handleSDGChange}>
<option value="SDG 1: No Poverty">SDG 1: No Poverty</option>
<option value="SDG 2: Zero Hunger">SDG 2: Zero Hunger</option>
<option value="SDG 3: Good Health & Well Being">SDG 3: Good Health & Well Being</option>
</select>
<label>Assignment Type:</label>
<select value={this.state.assignment_type} onChange={this.handleAssignmentChange}>
<option value="1">1: Discussion Project</option>
<option value="2">2: PDF Case study</option>
<option value="3">3: Community Project</option>
</select>
<label>Theme:</label>
<select value={this.state.theme} onChange={this.handleThemeChange}>
<option value="Demographic">Demographic</option>
<option value="Economical">Economical</option>
<option value="Socio-cultural">Socio-cultural</option>
<option value="Technological">Technological</option>
<option value="Ecological">Ecological</option>
<option value="Poltical">Poltical</option>
</select>
<input type="submit" value="Submit" />
</form>
);
}
}
export default Dropdown
Here is my projects.js routes backend code:
const express = require('express')
const {
createProject,
getProject,
getProjects,
deleteProject,
updateProject,
filterProject
} = require('../controllers/projectController')
const router = express.Router()
// GET all workouts
router.get('/', getProjects) // Base route for /api/projects
// FILTER workouts
router.get('/filter', filterProject)
// GET a single workout
router.get('/:id', getProject)
// POST all workouts
router.post('/', createProject)
// DELETE a single workout
router.delete('/:id', deleteProject)
// UPDATE a single workout
router.patch('/:id', updateProject)
module.exports = router
And here is my projectController.js which handles all the requests in the backend (i only included the relevant ones):
const Project = require('../models/projectModel')
const mongoose = require('mongoose')
// get all projects
const getProjects = async (req, res) => {
const projects = await Project.find({}).sort({ createdAt: -1 }) // Specify
// const test = await Project.find({sdg: "SDG 1: No Poverty", assignment_type: 1})
// console.log(test)
res.status(200).json(projects)
}
// filtering a project, calling this everytime filter is changed
const filterProject = async (req, res) => {
const {sdg, assignment_type, theme} = req.body
const filteredProjects = await Project.find({sdg: sdg, assignment_type: assignment_type, theme: theme})
res.status(200).json(filteredProjects)
}
module.exports = {
getProjects,
getProject,
createProject,
deleteProject,
updateProject,
filterProject
}
In addition, is there a way to use react context so that every time the user clicks submit, it will just update the results without refreshing the page?
I'm new to react an am trying to create an app to use in my portfolio. Essentially the program is a menu that has access to different menus(json files: texas_pick.js, breakfast.js...), the program is meant to display the menu options in form of buttons, the buttons' details are retrieved from their respective json file. The problem that I am facing is that when making a click on a menu option the data of the last menu item is retrieved. I programmed the backend to only push the item's name and price to the database, and the frontend, to retrieve this data and display it on a table. The data retrieved is only the last button's and not any others. I believe the problem to possibly be within my button code. Any help/tips/recommendations you could give are greatly appreciated.
I clicked every menu item and only the data from the last one was retrieved
import React from 'react'
import {useEffect,useState} from 'react'
import axios from 'axios'
import Texas_Pick from '../../json_files/texas_pick.json'
import './Mid_Container.css'
function Mid_Container() {
const [items, setItems] = useState(Texas_Pick);
const [order, setOrder] = useState({
item: '',
cost: ''
})
const createOrder = () => {
axios
.post("http://localhost:5000/orders", order)
.then(res => {window.location.reload(false)})
.catch(err => console.error(err));
}
const item1 = items[0];
const item2 = items[1];
const item3 = items[2];
const item4 = items[3];
const item5 = items[4];
const item6 = items[5];
return (
<div className="Mid_Container">
<button
style={{backgroundImage: `url(${item1.image})`}}
value={order.item=item1.item,order.cost=item1.price}
onClick={createOrder}
>
<p id="pPrice">${item1.price}</p>
<p id="pItem" >{item1.item}</p>
</button>
<button
style={{backgroundImage: `url(${item2.image})`}}
value={order.item=item2.item,order.cost=item2.price}
onClick={createOrder}
>
<p id="pPrice">${item2.price}</p>
<p id="pItem" >{item2.item}</p>
</button>
<button
style={{backgroundImage: `url(${item3.image})`}}
value={order.item=item3.item,order.cost=item3.price}
onClick={createOrder}
>
<p id="pPrice">${item3.price}</p>
<p id="pItem" >{item3.item}</p>
</button>
<button
style={{backgroundImage: `url(${item4.image})`}}
value={order.item=item4.item,order.cost=item4.price}
onClick={createOrder}
>
<p id="pPrice">${item4.price}</p>
<p id="pItem" >{item4.item}</p>
</button>
</div>
)
}
export default Mid_Container
I think that you should have this approach:
function SomeComponent() {
// Mocking your datas
const [items, setItems] = React.useState([
{
price: "1",
item: "i am the first",
image: "image1.png",
},
{
price: "7",
item: "I am the second",
image: "image2.png",
},
{
price: "3",
item: "i am the third",
image: "image3.png",
},
]);
const [order, setOrder] = React.useState();
const [myResponse, setMyResponse] = React.useState();
const createOrder = (clickedItem) => {
setOrder(clickedItem);
console.log(clickedItem);
// axios
// .post("http://somewhere", clickedItem)
// .then((res) => {
// setMyResponse(res); // or setMyResponse(res.json());
// })
// .catch((err) => console.error(err));
};
console.log('Log selected order in render loop ==> ', order);
console.log('Log response in render loop ==> ', myResponse);
return (
<div>
<div className="Mid_Container">
{items.length && items.map((currItem, index) => {
return (
<button
key={index}
style={{ backgroundImage: `url(${currItem.image})` }}
onClick={() => createOrder(currItem)}
>
<p id="pPrice">${currItem.price}</p>
<p id="pItem">{currItem.item}</p>
</button>
);
})}
</div>
</div>
);
}
Mapping on your items with map function, and pass the current item to your onClickEvent.
I also think you don't need a value attribute on your buttons. It's also not the place to do operations like you do :)
You also don't have to reload the page in the "then" of your promise. React is made to do SPA (single page application), so in the "then", you can put some code like "setResult(myResponse)" to store in you component the new data you got from your API.
I am very new to React so I am still learning a lot. No matter what I do, the onSelect just does not seem to work. I made a simple function that it should call. You can tell it's been called via the console.log but in the browser when I look at the console it just does nothing.
Here is the code for the dropdown:
import React,{useState} from 'react';
import SEPractices from "../dummydata/SEPractices"
const optionItems = SEPractices.map((SEPractice) =>
<option key={SEPractice.practice}>{SEPractice.practice}</option>
);
const Dropdown = () => {
const handleSelect=(e)=>{
console.log("Inside");
}
return (
<div>
<select onSelect={handleSelect}>
<option Default hidden>
Select an SE Practice
</option>
{optionItems}
</select>
</div>
)
}
export default Dropdown;
Try using onChange
<select value={selectValue} onChange={handleSelect}>
<option Default hidden>
Select an SE Practice
</option>
{optionItems}
</select>
And handleSelect like this:
const [selectValue, setValue] = useState("")
cosnt handleSelect = (e) => {
setValue(e.target.value);
}
<select value={selectValue} onChange={handleSelect}>
<option value="dummyData">
Select an SE Practice
</option>
{optionItems}
</select>
and don't use the Default and hidden, React will take care of default value why specifying value={selectValue} in element.
Also, we need to pass the value to the <option>, it will track based on the value.
So I'm using Nodejs, MongoDB and Reactjs
and I'm trying to Edit properties of projects.
I have multiple projects and when I want to edit properties of one I can't do it. We can access to properties inside inputs, we can see Title and Type but can't even delete, write, he access to properties by its ID but then I can't change it, I guess I have multiple problems here than.
I'll write here my server code, and my Edit/Update project page and a gif with an example when I say that I can't even change anything on inputs.
My server code:
//Render Edit Project Page byId
app.get('/dashboard/project/:id/edit', function(req, res){
let id = req.params.id;
Project.findById(id).exec((err, project) => {
if (err) {
console.log(err);
}
res.json(project);
});
}
//Update Projects Properties byId
app.put('/dashboard/project/:id/edit', function(req, res){
var id = req.params.id;
var project = {
title: req.body.title,
typeOfProduction: req.body.typeOfProduction
};
Project.findByIdAndUpdate(id, project, {new: true},
function(err){
if(err){
console.log(err);
}
res.json(project);
})
};
My React Component Edit Project Page
import React, { Component } from 'react';
import { NavLink } from 'react-router-dom';
import './EditProject.css';
class EditProject extends Component {
constructor(props){
super(props);
this.state = {
//project: {}
title: '',
typeOfProduction: ''
};
}
inputChangedHandler = (event) => {
const updatedProject = event.target.value;
}
componentDidMount() {
// console.log("PROPS " + JSON.stringify(this.props));
const { match: { params } } = this.props;
fetch(`/dashboard/project/${params.id}/edit`)
.then(response => { return response.json()
}).then(project => {
console.log(JSON.stringify(project));
this.setState({
//project: project
title: project.title,
typeOfProduction: project.typeOfProduction
})
})
}
render() {
return (
<div className="EditProject"> EDIT
<form method="POST" action="/dashboard/project/${params.id}/edit?_method=PUT">
<div className="form-group container">
<label className="form--title">Title</label>
<input type="text" className="form-control " value={this.state.title} name="title" ref="title" onChange={(event)=>this.inputChangedHandler(event)}/>
</div>
<div className="form-group container">
<label className="form--title">Type of Production</label>
<input type="text" className="form-control " value={this.state.typeOfProduction} name="typeOfProduction" ref="typeOfProduction" onChange={(event)=>this.inputChangedHandler(event)}/>
</div>
<div className="form-group container button">
<button type="submit" className="btn btn-default" value="Submit" onClcik={() => onsubmit(form)}>Update</button>
</div>
</form>
</div>
);
}
}
export default EditProject;
Erros that I have:
1- DeprecationWarning: collection.findAndModify is deprecated. Use findOneAndUpdate, findOneAndReplace or findOneAndDelete instead.
2- Inputs can't change
3- When click "Update" button:
I think your update override the entire object because you forgot the $set operator. This is the operator to change only the atributtes of an object and not the entire object replacing!
Example:
Model.update(query, { $set: { name: 'jason bourne' }}, options, callback)
First of all, concerning the deprecation warning, you need to change the method findAndModify (As I do not see it here, I guess you're using it elsewhere, or maybe one of the methods you use is calling it) by one of the suggested methods and change your code accordingly.
Then, you need to learn about React and controlled components : https://reactjs.org/docs/forms.html
You need to set the component's state in your onChange handler, such as :
this.setState({
title: event.target.value // or typeOfProduction, depending on wich element fired the event
});
This is called a controlled component in React.
Concerning the response body you get when clicking on Update button, this is actually what you asked for :
res.json(project);
returns the project variable as a JSON file, which is displayed on your screenshot.
See this question for more information about it : Proper way to return JSON using node or Express
Try replace "value" in input tag with "placeholder"
I need to fetch the select drop down list vales from Mongo DB mongoose.
In my angular 2 i have something which is static as:
UPDATED CODE:
I want to achieve something like this and I have tried like :
<form role="form">
<fieldset class="form-group">
<label>Select Category</label><br/><br/>
<select [(ngModel)]="selectedObject" name="first" class="form-control">
//Static data
<!--<option>Select</option>
<option>Juices</option>
<option>Chats</option>
<option>Meals</option>-->
<option *ngFor="let c of categories"
[value]="c"
[selected]="c.categoryName == selectedObject.categoryName"
>
{{c.categoryName}}
</option>
</select>
</fieldset>
</form>
In my component.ts i have something like:
export class BlankPageComponent implements OnInit {
selectedObject = null;
categories = [];
constructor(private addproductService: AddproductService,
private flashMessage: FlashMessagesService,
private router: Router) { }
ngOnInit() {
const categories = {
category_name: this.category_name
}
this.addproductService.loadData(categories).subscribe(data => {
});
\src\app\shared\services\addproduct.service.ts
export class AddproductService {
categories: any;
loadData(pList: any) {
this.categories = pList;
if (this.categories.length > 0) {
let headers = new Headers();
headers.append('Content-Type', 'application/json');
return this.http.post('http://10.22.*.*:3000/categories/get', this.categories, { headers: headers })
.map(res => res.json());
//this.selectedObject = this.categories[0];
}
}
Error as of now: TypeError: Cannot read property 'subscribe' of undefined
But i need to get the values of the drop down from backend(bind them)
In my mongoose backend i have a document with field name : category_name which has values like : Juice, Meals, Chats etc and in postman i fetch them like using API:
http://10.22..:3000/categories/get
I am able to fetch the category from nodejs using GET request,
But how to bind the select control and populate data from mongoose
.ts
categories = ['Juices', 'Chats', 'Meals'];
selectedCategory: string;
.html
<form role="form">
<fieldset class="form-group">
<label>Select Category</label><br/><br/>
<select [(ngModel)]="selectedCategory" class="form-control">
<option disabled>--Select--</option>
<option *ngFor="let category of categories" [value]="category">{{category}}</option>
</select>
</fieldset>
</form>