i'm trying to submit each time i change my input.
when i try to change the input my submit works yet changes the params to this weird looking string:
"http://localhost:3000/items?0=G&1=r&2=e&3=e&4=n&5=+&6=D&7=j&8=i&9=n&10=n"
this is the code
export async function action({ request }) {
const body = await request.formData();
}
export const loader: LoaderFunction = async ({ request }) => {
const url = new URL(request.url);
const term = url.searchParams.get('sellers');
console.log('term:', term);
const data: LoaderData = {
itemList: await db.item.findMany({
where: {
seller: term,
},
select: {
seller: true,
price: true,
},
}),
};
return json(data);
};
type LoaderData = {
itemList: Array<Item>;
};
export default function ItemsRouteIndex() {
const submit = useSubmit();
const { itemList } = useLoaderData<LoaderData>();
function handleChange(e) {
console.log('e:', e);
submit(e, { method: 'get', action: '/items' });
}
return (
<Form onChange={(e) => handleChange(e.target.value)}>
<select name='sellers'>
<option value='Blue Djinn'>Blue Djinn</option>
<option value='Green Djinn'>Green Djinn</option>
</select>
<button type='submit'>Search</button>
</Form>
);
}
The issue was that i tried to submit the value of the event and not the event form.
Here's the fix :
<Form onChange={(e) => handleChange(e.target.form)}>
<select name='sellers'>
<option value='Blue Djinn'>Blue Djinn</option>
<option value='Green Djinn'>Green Djinn</option>
</select>
</Form>
Related
Everytime I'm on my purchase page everything works and updates fine. When I hit submit for the first time in the page it updates my db correctly but alert window does not pop up. => Page rerenders and sets itself to initial view. If I stay on the page and submit a second purchase then I get a pop up box that says failure to fetch in post method and then the alert box saying that the purchase was successful pops up. Even though the failure occurred nothing is functionally wrong. All database documents are up to date. Somebody please help I have no clue what I'm doing wrong.
My front end react.js
import React, { useState, useEffect } from 'react';
import { NumberFormat as numberFormat } from '../numberFormat';
export default function Purchase() {
// user input collection structure
const [form, setForm] = useState({
amount: '',
title: '',
})
// user spending limit
const [limit, setLimit] = useState({
balance: 0,
limit: 0,
})
useEffect(() => {
async function getLimit() {
const response = await fetch(`http://localhost:4000/balance/`);
if (!response.ok) {
const message = `An error occured at effect: ${response.statusText}`;
window.alert(message);
return;
}
const data = await response.json();
const userBal = data["balance"];
const userLim = 50 - parseFloat(userBal);
setLimit({ balance: userBal, limit: userLim });
}
getLimit();
return;
}, []);
// Update State Properties
function updateForm(value) {
return setForm((prev) => {
return { ...prev, ...value };
});
}
function validateInput(input){
input = input * 1000;
if (input%10 === 0) return false;
else return true;
}
async function onSubmit() {
// check that amount is valid
if (form.title === ''){
window.alert(`Please Include a Title for the payment`);
return;
}
const bal = parseFloat(limit.balance);
const lim = parseFloat(limit.limit);
const amt = parseFloat(form.amount);
if (amt > lim || amt === 0 || amt === '' || validateInput(amt)){
window.alert(`Invalid Amount ${form.amount}.\nPlease enter value greater than 0.00 within ${lim}.`);
return;
}
const newPurchase = {
type: 'purchase',
amount: form.amount,
title: form.title,
balanceToSet: amt + bal
}
await fetch(`http://localhost:4000/purchase/add`, {
method: 'POST',
mode:'cors',
headers: {
'Access-Control-Allow-Origin': '*',
"Content-Type": 'application/json; charset=UTF-8',
},
body: JSON.stringify(newPurchase)
}
)
.then((response) => response.json()).then((data)=> {console.log(data);
})
.catch((err) => {
window.alert(`post fetch error ${err.message}`);
return;
});
window.alert(`Purchase ${form.title} of amount $${form.amount} posted.`);
return;
}
return (
// Will Display
<div className='home'>
{/* Title */}
<h1 className='hometitle'>
Make A Purchase
</h1>
<div>
<h1> Your Fizz Balance: {numberFormat(limit.balance)}</h1>
<h1> Your Fizz Allowance: {numberFormat(limit.limit)}</h1>
</div>
{/* Current Allowance */}
{/* Debt owed to fizz */}
{/* Make Purchase Form
If incorrect parameters then show text saying invalid amount below
On submission alert shows telling user a purchase of certain amount was made
render rest of the page */}
<form onSubmit={onSubmit}>
<div className="form-group">
<label htmlFor='title'>Title:</label>
<input
type='text'
id='name'
value={form.title}
onChange={(e) => updateForm({ title: e.target.value })}
/>
</div>
<div className="form-group">
<label htmlFor="amount">Amount:</label>
<input
type="text"
id="amount"
value={form.amount}
onChange={(e) => updateForm({ amount: e.target.value })}
/>
</div>
<div>
<input
type='submit'
value='Make Purchase'
/>
</div>
</form>
</div>
);
}
My backend node.js
const express = require("express");
const purchaseRoutes = express.Router();
const dbo = require("../db/connection");
const { floor } = require('mathjs');
// Add purchase to History Table and update balance in user info
purchaseRoutes.route("/purchase/add").post(
async function (req, response) {
let db_connect = dbo.getDb();
// Writing Purchase to History Table
let thisPurchase = {
type: req.body.type,
amount: parseFloat(req.body.amount),
title: req.body.title,
rfndStatus: false,
date: Date.now()
};
let queryParams = { name: "user" };
// post query contents
let updatedBalance = {
$set: {
balance: floor(req.body.balanceToSet * 1000) / 1000
}
};
const session = db_connect.startSession();
const transactionDetails = {
readPreference: 'primary',
readConcern: { level: 'local' },
writeConcern: { w: 'majority' }
}
try {
const transactionResults = await session.withTransaction(async () => {
const userColl = db_connect.db('fizzData').collection('userData');
const histColl = db_connect.db('fizzData').collection('transHist');
await userColl.updateOne(queryParams, updatedBalance,{session});
await histColl.insertOne(thisPurchase,{session});
}, transactionDetails);
response.json(transactionResults);
console.log(transactionResults);
} catch(e){
console.log(`transaction failed ${e}`)
}
finally {
await session.endSession();
}
});
I thought it could have been a cors issue so I installed the cors extension in chrome and added the mode tag to the header. The problem is not with the fetch in useEffect or at least the call because it correctly calls the right values from the database.
Seems like user agent is not aware that the form event is getting handled.
To resolve this, modify your onSubmit function as follows:
async function onSubmit(event) {
event?.preventDefault();
// ... rest of
// ... of the
// ... code
}
See if it works :)
EDIT: more info on above topic.
import "./styles.css";
export default function App() {
const onSubmit = (formEvent) => {
// if you comment this line
// you will see that console.log("Line got executed"); will print and the window will get reloaded and console.log is gone
formEvent.preventDefault();
// The above line with prevent reloading of the window
console.log("Line got executed");
};
const onButtonClick = (buttonEvent) => {
// if we comment this out the button will SUMBIT the form
buttonEvent.preventDefault();
// The above line with prevent submission of the form
// because this button is of type submit and it's default action is to submit the form
// so by adding above line we are disabling its default behavior
console.log("Button is clicked not for submission");
};
return (
<div className="App">
<form onSubmit={onSubmit}>
<input id="form-input" />
<button type="submit" onClick={onButtonClick}>
Submit or click
</button>
</form>
</div>
);
}
Codesandbox link to play around: https://codesandbox.io/s/react-preventdefault-udx49m?file=/src/App.js:0-1053
My Goal for this one is to Add ObjectId inside the array
In my backend Im declare schema on this code:
tchStudents: [{
type: Schema.Types.ObjectId,
ref: "Student"
}]
THen Im do adding an ObjectId to insert to the array of ObjectID:
My BackEnd is very fine
router.put('/assignAddStudents/:tchID', async (req,res) => {
try {
const searchTch = await Teacher.findOne({ tchID: req.params.tchID })
if(!searchTch){
return res.status(404).send({
success: false,
error: 'Teacher ID not found'
});
} else {
let query = { tchID: req.params.tchID }
let assignedStudentObjID = {$push:{tchStudents: req.body.tchStudents }}
Teacher.updateOne(query, assignedStudentObjID ,() => {
try{
return res.status(200).send({
success: true,
msg: 'Student ID has been assigned'
});
} catch(err) {
console.log(err);
return res.status(404).send({
success: false,
error: 'Teacher ID not found'
})
}
})
}
} catch (err) {
console.log(err)
}
})
But my Front End Not working
err: BAD REQUEST(400) Unexpected token " in JSON at position 0
import React, {useState} from 'react'
import axios from 'axios'
import { URL } from '../../utils/utils'
import { Modal, Button } from 'react-materialize';
import ListTchStudents from '../lists/ListTchStudents';
const trigger =
<Button
style={{marginLeft:'2rem'}}
tooltip="Add More..."
tooltipOptions={{
position: 'top'
}}
className="btn-small red darken-4">
<i className="material-icons center ">add_box</i>
</Button>;
const MdlAddStudents =({teacher}) => {
const [data, setData] = useState('');
const { tchStudents} = data;
const {
tchID,
} = teacher; // IF WE RENDER THIS IT TURNS INTO OBJECT
const assignedStudent = () => {
// BUT WE SENT IT TO THE DATABASE CONVERT TO JSON.STRINGIFY to make ObjectId
const requestOpt = {
method: 'PUT',
headers: { 'Content-Type': 'application/json'},
body: JSON.stringify(data)
}
axios.put(`${URL}teachers/assignAddStudents/${tchID}`, data,requestOpt)
.then(res => {
setData(res.data.data)
})
}
return (
<Modal header="Add Students" trigger={trigger}>
Please ADD and REMOVE Student ID No. for {tchID}
<div>
<ul
style={{marginBottom:'2rem'}}
className="collection">
{
Object.values(teacher.tchStudents).map(tchStudent => {
return(
<ListTchStudents
tchStudent={tchStudent}
/>
);
})
}
</ul>
<div className="row">
<div className="col s6 offset-s3"></div>
<div className="input-field">
<label
htmlFor=""
className="active black-text"
style={{fontSize:'1.3rem'}}>
Add Students here:
</label>
<input
type="text"
name="tchStudents"
value={(tchStudents)}
className="validate"
onChange={(e) => setData(e.target.value)}
/>
</div>
</div>
</div>
{/* BUT WE SENT IT TO THE DATABASE CONVERT TO JSON.STRINGIFY to send ObjectId to the database
*/}
<div className="row">
<div className="col s2 offset-s3" ></div>
<Button
onClick={assignedStudent}
tooltip="Add Students"
tooltipOptions={{
position: 'right'
}}
className="btn green darken-3">
<i className="material-icons ">add_circle</i>
</Button>
</div>
<p>There are {Object.values(teacher.tchStudents).length} student/s
assigned</p>
</Modal>
)
}
// MdlAddStudents.propTypes = {
// assignedStudents: PropTypes.func.isRequired,
// }
export default MdlAddStudents;
// export default connect(null, (assignedStudents))(MdlAddStudents);
Thank you for helping out
The problem stems from you attempting to wrap your tchStudents state property in an object named data.
My advice is to keep it very simple
// it's just a string
const [tchStudents, setTchStudents] = useState("")
const assignedStudent = () => {
// create your request payload
const data = { tchStudents }
// no config object required
axios.put(`${URL}teachers/assignAddStudents/${tchID}`, data)
.then(res => {
// not sure what you want to do here exactly but
// `res.data` should look like
// { success: true, msg: 'Student ID has been assigned' }
setTchStudents("") // clear the input ¯\_(ツ)_/¯
})
}
The only other change is to use the new setter name in your <input>...
<input
type="text"
name="tchStudents"
value={tchStudents}
className="validate"
onChange={(e) => setTchStudents(e.target.value)}
/>
I'm trying to build a website where user post queries and other users are able to answer in it. I'm trying to implement various tags in it using react-tags-input module. However i'm having problem in sending those tags to node server. Help needed.
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Link } from 'react-router-dom';
import '../Login.css';
import "./Home.css";
import Post from "./Post";
import { WithContext as ReactTags } from 'react-tag-input';
const KeyCodes = {
comma: 188,
enter: 13,
};
const delimiters = [KeyCodes.comma, KeyCodes.enter];
class query extends Component {
constructor() {
super();
this.state = {
userquery:'',
queries:[],
tags: [],
suggestions: [
{ id: "Javascript", text: "Javascript" },
{ id: "Java", text: "Java" },
{ id: 'node.js', text: 'node.js' },
{ id: 'react.js', text: 'react.js' },
{ id: 'express', text: 'express' },
{ id: 'bootstrap', text: 'bootstrap' },
{ id: 'python', text: 'python' },
{ id: 'c++', text: 'c++' }
]
};
this.handleDelete = this.handleDelete.bind(this);
this.handleAddition = this.handleAddition.bind(this);
this.handleDrag = this.handleDrag.bind(this);
}
onChange = (e) => {
const state = this.state
state[e.target.name] = e.target.value;
this.setState(state);
}
componentDidMount(){
fetch('/query').
then((Response)=>Response.json()).
then(data =>{
this.setState({queries:data.reverse()});
})
}
handleDelete(i) {
const { tags } = this.state;
this.setState({
tags: tags.filter((tag, index) => index !== i),
});
}
handleAddition(tag) {
this.setState(state => ({ tags: [...state.tags, tag] }));
}
handleDrag(tag, currPos, newPos) {
const tags = [...this.state.tags];
const newTags = tags.slice();
newTags.splice(currPos, 1);
newTags.splice(newPos, 0, tag);
// re-render
this.setState({ tags: newTags });
}
render() {
const {userquery } = this.state;
const { tags, suggestions } = this.state;
return (
<div class="container">
<form action="/queries" method="POST">
<h2 class="form-signin-heading" color="blue">Want to ask something? ask here!</h2>
<label for="inputQuery" class="sr-only">query</label>
<textarea type="text" class="form-control" placeholder="want to ask something? ask here!" name="userquery" required/>
<br/>
<div class="form-group ">
<input class="form-control" type="text" name="image" placeholder="image url"/>
</div>
<div class="form-group ">
<ReactTags
name='tags'
tags={tags}
suggestions={suggestions}
handleDelete={this.handleDelete}
handleAddition={this.handleAddition}
handleDrag={this.handleDrag}
delimiters={delimiters}
/>
</div>
<br/>
<button class="btn btn-lg btn-primary btn-block" >Ask</button>
</form>
<section>
<h2> Recent Posts</h2>
</section>
{this.state.queries.map((item, key) => {
return (<Post item={item} key={key} />)
}
)
}
</div>
);
}
}
export default query;
server file - I'm able to get userquery and image but req.body.tags is returning an empty object.
app.post("/queries",isLoggedIn,function(req,res){
var postQuery =req.body.userquery;
var userImages =req.body.image;
console.log(req.body.tags);
username=req.user.username;
var newQuery = {
name:username,
image:userImages,
description:postQuery
}
Query.create(newQuery,function(err,newlyCreated){
if(err){
console.log(err);
}
else{
res.redirect("http://localhost:3000/home");
}
})
// res.send("you hit the post route")
});
Edited
onSubmit = e => {
e.preventDefault()
const {someText, tags} = this.state
const data = {someText, tags: tags.map(x => x.id)}
alert(`Submitting: ${JSON.stringify(data)}`)
fetch(
'queries',
{
method: 'POST',
mode: "cors", // no-cors, cors, *same-origin
cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
credentials: "same-origin",
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
},
)
}
server.js
app.post("/queries",function(req,res){
console.log("tags are:-",req.body)
});
output
tags are:- {}
The problem is in react-tags storing selected tags as a bunch of span elements, not input. So, these values are not included in form data being submitted.
You have to handle form submit logic manually.
Here is an example of handlers you need to add/change:
onSubmit = e => {
e.preventDefault() // stop default form submission
const { field1, field2, tags } = this.state // select form input values to submit
const data = { field1, field2, tags: tags.map(t => t.id) } // create an object to submit
fetch(
'url',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
},
) // send POST request
}
onChange = e => this.setState({ [e.target.name]: e.target.value }) // for plain inputs
onAdd = tag => this.setState(state => ({ tags: [...state.tags, tag] })) // for tags input
render() {
return (
<form onSubmit={this.onSubmit}>
<input name="field1" onChange={this.onChange} />
<input name="field2" onChange={this.onChange} />
<ReactTags
tags={this.state.tags}
handleAddition={this.onAdd}
/>
<button type="submit">submit</button>
</form>
)
}
Working example is in this sandbox.
Result is here: https://prnt.sc/kg2j9p
Update: now codesandbox uses fetch and posts the form data, you can try to submit form and see outgoing request in network tab in browser dev tools.
Im using MEAN (MongoDB Express Angular Node) for binding my drop down in angular 2 with MonogDB values in the backend.
I have routes and models in node JS as follows:
var\www\html\Express\nodeauthapp-master\routes\categories.js
//Get all categories
router.get('/get',(req,res,next) => {
//res.send('Get categories');
Categories.getAllCategories((err,categories)=>{
if(err)
{
res.json({success: false, msg:'Failed to get categories'});
}
else
{
res.json({success: true, mainCategories:categories});
}
});
})
\var\www\html\Express\nodeauthapp-master\models\categories.js
// Categories Schema
const CategoriesSchema = mongoose.Schema({
category_name: {
type: String,
required: true
}
});
const Categories = module.exports = mongoose.model('Categories', CategoriesSchema);
//Get all categories
module.exports.getAllCategories = function(callback){
Categories.find({},callback)
}
IN Angular 2 im binding drop down like:
blank-page.component.html
<form role="form">
<fieldset class="form-group">
<label>Select Category</label><br/><br/>
<select [(ngModel)]="selectedObject" name="selectedObject" class="form-control">
<option disabled>--Select--</option>
<option *ngFor="let category of categories" [value]="category">{{category}}</option>
</select>
</fieldset>
</form>
blank-page.component.ts
export class BlankPageComponent implements OnInit {
category:String;
public categories: Array<any> = [];
constructor(private addproductService: AddproductService,
private flashMessage: FlashMessagesService,
private router: Router) { }
ngOnInit() {
const category = {
categories: this.category
}
console.log(category);
this.addproductService.loadData(category).subscribe(data => {
if(data.success){
this.categories = data.mainCategories;
console.log('Drop down binded');
} else {
console.log('Not binded');
}
});
}
addproduct.service.ts
export class AddproductService {
category: any;
loadData(category) {
let headers = new Headers();
headers.append('Content-Type', 'application/json');
return this.http.get('http://10.*.*.*:3000/categories/get', { headers: headers })
.map(res => res.json());
}
}
I Get Drop down binded from the console log , but in frontend, there seems to be no values binded.
When i hit the GET API url in postman, i get the categories list.
In the browser logs i get : Object {categories: undefined}
My object has :
Change your HTML code to this :
<form role="form">
<fieldset class="form-group">
<label>Select Category</label><br/><br/>
<select [(ngModel)]="selectedObject" name="selectedObject" class="form-control">
<option disabled>--Select--</option>
<option *ngFor="let category of categories" [value]="category.category_name">{{category.category_name}}</option>
</select>
</fieldset>
</form>
I have an application that will include Topics which contain Articles. When the user creates an article, I want them to select a topic to associate the article with from a dropdown that presents the available topics. The UI side of this presents the user with a form that looks correct at the moment, however, the topic object is not being passed with the form and I cant understand why. Can someone please help me understand how this should be done?
the relevant pieces here are below:
This is the select statement in my form that properly displays the options for topics that I want to present to the user.
<select [ngFormControl]="myForm.find('topic')" id="topic" class="form-control" required>
<option *ngFor="let a of topics" [value]="a">{{a.title}}</option>
</select>
When I try to verify that I received the data I'm looking for I get an 'undefined' error with this line:
console.log(this.myForm.value.topic.title);
If I do this I get [object, object]
console.log(this.myForm.value.topic);
what gets submitted to the service is this:
Object { content: "8th content", title: "8th title", articleId: "57588eaf1ac787521d15ac34", username: "Jason", userId: Object, topicId: undefined }
Can someone please help me understand what I'm missing here to be able to send the result of this form select tag into my Angular2 form?
My entire article-input.component.ts file
import { Component, OnInit } from '#angular/core';
import {Article} from './article';
import {ArticleService} from "./article.service";
import {ErrorService} from "../errors/error.service";
import { FormBuilder, ControlGroup, Validators, Control } from '#angular/common';
import {TopicService} from "../topics/topic.service";
import {Topic} from "../topics/topic";
#Component({
selector: 'my-article-input',
template: `
<section class="col-md-8 col-md-offset-2">
<form [ngFormModel]="myForm" (ngSubmit)="onSubmit()">
<div class="form-group">
<label for="title">Title</label>
<input [ngFormControl]="myForm.find('title')" type="text" id="title" class="form-control" #input [value]="article?.title">
</div>
<div class="form-group">
<label for="content">Content</label>
<input [ngFormControl]="myForm.find('content')" type="text" id="content" class="form-control" #input [value]="article?.content">
</div>
<select [ngFormControl]="myForm.find('topic')" id="topic" class="form-control" required>
<option *ngFor="let a of topics" [value]="a">{{a.title}}</option>
</select>
<button type="submit" class="btn btn-primary" [disabled]="!myForm.valid">{{ !article ? 'Add Article' : 'Save Article' }}</button>
<button type="button" class="btn btn-danger" (click)="onCancel()" *ngIf="article">Cancel</button>
</form>
</section>
`
})
export class ArticleInputComponent implements OnInit {
myForm: ControlGroup;
article: Article = null;
constructor(private _fb:FormBuilder, private _articleService: ArticleService, private _errorService: ErrorService, private _topicService: TopicService ) {}
topics: Topic[];
onSubmit() {
console.log(this.myForm.value.topic.title);
if (this.article) {
// Edit
this.article.content = this.myForm.value.content;
this.article.title = this.myForm.value.title;
this.article.topicId = this.myForm.value.topic.topicId;
this._articleService.updateArticle(this.article)
.subscribe(
data => console.log(data),
error => this._errorService.handleError(error)
);
this.article = null;
} else {
const article: Article = new Article(this.myForm.value.content, this.myForm.value.title, null, 'DummyArticleInput', this.myForm.value.topic);
this._articleService.addArticle(article)
.subscribe(
data => {
console.log(data);
this._articleService.articles.push(data);
},
error => this._errorService.handleError(error)
);
}
}
onCancel() {
this.article = null;
}
ngOnInit() {
this.myForm = this._fb.group({
title: ['', Validators.required],
content: ['', Validators.required],
topic: ['', Validators.required]
});
this._articleService.articleIsEdit.subscribe(
article => {
this.article = article;
}
);
this._topicService.getTopics().subscribe(
topics => {
this.topics = topics;
this._topicService.topics = topics
},
error => this._errorService.handleError(error)
);
}
}
So the answer to this is to not use the myForm element with ngFormControl for objects/form field that are going to be references to other objects within the application. Instead what needed to be done was as follows.
Create topics, topic, and a selectedTopic
topics: Topic[];
topic = '';
selectedTopic = '';
Add a select tag with an option tag to the form for a user to be able to select the topic they want by binding with ngModel to 'topic'
<select [ngModel]="topic" (ngModelChange)="onChange($event)">
<option [ngValue]="i" *ngFor="let i of topics">{{i.title}}</option>
</select>
Create an onChange method that will update this.selectedTopic based on the user selecting a topic in the form
onChange(newValue) {
this.selectedTopic = newValue;
console.log(this.selectedTopic);
console.log(this.selectedTopic.topicId);
}
Then in the onSubmit method use the myForm.value's for the info coming from the actual form, and use the databinding on this.selectedTopic for the topic that is being selected
onSubmit() {
if (this.article) {
// Edit
this.article.content = this.myForm.value.content;
this.article.title = this.myForm.value.title;
this.article.topicId = this.selectedTopic;
this._articleService.updateArticle(this.article)
.subscribe(
data => console.log(data),
error => this._errorService.handleError(error)
);
this.article = null;
} else {
const article: Article = new Article(this.myForm.value.content, this.myForm.value.title, null, 'dummyUserName', 'dummyUserId', this.selectedTopic.topicId);
this._articleService.addArticle(article)
.subscribe(
data => {
// console.log('what comes back from addArticle is: ' + JSON.stringify(data));
this._articleService.articles.push(data);
},
error => this._errorService.handleError(error)
);
}
From where we started, we have now gotten to a place where everything works and the object getting created looks like this:
{ "_id" : ObjectId("5758c2e173fd33e04092c87e"), "content" : "c66", "title" : "t66", "user" : ObjectId("5755e5be96162f52a4f01dd8"), "topic" : ObjectId("57572d92e802307d199f0afa"), "__v" : 0 }
For reference, the entire (working) article-input.component.ts file looks like this:
import { Component, OnInit } from '#angular/core';
import {Article} from './article';
import {ArticleService} from "./article.service";
import {ErrorService} from "../errors/error.service";
import { FormBuilder, ControlGroup, Validators, Control } from '#angular/common';
import {TopicService} from "../topics/topic.service";
import {Topic} from "../topics/topic";
#Component({
selector: 'my-article-input',
template: `
<section class="col-md-8 col-md-offset-2">
<form [ngFormModel]="myForm" (ngSubmit)="onSubmit()">
<div class="form-group">
<label for="title">Title</label>
<input [ngFormControl]="myForm.find('title')" type="text" id="title" class="form-control" #input [value]="article?.title">
</div>
<div class="form-group">
<label for="content">Content</label>
<input [ngFormControl]="myForm.find('content')" type="text" id="content" class="form-control" #input [value]="article?.content">
</div>
<select [ngModel]="topic" (ngModelChange)="onChange($event)">
<option [ngValue]="i" *ngFor="let i of topics">{{i.title}}</option>
</select>
<button type="submit" class="btn btn-primary" >{{ !article ? 'Add Article' : 'Save Article' }}</button>
<button type="button" class="btn btn-danger" (click)="onCancel()" *ngIf="article">Cancel</button>
</form>
</section>
`
})
export class ArticleInputComponent implements OnInit {
myForm: ControlGroup;
article: Article = null;
constructor(private _fb:FormBuilder, private _articleService: ArticleService, private _errorService: ErrorService, private _topicService: TopicService ) {}
topics: Topic[];
topic = '';
selectedTopic = '';
onChange(newValue) {
this.selectedTopic = newValue;
console.log(this.selectedTopic);
console.log(this.selectedTopic.topicId);
}
onSubmit() {
if (this.article) {
// Edit
this.article.content = this.myForm.value.content;
this.article.title = this.myForm.value.title;
this.article.topicId = this.selectedTopic;
this._articleService.updateArticle(this.article)
.subscribe(
data => console.log(data),
error => this._errorService.handleError(error)
);
this.article = null;
} else {
const article: Article = new Article(this.myForm.value.content, this.myForm.value.title, null, 'dummyUserName', 'dummyUserId', this.selectedTopic.topicId);
this._articleService.addArticle(article)
.subscribe(
data => {
// console.log('what comes back from addArticle is: ' + JSON.stringify(data));
this._articleService.articles.push(data);
},
error => this._errorService.handleError(error)
);
}
}
onCancel() {
this.article = null;
}
ngOnInit() {
this.myForm = this._fb.group({
title: ['', Validators.required],
content: ['', Validators.required],
topic: ['', Validators.required]
});
this._articleService.articleIsEdit.subscribe(
article => {
this.article = article;
}
);
this._topicService.getTopics().subscribe(
topics => {
this.topics = topics;
this._topicService.topics = topics
},
error => this._errorService.handleError(error)
);
}
}
my topic.ts angular model looks like this:
export class Topic {
description: string;
title: string;
username: string;
topicId: string;
userId: string;
constructor (description: string, title: string, topicId?: string, username?: string, userId?: string) {
this.description = description;
this.title = title;
this.topicId = topicId;
this.username = username;
this.userId = userId;
}
}
the article.service.ts looks like this:
addArticle(article: Article) {
const body = JSON.stringify(article);
console.log(body);
const headers = new Headers({'Content-Type': 'application/json'});
const token = localStorage.getItem('token') ? '?token=' + localStorage.getItem('token') : '';
return this._http.post('http://localhost:3000/article' + token, body, {headers: headers})
.map(response => {
const data = response.json().obj;
let article = new Article(data.content, data.title, data._id, data.user.firstName, data.user, data.topicId);
return article;
})
.catch(error => Observable.throw(error.json()));
}
an in the backend in Node my article.js looks like this:
router.post('/', function(req, res, next) {
var decoded = jwt.decode(req.query.token);
User.findById(decoded.user._id, function(err, doc) {
if (err) {
return res.status(401).json({
title: 'An Error occured',
error: err
});
}
var article = new Article({
content: req.body.content,
title: req.body.title,
user: doc,
topic: req.body.topicId
});
console.log(req.body);
article.save(function(err, result){
if (err) {
return res.status(404).json({
title: 'An Error occured',
error: err
});
}
doc.articles.push(result);
doc.save();
res.status(201).json({
article: 'Saved Article',
obj: result
});
});
});
});
I hope this helps others who had the same issue as I did.