Sending query parameter and input value to a method - node.js

looking for some help here please. So, I have a page that list the users from a group using the groupID as the parameter. Sometimes these list of users could be really long and I wanted to build in a search functionality to search the user(member) of the group.
I access the Workspace Directory API and method members.list like this:
const groupUsers = async function (groupid, from) {
var members = [];
var pageToken = '';
var newPageToken = '';
const admin = await authorizeDirectory();
const resp = await admin.members.list({
groupKey: groupid,
});
members = resp.data.members;
console.log(members);
// console.log(members);
// members = resp.data.members;
const total = members.length;
return { results: members.splice(from, 10), total };
};
I then pass the response back to an index.js file that handles it for rendering to a handlebars template. Inside this template I have form that allows the user to enter the email address so that I can use it to search for it directly from the members.get method
My code for that is:
<div class="searchWrapper">
<form action="/group" method="GET" class="searchBar">
<input type="text" name="searchBar" id="searchBar" placeholder="Enter the user's email ID"
value="{{this.term}}" />
<span class="search-button">
<a class="waves-effect waves-light btn-small" id="searchButton">FIND</a>
</span>
</form>
</div>
Could someone please help me understand how do I send the groupID and the value entered in the input search bar to the method, so that I can query the API. Would really appreciate the help.
My routes:
router.get('/group/:groupid', isUserAuthenticated, async (req, res) => {
const groupid = req.params.groupid;
const from = parseInt(req.query.from || 0, 10) || 0;
const members = await groupUsers(groupid, from);
const pages = [];
for (var i = 0; i < Math.ceil(members.total / 10); i++)
pages.push({ page: i * 10, label: i + 1 });
res.render('groups', {
members: members.results,
total: members.total,
groupid: groupid,
from: from,
pages: pages,
});
// res.send(`Group ID = ${req.params.groupid}`);
});

Related

How can I recall a GET request with new parameters and have it get the new items with the parameters in MERN stack?

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?

TypeError: Cannot read property 'count' of undefined

I am a novice MERN stack developer.
I am trying to calculate the number of pages for pagination. The info object prints in console.log. However, when I try to use it in the for loop I get an error.
Can someone please explain what's the React logic or flow behind this? I have had issues with this multiple times but, could fix it with conditional rendering. But, somehow I wasn't able to fix this and I don't seem to understand the logic of how the flow in react is.
App Component :
const App = () => {
const [episodes, setEpisodes] = useState({});
const [loading, setLoading] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const [episodesPerPage, setEpisodesPerPage] = useState(10);
useEffect(() => {
const fetchEpisodes = async () => {
setLoading(true);
const res = await axios.get('https://rickandmortyapi.com/api/episode/');
setEpisodes(res.data);
setLoading(false);
};
fetchEpisodes();
}, []);
console.log(episodes.info);
return (
<div>
<div id='header'>
<h1>Rick & Morty</h1>
<h2>Episodes</h2>
</div>
<div>
<h3>All Episodes</h3>
<EpisodeList episodeList={episodes.results} loading={loading} />
<Pagenation info={episodes.info} />
</div>
</div>
);
};
export default App;
Pagenation Component:
const Pagenation = ({ info }) => {
const pageNumbers = [];
console.log(info);
for (let i = 1; i <= Math.ceil(info.count / 20); i++) {
pageNumbers.push(i);
}
return (
<nav aria-label='...'>
<ul class='pagination pagination-lg'>
{pageNumbers.map((number) => {
return (
<li class='page-item active' aria-current='page'>
<span class='page-link'>
{number}
<span class='sr-only'>(current)</span>
</span>
</li>
);
})}
</ul>
</nav>
);
};
Conditional rendering can be the solution here as well.
episodes is initially an empty object, so episodes.info is initially undefined. This means you cannot access a property on info without checking if it exists first because you know already that it will be undefined at the beginning.
A simple solution might look like this:
{episodes.info && <Pagenation info={episodes.info} />}
You could also move the conditional into the Pagenation component to be something like this:
if (info) {
for (let i = 1; i <= Math.ceil(info.count / 20); i++) {
pageNumbers.push(i);
}
}
Regardless of your strategy to avoid the error, the core of the issue is that you have data that is loaded after the component mounts. This means you need to account for that data being missing for at least one render.

Cheerio : Getting a text from a list

I am scraping a web site and using node and cheerio for that purpose.
I have the below structure
<li class="wrap-level-1">
<a class="level-2 link" href="https:mysite..." target="_blank"> Tropical Viking </a>
</li>
How do I get the Tropical Viking text only ?
I am trying this
$('.wrap-level-1').map((i, el) => {
console.log('entering scrapper')
const count = resultCount++
console.log(count)
//This is what I need
const title = $(el).find('a').???
const metadata = {
title: title
}
parsedResults.push(metadata)
console.log(metadata)
})
Thanks for your help
It looks like you want this:
let parsedResults = $('.wrap-level-1').map((i, el) => {
console.log('entering scrapper')
const count = resultCount++
console.log(count)
// This is what I need
const title = $(el).find('a').text()
const metadata = {
title: title
}
return metadata
}).get()

I am trying to reset the state to an empty object after every onClick occurs

I am working on a project in React. The idea is that when you search an artist an img render on the pg. Once you click the image a list of collaborating artists is rendered. You can then click a name and see that persons collabpratign artists. Here is my issue: Rather than the state clearing/resetting each time a new artist is clicked, new artists just add on to the original state. Can someone help me figure out how to clear the state so that the state clears and returns a new list of collaborators? Been stuck on this for hours. Here is the code
searchForArtist(query) {
request.get(`https://api.spotify.com/v1/search?q=${query}&type=artist`)
.then((response) => {
const artist = response.body.artists.items[0];
const name = artist.name;
const id = artist.id;
const img_url = artist.images[0].url;
this.setState({
selectedArtist: {
name,
id,
img_url,
},
});
})
.then(() => {
this.getArtistAlbums();
})
.catch((err) => {
console.error(err);
});
}
getArtistCollabs() {
console.log('reached get artist collab function');
const { artistCounts } = this.state;
// console.log(artistCounts);
const artist = Object.keys(artistCounts).map((key) => {
//kate
const i = document.createElement("div");
i.innerHTML = key;
i.addEventListener('click', () => {
this.searchForArtist(key);
})
document.getElementById("collabs").appendChild(i);
});
this.setState({});
}
//kate
renderArtists() {
const artists = this.getArtistCollabs();
}
render() {
const img_url = this.state.selectedArtist.img_url;
return (
<div>
<form onSubmit={this.handleSubmit}>
<input type='text' name='searchInput' className="searchInput" placeholder="Artist" onChange={this.handleChange} />
<input type='submit' className="button" />
</form>
<img className="artist-img" src={this.state.selectedArtist.img_url}
// kate
onClick={this.renderArtists} alt="" />
<div id="collabs">
</div>
</div>
Your problem is right here:
const artist = Object.keys(artistCounts).map((key) => {
//kate
const i = document.createElement("div");
i.innerHTML = key;
i.addEventListener('click', () => {
this.searchForArtist(key);
})
document.getElementById("collabs").appendChild(i);
What you have done here is manually create html elements and insert them into the dom. As soon as this takes place react has no control over these newly created elements. You should only manipulate the DOM like this when its absolutely necessary. Instead you should be making a new component called something like <ArtistCollaborators> and it should take in the artists as props and be what renders the code you have here into the DOM using its own render method.
This will be the React way of doing it, and allows react to be fully control of what you are rendering into the DOM.

How to add album as playlist?

Im making a Spotify application. When you play a song, the application will show the album like this:
http://s17.postimage.org/votnl6epp/Schermafbeelding_2012_04_04_om_22_47_54.png
Ive read the Playlist documentation (https://developer.spotify.com/technologies/apps/docs/beta/c49e02a392.html), but I cant figure out how to subscribe on a album.
Can anyone help me?
How to subscribe to a playlist?
/* Instantiate the global sp object; include models & views */
var sp = getSpotifyApi(1);
var models = sp.require('sp://import/scripts/api/models');
var views = sp.require('sp://import/scripts/api/views');
$("#subscribe").click(function(playlist){
var playlist = models.Playlist.fromURI("spotify:user:spotify:playlist:3Yrvm5lBgnhzTYTXx2l55x");
playlist.subscribed = true;
playlist.observe(models.EVENT.CHANGE, function() {
console.log("Playlist is subscribed!");
});
});
The HTML input would be:
<input type="button" id="subscribe" value="Subscribe" />
You can't, you need to create a playlist from the album, here is how I proceed :
alb = m.Album.fromURI(uri, function(album) {
pl.name = album.name;
$.each(album.tracks,function(index,track){
pl.add(m.Track.fromURI(track.uri));
});
var player = new v.Player();
player.track = pl.get(0);
player.context = album;
var saveButton = "<button id='savePlaylist' class='add-playlist sp-button sp-icon' <span class='sp-plus'></span>Add as Playlist</button>";
var list = new v.List(album , function(track) {
return new v.Track(track, v.Track.FIELD.STAR | v.Track.FIELD.SHARE | v.Track.FIELD.NAME | v.Track.FIELD.DURATION);
});
$("xxx").live('click',function(e){
var myAwesomePlaylist = new m.Playlist(album.artist.name + " - " + pl.name);
$.each(pl.data.all(),function(i,track){
myAwesomePlaylist.add(track);
});
e.preventDefault();
});
Of course you have to proceed with the HTML part.
Hope it will help
Geraud

Resources