I have a UserFeed component and EditForm component. As soon as I edit the form, I need to be redirected to the UserFeed and the updated data should be shown on UserFeed(title and description of the post).
So, the flow is like-UserFeed, which list the posts, when click on edit,redirected to EditForm, updates the field, redirected to UserFeed again, but now UserFeed should list the posts with the updated data, not the old one.
In this I'm just redirectin to / just to see if it works. But I need to be redirected to the feed with the updated data.
EditForm
import React, { Component } from "react";
import { connect } from "react-redux";
import { getPost } from "../actions/userActions"
class EditForm extends Component {
constructor() {
super();
this.state = {
title: '',
description: ''
};
handleChange = event => {
const { name, value } = event.target;
this.setState({
[name]: value
})
};
componentDidMount() {
const id = this.props.match.params.id
this.props.dispatch(getPost(id))
}
componentDidUpdate(prevProps) {
if (prevProps.post !== this.props.post) {
this.setState({
title: this.props.post.post.title,
description: this.props.post.post.description
})
}
}
handleSubmit = () => {
const id = this.props.match.params.id
const data = this.state
this.props.dispatch(updatePost(id, data, () => {
this.props.history.push("/")
}))
}
render() {
const { title, description } = this.state
return (
<div>
<input
onChange={this.handleChange}
name="title"
value={title}
className="input"
placeholder="Title"
/>
<textarea
onChange={this.handleChange}
name="description"
value={description}
className="textarea"
></textarea>
<button>Submit</button>
</div>
);
}
}
const mapStateToProps = store => {
return store;
};
export default connect(mapStateToProps)(EditForm)
UserFeed
import React, { Component } from "react"
import { getUserPosts, getCurrentUser } from "../actions/userActions"
import { connect } from "react-redux"
import Cards from "./Cards"
class UserFeed extends Component {
componentDidMount() {
const authToken = localStorage.getItem("authToken")
if (authToken) {
this.props.dispatch(getCurrentUser(authToken))
if (this.props && this.props.userId) {
this.props.dispatch(getUserPosts(this.props.userId))
} else {
return null
}
}
}
render() {
const { isFetchingUserPosts, userPosts } = this.props
return isFetchingUserPosts ? (
<p>Fetching....</p>
) : (
<div>
{userPosts &&
userPosts.map(post => {
return <Cards key={post._id} post={post} />
})}
</div>
)
}
}
const mapStateToPros = state => {
return {
isFetchingUserPosts: state.userPosts.isFetchingUserPosts,
userPosts: state.userPosts.userPosts.userPosts,
userId: state.auth.user._id
}
}
export default connect(mapStateToPros)(UserFeed)
Cards
import React, { Component } from "react"
import { connect } from "react-redux"
import { compose } from "redux"
import { withRouter } from "react-router-dom"
class Cards extends Component {
handleEdit = _id => {
this.props.history.push(`/post/edit/${_id}`)
}
render() {
const { _id, title, description } = this.props.post
return (
<div className="card">
<div className="card-content">
<div className="media">
<div className="media-left">
<figure className="image is-48x48">
<img
src="https://bulma.io/images/placeholders/96x96.png"
alt="Placeholder image"
/>
</figure>
</div>
<div className="media-content" style={{ border: "1px grey" }}>
<p className="title is-5">{title}</p>
<p className="content">{description}</p>
<button
onClick={() => {
this.handleEdit(_id)
}}
className="button is-success"
>
Edit
</button>
</div>
</div>
</div>
</div>
)
}
}
const mapStateToProps = state => {
return {
nothing: "nothing"
}
}
export default compose(withRouter, connect(mapStateToProps))(Cards)
updatePost action
export const updatePost = (id, data, redirect) => {
return async dispatch => {
dispatch( { type: "UPDATING_POST_START" })
try {
const res = await axios.put(`http://localhost:3000/api/v1/posts/${id}/edit`, data)
dispatch({
type: "UPDATING_POST_SUCCESS",
data: res.data
})
redirect()
} catch(error) {
dispatch({
type: "UPDATING_POST_FAILURE",
data: { error: "Something went wrong"}
})
}
}
}
I'm not sure if my action is correct or not.
Here's the updatePost controller.
updatePost: async (req, res, next) => {
try {
const data = {
title: req.body.title,
description: req.body.description
}
const post = await Post.findByIdAndUpdate(req.params.id, data, { new: true })
if (!post) {
return res.status(404).json({ message: "No post found "})
}
return res.status(200).json({ post })
} catch(error) {
return next(error)
}
}
One mistake is that to set the current fields you need to use $set in mongodb , also you want to build the object , for example if req.body does not have title it will generate an error
updatePost: async (req, res, next) => {
try {
const data = {};
if(title) data.title=req.body.title;
if(description) data.description=req.body.description
const post = await Post.findByIdAndUpdate(req.params.id, {$set:data}, { new: true })
if (!post) {
return res.status(404).json({ message: "No post found "})
}
return res.status(200).json({ post })
} catch(error) {
return next(error)
}
}
Related
I am working on a product where I have pre orders and normal orders. For my pre order items, I have the variable pre_orderQty, and for my normal items, I have the variable qty. In my OrderScreen.js, I have a handler that deals with if an order has been shipped (shipHandler). The issue that I am having is that I am trying to get this handler to also update any items in my order that have a qty value of 0 to the value of the pre_orderQty when clicked.
I am using node in my backend and react in my frontend. The orders are stored in mongodb. I'm not exactly sure how to do this, and I would really appreciate any help or advice on how to accomplish this.
So far, I have attempted to do this by:
In my backend orderRouter.js
orderRouter.put(
'/:id/updated',
isAuth,
isAdmin,
expressAsyncHandler(async (req, res) => {
const order = await Order.findById(req.params.id);
if (order && order.orderItems.qty === 0) {
order.orderItems.qty = order.orderItems.pre_orderQty;
order.orderItems.pre_orderQty = 0;
const updatedOrder = await order.save();
res.send({ message: 'Order Updated', order: updatedOrder });
} else {
res.status(404).send({ message: 'Order Not Found' });
}
}),
);
Frontend:
OrderActions.js
export const updateOrder = (orderId) => async (dispatch, getState) => {
dispatch({ type: ORDER_UPDATE_REQUEST, payload: orderId });
const {
userSignin: { userInfo },
} = getState();
try {
const { data } = Axios.put(
`/api/orders/${orderId}/updated`,
{},
{
headers: { Authorization: `Bearer ${userInfo.token}` },
},
);
dispatch({ type: ORDER_UPDATE_SUCCESS, payload: data });
} catch (error) {
const message = error.response && error.response.data.message ? error.response.data.message : error.message;
dispatch({ type: ORDER_UPDATE_FAIL, payload: message });
}
};
OrderScreen
import Axios from 'axios';
import { PayPalButton } from 'react-paypal-button-v2';
import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link } from 'react-router-dom';
import {shipOrder, deliverOrder, detailsOrder, payOrder, updateOrder } from '../actions/orderActions';
import LoadingBox from '../components/LoadingBox';
import MessageBox from '../components/MessageBox';
import {
ORDER_DELIVER_RESET,
ORDER_SHIPPED_RESET,
ORDER_RETURN_RESET,
ORDER_PAY_RESET,
} from '../constants/orderConstants';
export default function OrderScreen(props) {
const orderId = props.match.params.id;
const [sdkReady, setSdkReady] = useState(false);
const orderDetails = useSelector((state) => state.orderDetails);
const { order, loading, error } = orderDetails;
const userSignin = useSelector((state) => state.userSignin);
const { userInfo } = userSignin;
const orderShip = useSelector((state) => state.orderShip);
const {
loading: loadingShip,
error: errorShip,
success: successShip,
} = orderShip;
const orderUpdate = useSelector((state) => state.orderUpdate);
const {
loading: loadingUpdate,
error: errorUpdate,
success: successUpdate,
} = orderUpdate;
const dispatch = useDispatch();
useEffect(() => {
if (
!order ||
successShip ||
successUpdate ||
(order && order._id !== orderId)
) {
dispatch({ type: ORDER_SHIPPED_RESET });
dispatch({ type: ORDER_UPDATE_RESET });
dispatch(detailsOrder(orderId));
} else {
if (!order.isPaid) {
if (!window.paypal) {
addPayPalScript();
} else {
setSdkReady(true);
}
}
}
}, [dispatch, order, orderId, sdkReady, successShip, successUpdate, order]);
const shipHandler = () => {
dispatch(shipOrder(order._id));
dispatch(updateOrder(order._id));
};
return loading ? (
<LoadingBox></LoadingBox>
) : error ? (
<MessageBox variant="danger">{error}</MessageBox>
) : (
<div>
<h1>Order {order._id}</h1>
<div className="row top">
<div className="col-1">
<div className="card card-body">
<ul>
{userInfo.isAdmin && (
<li>
{loadingShip && <LoadingBox></LoadingBox>}
{errorShip && (
<MessageBox variant="danger">{errorShip}</MessageBox>
)}
<button
type="button"
className="primary block"
onClick={shipHandler}
>
Order Shipped
</button>
</li>
)}
</ul>
</div>
</div>
</div>
</div>
);
}
i'm currently creating my first MERN App, and everything is going well, until something happened, and i'm going my try to explain because i need help !
What i'm doing is a facebook clone, where you can post something, you can delete your post and you can update your post, the logic is simple, i call dispatch to pass the data to the actions, the actions pass the data to the backend, and the backend return something to me and it saves in my store, because i'm using redux
The problem is that, when i have 2 post, and i want to delete a post, or maybe i want to edit it, the other post dissapears, it's like it loses its id and then loses the information, then i can't do anything but reaload the page, and it happens always
this is how it looks like, everything fine
Then, after trying to edit a post, the second one lost its information, and in the console, it says that Warning: Each child in a list should have a unique "key" prop, and i already gave each post the key={_id}, but the post lost it and i don't know how
Here's the code
Posts.js
import React, { useState } from "react";
import "./Posts.css";
import moment from "moment";
// Icons
import { BiDotsVertical, BiLike } from "react-icons/bi";
import { MdDeleteSweep } from "react-icons/md";
import { AiFillLike } from "react-icons/ai";
import { GrClose } from "react-icons/gr";
// Calling actions
import { deletePost, } from "../actions/posts.js";
// Gettin The Data From Redux
import { useSelector, useDispatch } from "react-redux";
const Posts = ({ setCurrentId }) => {
const [animation, setAnimation] = useState(false);
const [modal, setModal] = useState(false);
const [modalPost, setModalPost] = useState({});
// Getting The Posts
const posts = useSelector(state => state.posts);
const dispatch = useDispatch();
// Showing And Hiding Modal Window
const ModalWindow = post => {
setModalPost(post);
setModal(true);
};
// Liking the post
// const Like = id => {
// dispatch(giveLike(id));
// setAnimation(!animation);
// };
if (!posts.length) {
return <div>Loading</div>;
} else {
return (
<div className="Posts">
{/* // Modal window for better look to the post */}
{/* {modal && (
<div className="modalWindow">
<div className="container">
<div className="container-image">
<img src={modalPost.image} alt="" />
</div>
<div className="information">
<div className="container-information">
<div className="data-header">
<h2>
User <br />{" "}
<span style={{ fontWeight: "400" }}>
{moment(modalPost.createdAt).fromNow()}
</span>
</h2>
<span className="data-icon" onClick={() => setModal(false)}>
<GrClose />
</span>
</div>
<div className="message">
<h2>{modalPost.title}</h2>
<p>{modalPost.message}</p>
</div>
</div>
</div>
</div>
</div>
)} */}
{/* */}
{posts.map(post => {
const { _id, title, message, image, createdAt, likes } = post;
return (
<div className="Posts-container" key={_id}>
<div className="Fit">
<div className="Fit-stuff">
<h2 className="Fit-stuff_title">
User <br />{" "}
<span style={{ fontWeight: "400" }}>
{moment(createdAt).fromNow()}
</span>
</h2>
<a
className="Fit-stuff_edit"
href="#form"
onClick={() => setCurrentId(_id)}
>
<BiDotsVertical />
</a>
</div>
<div className="Fit-data">
<h2 className="Fit-data_title">{title}</h2>
<p className="Fit-data_message">{message}</p>
{image ? (
<div className="Fit-img">
<img
onClick={() => ModalWindow(post)}
src={image}
alt=""
/>
</div>
) : (
<div></div>
)}
</div>
<div className="Fit-shit">
<span>
{animation ? (
<AiFillLike className="fullLightBlue" />
) : (
<BiLike />
)}
{likes}
</span>
<span onClick={() => dispatch(deletePost(_id))}>
<MdDeleteSweep />
</span>
</div>
</div>
</div>
);
})}
</div>
);
}
};
export default Posts;
The form where i call update and create Post
import React, { useState, useEffect } from "react";
import Filebase from "react-file-base64";
// For the actions
import { useDispatch, useSelector } from "react-redux";
import { createPost, updatePost } from "../actions/posts.js";
import {
Wrapper,
FormContainer,
Data,
DataInput,
SecondDataInput,
FormContainerImg,
FormContainerButtons,
Buttons
} from "./FormStyled.js";
const Form = ({ currentId, setCurrentId }) => {
const [formData, setFormData] = useState({
title: "",
message: "",
image: ""
});
const specificPost = useSelector(state =>
currentId ? state.posts.find(p => p._id === currentId) : null
);
// Sending The Data And Editing The data
const dispatch = useDispatch();
useEffect(() => {
if (specificPost) setFormData(specificPost);
}, [specificPost]);
// Clear Inputs
const clear = () => {
setCurrentId(0);
setFormData({ title: "", message: "", image: "" });
};
const handleSubmit = async e => {
e.preventDefault();
if (currentId === 0) {
dispatch(createPost(formData));
clear();
} else {
dispatch(updatePost(currentId, formData));
clear();
}
};
return (
<Wrapper>
<FormContainer onSubmit={handleSubmit}>
<Data>
<DataInput
name="title"
maxLength="50"
placeholder="Title"
type="text"
value={formData.title}
onChange={e => setFormData({ ...formData, title: e.target.value })}
/>
<SecondDataInput
name="message"
placeholder="Message"
maxLength="300"
value={formData.message}
required
onChange={e =>
setFormData({ ...formData, message: e.target.value })
}
/>
<FormContainerImg>
<Filebase
required
type="file"
multiple={false}
onDone={({ base64 }) =>
setFormData({ ...formData, image: base64 })
}
/>
</FormContainerImg>
<FormContainerButtons>
<Buttons type="submit" create>
{specificPost ? "Edit" : "Create"}
</Buttons>
<Buttons onClick={clear} clear>
Clear
</Buttons>
</FormContainerButtons>
</Data>
</FormContainer>
</Wrapper>
);
};
export default Form;
My actions
import {
GETPOSTS,
CREATEPOST,
DELETEPOST,
UPDATEPOST,
LIKEPOST
} from "../actionTypes/posts.js";
import * as api from "../api/posts.js";
export const getPosts = () => async dispatch => {
try {
const { data } = await api.getPosts();
dispatch({ type: GETPOSTS, payload: data });
} catch (error) {
console.log(error);
}
};
export const createPost = newPost => async dispatch => {
try {
const { data } = await api.createPost(newPost);
dispatch({ type: CREATEPOST, payload: data });
} catch (error) {
console.log(error);
}
};
export const updatePost = (id, updatePost) => async dispatch => {
try {
const { data } = await api.updatePost(id, updatePost);
dispatch({ type: UPDATEPOST, payload: data });
} catch (error) {
console.log(error);
}
};
export const deletePost = id => async dispatch => {
try {
await api.deletePost(id);
dispatch({ type: DELETEPOST, payload: id });
} catch (error) {
console.log(error);
}
};
Redux Part
import {
GETPOSTS,
CREATEPOST,
DELETEPOST,
UPDATEPOST,
LIKEPOST
} from "../actionTypes/posts.js";
const postData = (posts = [], action) => {
switch (action.type) {
case GETPOSTS:
return action.payload;
case CREATEPOST:
return [...posts, action.payload];
case UPDATEPOST:
return posts.map(post =>
action.payload._id === post._id ? action.payload : posts
);
case DELETEPOST:
return posts.filter(post => post._id !== action.payload);
default:
return posts;
}
};
export default postData;
My controllers in the backend
import mongoose from "mongoose";
import infoPost from "../models/posts.js";
// Getting All The Posts
export const getPosts = async (req, res) => {
try {
const Posts = await infoPost.find();
res.status(200).json(Posts);
} catch (error) {
res.status(404).json({ message: error.message });
console.log(error);
}
};
// Creating A Post
export const createPost = async (req, res) => {
const { title, message, image } = req.body;
const newPost = new infoPost({ title, message, image });
try {
await newPost.save();
res.status(201).json(newPost);
} catch (error) {
res.status(409).json({ message: error.message });
console.log(error);
}
};
// Update A Post
export const updatePost = async (req, res) => {
const { id } = req.params;
const { title, message, image } = req.body;
if (!mongoose.Types.ObjectId.isValid(id))
return res.status(404).send(`No Post With Id Of ${id}`);
const updatedPost = { title, message, image, _id: id };
await infoPost.findByIdAndUpdate(id, updatedPost, { new: true });
res.json(updatedPost);
};
// Deleting A Post
export const deletePost = async (req, res) => {
const { id } = req.params;
if (!mongoose.Types.ObjectId.isValid(id))
return res
.status(404)
.send(`We Couldnt Found The Post With Id Of ${id} To Delete`);
await infoPost.findByIdAndRemove(id);
res.json(`Post With Id Of ${id} Deleted Succesfully`);
};
// Liking A Post
export const likePost = async (req, res) => {
const { id } = req.params;
if (!mongoose.Types.ObjectId.isValid(id))
return res.status(404).send(`No post with id: ${id}`);
const post = await infoPost.findById(id);
const updatedPost = await infoPost.findByIdAndUpdate(
id,
{ likeCount: post.likeCount + 1 },
{ new: true }
);
res.json(updatedPost);
};
Even though i've been trying to solve this problem for nearly 3.5 hours, i think that the problem might be in my Posts.js part, if you can help me, you're the greatest !
I am building a MEAN stack application. I am receiving a 404 response when submitting a put request.This only occurs when I am trying to edit a booking. It does not successfully edit a specific booking.
Client Side
booking.service.ts
getBooking(id: string) {
return this.http.get<{ _id: string, title: string, content: string }>("http://localhost:3000/api/bookings/" + id);
}
updateBooking(id: string, title: string, content: string){
const booking: Booking = { id: id, title: title, content: content };
this.http.put("http://localhost:3000/api/bookings/" + id, booking)
.subscribe((response) => {
const id = booking.id;
const updateBook = [...this.bookings];
const oldBookingIndex = updateBook.findIndex(b => b.id === id);
updateBook[oldBookingIndex] = booking;
this.bookings = updateBook;
this.bookingUpdate.next([...this.bookings]);
});
}
booking-create.component.html
<mat-card>
<form (submit)="onSaveBooking(bookingForm)" #bookingForm="ngForm">
<mat-form-field>
<input matInput type="date" name="title" [ngModel]="booking?.title"
required minlength="3"
#title="ngModel"
placeholder="Date">
<mat-error *ngIf="title.invalid">Please enter title</mat-error>
</mat-form-field>
<mat-form-field>
<textarea matInput name="content" [ngModel]="booking?.content" required
#content="ngModel" placeholder="Golf Course"></textarea>
<mat-error *ngIf="content.invalid">Please enter content</mat-error>
</mat-form-field>
<hr>
<button mat-button
color="accent"
type="submit">Save Booking</button>
</form>
</mat-card>
booking-create.component.ts
import { Component, OnInit } from '#angular/core';
//import {Booking } from '../booking-list/booking.model';
import { NgForm } from '#angular/forms';
import { BookingService } from '../booking.service';
import { ActivatedRoute, ParamMap } from '#angular/router';
import { Booking } from '../booking-list/booking.model';
#Component({
selector: 'app-booking-create',
templateUrl: './booking-create.component.html',
styleUrls: ['./booking-create.component.css']
})
export class BookingCreateComponent implements OnInit {
enteredTitle = '';
enteredContent = '';
booking: Booking;
private mode = 'create';
private bookingId: string;
constructor(public bookingService: BookingService, public route: ActivatedRoute) { }
ngOnInit() {
this.route.paramMap.subscribe((paramMap: ParamMap) => {
if (paramMap.has('bookingId')){
this.mode = 'edit';
this.bookingId = paramMap.get('bookingId');
this.bookingService.getBooking(this.bookingId).subscribe(bookingData => {
this.booking = {id: bookingData._id, title: bookingData.title, content: bookingData.content};
});
}
else {
this.mode = 'create';
this.bookingId = null;
}
});
}
//bookingCreated = new EventEmitter<Booking>();
onSaveBooking(form: NgForm){
if(form.invalid){
return;
}
if (this.mode === 'create') {
this.bookingService.addBooking(form.value.title, form.value.content);
}
else {
this.bookingService.updateBooking(this.bookingId, form.value.title, form.value.content);
}
form.resetForm();
}
}
Server side
app.js
app.get("/api/bookings/:id", (req, res, next) => {
Booking.findById(req.params.id).then(booking => {
if (booking) {
res.status(200).json(booking);
}
else {
res.status(404).json({ message: 'Booking not found'});
}
})
});
app.put("/api/bookings/:id", (req, res, next) => {
const booking = new Booking({
_id: req.body.id,
title: req.body.title,
content: req.body.content
});
Booking.updateOne({_id: req.params.id}, booking).then(result => {
console.log(result);
res.status(200).json({message: 'Booking updated'});
});
});
I guess when you are use using 'booking' in this line that throws an issue
Booking.updateOne({_id: req.params.id}, booking).then(result => {
You can try just to use a single params to see if it is working
{$set: {"title": req.body.title}}
Not sure if the whole object can be passed like that.
I'm trying to figure out why my code isn't working but I'm still not understanding why I'm getting this type error.
import React, { Component } from 'react';
import axios from 'axios'
class List extends Component {
state = {
title: '',
description: ''
}
componentDidMount(){
const initialState = {
_id: this.props.list._id,
title: this.props.list.title,
description: this.props.list.description
}
this.setState(initialState)
}
handleChange = (event) => {
const { value, name } = event.target
this.setState({[name]: value})
}
handleDelete = () => {
axios.delete(`/api/lists/${this.state._id}`).then(() => {
this.props.getAllLists()
})
}
handleUpdate = () => {
axios.patch(`/api/lists/${this.state._id}`, this.state).then(() => {
console.log("Updated List")
})
}
render() {
return (
<div>
<input onBlur={this.handleUpdate}
onChange={this.handleChange}
type="text" name="title"
value={this.state.title}
/>
<textarea onBlur={this.handleUpdate}
onChange={this.handleChange}
name="description" value={this.state.description}
/>
<button onClick={this.handleDelete}>X</button>
</div>
)
}
}
export default List
This is the Error msg at this link
Added the other part
import React, { Component } from 'react';
import axios from 'axios'
import List from './List';
class ListPage extends Component {
state = {
user: {},
lists: []
}
componentDidMount() {
this.getAllLists()
}
getAllLists = () => {
// make an api call to get one single user
// On the server URL is '/api/users/:userId'
const userId = this.props.match.params.userId
axios.get(`/api/users/${userId}`).then(res => {
this.setState({
user: res.data,
lists: res.data.lists
})
})
}
handleCreateNewList = () => {
const userId = this.props.match.params.userId
const payload = {
title: 'List Title',
description: 'List Description'
}
axios.post(`/api/users/${userId}/lists`, payload).then(res => {
const newList = res.data
const newStateLists = [...this.state.lists, newList]
this.setState({ lists: newStateLists })
})
}
render() {
return (
<div>
<h1>{this.state.user.username}'s List Page</h1>
onClick={this.handleCreateNewList}
New Idea
{this.state.lists.map(list => (
<List getAllLists={this.getAllLists} key={list._id} list={list}/>
))}
</div>
)
}
}
export default ListPage;
Sorry I can't comment yet. The error is because no 'list' prop is being passed to the component. Are you using the component like this:
<List list={{_id: 'someId', title: 'someTitle', description: 'someDesc'}} />
Also, why are you overwriting the state when the component mounts instead of setting the initial state?
I think you should first check if "this.state.lists" is empty or not, before passing the props.
I have a moments component which is similar to a thread/post. Users can like or dislike this moment. Once they like the moment i call the momentService to retrieve the entire list again so it refreshes and increases the like count of the moment.
However, as im only liking one post its not efficient to update all the other moments. Whats the best way to show this update/change without having to get all the moments again.
When i call the update i retrieve the updated moment object. So is there a way to update this specific moment in the moments object.
moments.component.html
<mat-card class="" *ngFor="let moment of moments">
<mat-card-header class="sub-header" fxLayout="row" fxLayoutAlign="space-between center">
<mat-card-subtitle>
{{moment.created_at}}
</mat-card-subtitle>
<mat-card-title>
<img src="http://via.placeholder.com/50x50" alt="" class="img-circle">
</mat-card-title>
<div>
<button mat-icon-button color="warn" matTooltip="Delete" (click)="delete(moment._id)">
<mat-icon>delete</mat-icon>
</button>
<button mat-icon-button>
<mat-icon>more_vert</mat-icon>
</button>
</div>
</mat-card-header>
<mat-card-content>
<p>
{{moment.body}}
</p>
</mat-card-content>
<mat-card-actions fxLayout="row" fxLayoutAlign="space-between center">
<div>
<button mat-icon-button matTooltip="Like" (click)="like(moment._id)">
<mat-icon>thumb_up</mat-icon> {{moment.likes.length}}
</button>
<button mat-icon-button matTooltip="Dislike" (click)="dislike(moment._id)">
<mat-icon>thumb_down</mat-icon> {{moment.dislikes.length}}
</button>
</div>
<button mat-icon-button matTooltip="Comments">
<mat-icon>comment</mat-icon> {{moment.comments.length}}
</button>
</mat-card-actions>
</mat-card>
moment.component.ts
import { Component, OnInit } from '#angular/core';
import { AuthService } from '../../../services/auth.service';
import { MomentService } from '../../../services/moment.service';
import { Router, ActivatedRoute, ParamMap } from '#angular/router';
import { UserService } from '../../../services/user.service';
#Component({
selector: 'app-moments',
templateUrl: './moments.component.html',
styleUrls: ['./moments.component.scss']
})
export class MomentsComponent implements OnInit {
user: any;
moments: any;
constructor(private authService: AuthService,
private userService: UserService,
private momentService: MomentService,
private route: ActivatedRoute,
private router: Router) { }
ngOnInit() {
this.route.parent.paramMap.switchMap((params: ParamMap) => {
let user_id = params.get('id');
return this.userService.get(user_id);
}).subscribe((res) => {
this.user = res;
console.log(this.user._id);
this.getMoments();
});
}
getMoments() {
this.momentService.all(this.user._id).subscribe((res) => {
this.moments = res.data;
}, (err) => {
console.log(err);
});
}
like(moment) {
let like = { 'like': this.user._id };
this.momentService.update(moment, like).subscribe((res) => {
this.getMoments();
console.log(res);
}, (err) => {
console.log(err);
});
}
dislike(moment) {
let dislike = { 'dislike': this.user._id };
this.momentService.update(moment, dislike).subscribe((res) => {
this.getMoments();
console.log(res);
}, (err) => {
console.log(err);
});
}
delete(moment) {
let id = moment;
this.momentService.delete(id).subscribe((res) => {
this.getMoments();
console.log(res);
}, (err) => {
console.log(err);
})
}
}
moments.service.ts
import { HttpParams } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { ApiService } from './api.service';
#Injectable()
export class MomentService {
path = 'moment/';
constructor(private apiService: ApiService) { }
create(user_id, moment) {
let endpoint = this.path;
return this.apiService.post(endpoint, moment);
}
delete(moment_id) {
let endpoint = this.path + moment_id;
return this.apiService.delete(endpoint);
}
all(user_id) {
let endpoint = this.path;
let params = new HttpParams().set('author', user_id);
return this.apiService.get(endpoint, params);
}
get(moment_id) {
let endpoint = this.path + moment_id;
return this.apiService.get('endpoint');
}
update(moment_id, data) {
let endpoint = this.path + moment_id;
return this.apiService.put(endpoint, data);
}
search(filters) {
let endpoint = this.path;
let params = new HttpParams().set('filters', filters);
return this.apiService.get(endpoint, params);
}
}
moment.controller.js (backend api)
update(req, res) {
let id = req.params.id;
Moment.findOne({ '_id': id }, (err, moment) => {
if (req.body.body) {
moment.body = req.body.body;
}
// LIKE&DISLIKE Toggle
if (req.body.like) {
if (moment.dislikes.indexOf(req.body.like) > -1) {
moment.dislikes.remove(req.body.like);
}
if (moment.likes.indexOf(req.body.like) > -1) {
moment.likes.remove(req.body.like);
} else {
moment.likes.push(req.body.like);
}
}
if (req.body.dislike) {
if (moment.likes.indexOf(req.body.dislike) > -1) {
moment.likes.remove(req.body.dislike);
}
if (moment.dislikes.indexOf(req.body.dislike) > -1) {
moment.dislikes.remove(req.body.dislike);
} else {
moment.dislikes.push(req.body.dislike);
}
}
moment.save((err, moment) => {
if (err) {
return res.status(404).json({
success: false,
status: 404,
data: {},
mesage: "Failed to update moment"
});
}
return res.status(201).json({
succss: true,
status: 201,
data: moment,
message: "Succesfully updated moment",
});
});
})
}
Instead of passing the id to the like/dislike methods, you can pass the entire object and from there pass the id to the update method, and on success, mutate the reference.