Rails + Vue Unexpected token u in JSON at position 0 - ruby-on-rails

Even though rails 6 is still in beta I thought to test it out building a rails + vue app but when trying to parse the json data im getting a error in the console "VM353:1 Uncaught SyntaxError: Unexpected token u in JSON at position 0" Not sure why my data is not being parsed. Undefined but cant figure out why
Here is my hello_vue.js file
import Vue from 'vue/dist/vue.esm'
import VueResource from 'vue-resource'
Vue.use(VueResource)
document.addEventListener('DOMContentLoaded', () => {
Vue.http.headers.common['X-CSRF-Token'] = document.querySelector('meta[name="csrf-token"]').getAttribute('content')
var element = document.getElementById("gameapp-form")
if(element != null) {
var game_application = JSON.parse(element.dataset.game_application)
var app = new Vue({
el: element,
data: function () {
return { game_application: game_application}
},
methods: {
saveApplication: function() {
this.$http.post('/game_applications', {game_application: this.game_application }).then(response => {
console.log(response)
}, response => {
console.log(response)
})
}
}
})
}
}
)
Here is my _form.html.erb file
<%= content_tag :div,
id: "gameapp-form",
data: {
game_application: game_application.to_json(except: [:created_at, :updated_at]),
} do %>
<label>Game Name</label>
<input type="text" v-model="game_application.name" />
<label>Game Name</label>
<input type="text" v-model="game_application.video_link" />
<button v-on:click="saveApplication">Send Application</button>
<% end %>

Use response.data with your axios request
this.$http.post('/game_applications', {game_application: this.game_application})
.then(response => response.data)
.then(response => {
console.log(response)
})
Axios returns your JSON output in the data field. So, in the first then we are basically returning response.data so that you can use it in the next then. Hope that makes sense.

Related

Creating a Review Rails Backend/React Frontend

I am attempting to allow a user that is logged in to create a review for a game. I am having a couple issues that keep popping up in my console.
HTTP Origin header (http://localhost:3001) didn't match request.base_url (http://localhost:3000)
I attempted to remedy this with putting config.force_ssl = true in my production file from what I read up on, but still hitting this issue.
Im also hitting
NoMethodError (undefined method `id' for nil:NilClass):
which is ref my review_controller in create
app/controllers/reviews_controller.rb:19:in `create'
Below is my ReviewController and my ReviewContainer and ReviewForm
class ReviewsController < ApplicationController
# before_action :authorized, only:[:create]
before_action :authenticate_with_http_digest, only: [:new, :create]
def index
reviews = Review.all
render json: reviews
end
def show
review = Review.find_by(params[:id])
render json: review
end
def create
game = Game.find_or_create_by(name: params[:review][:game_name])
review = Review.new(review_params)
review.game_id = game.id
review.user_id = #user.id
review.save
render json: review
end
def update
review = Review.find(params[:id])
review.update(review_params)
review.save
render json: review
end
def destroy
review = Review.find(params[:id])
review.destroy
render json: {error: "Review Removed"}
end
private
def review_params
params.require(:review).permit(:user_id, :game_id, :user_username, :reviewed_game, :rating, :game_name)
end
end
import React, { Component } from 'react'
import Review from './Review'
import ReviewForm from './ReviewForm'
export default class ReviewsContainer extends Component {
state = {
reviews: [],
}
componentDidMount(){
fetch('http://localhost:3000/reviews')
.then(res => res.json())
.then(reviews => this.setState({ reviews }))
}
addReview = (review) => {
fetch('http://localhost:3000/reviews',{
method: "POST",
headers: {
"Content-Type" : "application/json",
Accept: "application/json",
Authorization: `bearer ${localStorage.token}`
},
body: JSON.stringify({ review: review }
),
})
.then(res => res.json())
.then(( json => {
this.setState(prevState => ({
reviews: [...prevState.reviews, json ]
}))
}))
}
// handleShowForm = () => {
// this.setState({edit: false})
// }
render() {
return (
<div className="review-grid">
<ReviewForm addReview={this.addReview} review={this.handleSubmit} />
<h1 className="review-content">REVIEWS!</h1>
<ul className="review-cards">
{
this.state.reviews.map(review => <Review key={review.id} review={review}/>)
}
</ul>
</div>
)
}
}
import React, { Component } from 'react'
class ReviewForm extends React.Component {
state = {
reviewed_game: '',
rating: '',
user_username: '',
}
handleReviewedGame = (event) => {
this.setState ({
reviewed_game: event.target.value
})
}
handleRating = (event) => {
this.setState ({
rating: event.target.value
})
}
handleUser = (event) => {
this.setState ({
user_username: event.target.value
})
}
handleForm = (e) => {
e.preventDefault()
// console.log(e)
const review = {
reviewed_game: this.state.reviewed_game,
rating: this.state.rating,
}
this.props.addReview(review)
}
render() {
return (
<div className="form-container">
<form onSubmit={(e) => {this.handleForm(e)}}>
<div>
<label>Review</label>
<br></br>
<textarea type="text" placeholder="Drop Your Review" rows={10} cols={50} value={this.state.reviewed_game} onChange={this.handleReviewedGame} className="form"/>
<div>
<label>Stars</label>
<br></br>
<input type="number" max="5" min="0" value={this.state.rating} onChange={this.handleRating} />
</div>
</div>
<button type="submit" className="sub-review">Create Review!</button>
</form>
</div>
)
}
}
export default ReviewForm;
Any advise on how to correct the issue is appreciated! thanks!

Rails post controller not passing user association to React view

I'm trying to make a basic CRUD store app with Rails and React, but I'm stuck on displaying the author (user) association of the post. The post itself shows just fine. I'm trying to avoid using jbuilder so I can understand the problem I'm having.
The current show method in the controller, which works:
controllers/post_controller.rb
def show
if post
render json: post
else
render json: post.errors
end
end
The current React view, which works:
app/javascript/components/Post.js
import React from "react";
import { Link } from "react-router-dom";
class Post extends React.Component {
constructor(props) {
super(props);
this.state = { post: { description : '' } };
}
componentDidMount() {
const {
match: {
params: { id }
}
} = this.props;
const url = `/api/v1/show/${id}`;
fetch(url)
.then(response => {
if (response.ok) {
return response.json();
}
throw new Error("Network response was not ok.");
})
.then(response => this.setState({ post: response }))
.catch(() => this.props.history.push("/posts"));
}
render() {
const { post } = this.state;
let descriptionList = "No descriptions present";
if (post.description.length > 0) {
descriptionList = post.description
.split(",")
.map((description, index) => (
<li key={index} className="list-group-item">
{description}
</li>
));
}
return (
<div className="">
<div className="hero position-relative d-flex align-items-center justify-content-center">
<img
src={post.image}
alt={`${post.description} image`}
className="img-fluid position-absolute"
/>
<div className="overlay bg-dark position-absolute" />
</div>
<div className="container py-5">
<div className="row">
<div className="col-sm-12 col-lg-3">
<ul className="list-group">
<h5 className="mb-2">Description</h5>
{descriptionList}
<div>{post.title}</div>
<div>${(post.price * .01).toLocaleString()}</div>
</ul>
</div>
</div>
<Link to="/posts" className="btn btn-link">
Back to all posts
</Link>
</div>
</div>
);
}
}
export default Post;
When I add render json: post, include: :user to the controller and {post.user.email} and render() { const { post, user } = this.state;
to the view, the error message in the console is cannot read property 'email' of undefined. When I try to define the user in the controller method user = post.user.email and in the view {user}, the terminal error is:
NoMethodError (undefined method 'oswaldo#daugherty.info' for #<Post id: 5, title: "Post 5", description: "You can't synthesize the bandwidth without compres...", image: "https://loremflickr.com/300/300/cats 5", price: 883105, rating: nil, review: nil, created_at: "2021-01-31 23:26:03", updated_at: "2021-01-31 23:26:03", user_id: 5>):
I've checked my database and all the associations display correct there. In short, I don't know how to send the post's user association correctly to the view. What am I missing? Any help appreciated because I'm really spinning my wheels on this one.
you might be facing the bug reported bug.
If you are only looking email from related user record, you can use following
# in Post model
delegate :email, to: :user, prefix: true, allow_nil: true
# and while rendering
use Post.as_json(methods: [:user_email])

Unpermitted parameter: :recipe

I am having an issue with submitting my data from form on the front end. Everytime I submit the form I get a Unpermitted parameter: :recipe
I was told to make sure my attributes on my frontend matched what was on the backend strong params.
Here is what is in my controller for my create action and my strong params
class RecipesController < ApplicationController
def create
recipe = Recipe.create(recipe_params)
if recipe.save
render json: recipe
else
render json: { error: "Couldn't save" }
end
end
private
def recipe_params
params.permit(:category_id,:name,:ingredients,:chef_name,:origin,category_attribute:[:category])
end
end
And here is my React frontend where I am inputting the info in the form
Side note I took out the event handlers for this code snippet but left the submit handler just to keep this explanation shorter
import React, { Component } from 'react'
import Categories from './Categories.js'
class RecipeInput extends Component{
constructor(props){
super(props)
this.state = {
category: [],
categoryId: '',
name:'',
ingredients: '',
chef_name: '',
origin: ''
}
this.handleNameChange.bind(this)
this.handleOriginChange.bind(this)
this.handleChefChange.bind(this)
this.handleIngChange.bind(this)
}
componentDidMount(){
let initialCats = [];
const BASE_URL = `http://localhost:10524`
const CATEGOREIS_URL =`${BASE_URL}/categories`
fetch(CATEGOREIS_URL)
.then(resp => resp.json())
.then(data => {
initialCats = data.map((category) => {
return category
})
console.log(initialCats)
this.setState({
category: initialCats,
})
});
}
handleSubmit = (event) =>{
event.preventDefault();
this.props.postRecipes(this.state)
this.setState({
categoryId: '',
name:'',
ingredients: '',
chef_name: '',
origin: ''
})
}
render(){
return(
<div>
<form onSubmit={this.handleSubmit}>
<Categories category={this.state.category}/>
<div>
<label for='name'>Recipe Name:</label>
<input type='text' value={this.state.name} onChange={this.handleNameChange} />
</div>
<div>
<label for='name'>Country Origin:</label>
<input type='text' value={this.state.origin} onChange={this.handleOriginChange} />
</div>
<div>
<label for='name'>Chef Name:</label>
<input type='text' value={this.state.chef_name} onChange={this.handleChefChange} />
</div>
<div>
<label for='name'>Ingredients:</label>
<textarea value={this.state.ingredients} onChange={this.handleIngChange} />
</div>
<input value='submit' type='submit'/>
</form>
</div>
)
}
}
export default RecipeInput
I am just a little clueless on where to go to from here. Am I matching the attributes correctly?
Edit
I forgot to include my postRecipes function with my dispatches in place
export const postRecipes = (recipe)=>{
const BASE_URL = `http://localhost:10524`
const RECIPES_URL =`${BASE_URL}/recipes`
const config = {
method: "POST",
body:JSON.stringify(recipe),
headers: {
"Accept": "application/json",
"Content-type": "application/json"
}
}
//category field
return(dispatch)=>{
fetch(RECIPES_URL,config)
.then(response =>
response.json())
.then(resp => {
dispatch({
type: 'Add_Recipe',
payload:{
category:resp.category,
name: resp.name,
ingredients: resp.ingredients,
chef_name: resp.chef_name,
origin: resp.origin,
categoryId: resp.categoryId
}
})
})
//.then(response => <Recipe />)
.catch((error) => console.log.error(error))
}
}
Edit
Here is my reducer that defines my Add-Recipe action for payload.
export default function manageRecipes(state={
recipes:[],
category:[],
}, action){
switch(action.type){
case 'Add_Recipe':
const recipe = {
name: action.name,
ingredients: action.ingredients,
chef_name: action.chef_name,
origin: action.origin,
categoryId: action.categoryId,
category: action.category,
// id: cuidFn()
}
return{
...state,
recipes: [...state.recipes, recipe],
}
case 'Delete_Recipe':
const recipes = state.recipes.filter(recipe => recipe.id !== action.id)
return {...state, recipes}
case 'Add_Catagory':
const cat = {
name: action.name
}
return{
...state,
category: [...state.category, cat],
}
default:
return state
}
}
try this in your backend
def recipe_params
params.require(:recipe).permit(
:category_id, :name,:ingredients, :chef_name,
:origin,category_attribute:[:category]
)
end

how to delete a record in rails API and vue Js

i am trying to delete an ActiveRecord form rails API and Vue js using the axios library. I can fetch and add records without any issues .The issue I have is deleting a record
Here is my code
rails API
.....
# DELETE v1/customers/1
def destroy
#customer = Customer.find(params[:id])
#customer.destroy
end
Vue Js
........
<tr v-for="customer in customers" :key="customer.id">
<td>{{customer.first_name}}</td>
<td>{{customer.last_name}}</td>
<td>{{customer.email}}</td>
<td>{{customer.id}}</td>
<td><button #click="removecustomer(customer.id)"
type="button" class="btn btn-outline-danger btn-sm">Delete</button></td>
export default {
name: 'customers',
data (){
return{
customers:[]
}
},
created (){
this.getallcustomers()
},
methods: {
getallcustomers () {
let uri = 'http://localhost:3000/v1/customers';
this.axios.get(uri).then((response) => {
this.customers = response.data;
console.log(this.customers);
});
},
removecustomer(id){
let uri = `http://localhost:3000/v1/customers/${customer.id}`;
this.axios.delete(uri).then((res) => {
this.customers = this.customers.filter(customer => customer.id !== id);
});
}
}
}
So my removecustomer methods produce an error customer is not defined"
I need help
You are only passing only customer id as paramteter (id) to the removecustomer function and not the entire customer object, thus, you are getting an undefined error.
Replace:
let uri = `http://localhost:3000/v1/customers/${customer.id}`
With:
let uri = `http://localhost:3000/v1/customers/` + id

Rails + React js + react-rails gem submit form with single component

I am trying to submit form in react js with Rails.
I am new to React js and it is my first app
I am getting error No route matches [POST] "/"
Using single component(jsx) to submit the form.I am getting routing error.
Following is my code
EDIT
I changed the route and now I got error "InvalidAuthenticityToken in ItemsController#create"
How can i raise or alert variable in in jsx file ?
I add following route in route.rb
resources :items
root :to => redirect("/items")
ItemsController
def index
#presenter = { :action => items_path,
:csrf_token => request_forgery_protection_token,
:csrf_param => form_authenticity_token
}
end
def create
#item = Item.new(item_params)
#item.save
end
private
def item_params
params.require(:item).permit(:name, :price)
end
Index.html.erb
<%= react_component('Form1', {:presenter => #presenter.to_json}, {:presenter => true})%>
Form1.js.jsx
var Form1 = React.createClass({
handeSubmit: function( e ){
e.preventDefault();
// var form = e.target;
// var name = form.querySelector('[name="item[name]"]').value;
// var price = form.queySelector('[name="item[price]"]').value;
var name = this.refs.name.getDOMNode().value.trim();
var price = this.refs.price.getDOMNode().value.trim();
if(!name || !price)
{
return false;
}
var formData = $( this.refs.form.getDOMNode() ).serialize();
var action = this.props.presenter.action
// alert({formData});
$.ajax({
data: formData,
url: action,
type: "POST",
dataType: "json",
});
},
render: function(){
return (
<form ref="form" className="" action={ this.props.presenter.action } acceptCharset="UTF-8" method="post" onSubmit={this.handleSubmit} >
<input type="hidden" name={ this.props.presenter.csrf_param } value={ this.props.presenter.csrf_token } />
<input ref="name" name="item[name]" /><br/>
<input ref="price" name="item[price]" /><br/>
<button type="submit"> Submit</button>
</form>
)
}
});
It looks like, for AJAX requests, you should send the CSRF token as a header, not as a form field.
(Docs: http://api.rubyonrails.org/classes/ActionView/Helpers/CsrfHelper.html#method-i-csrf_meta_tags)
Here's how you could add that header in your case:
var csrfToken = this.props.presenter.csrf_token;
$.ajax({
data: formData,
url: action,
type: "POST",
dataType: "json",
// Before sending, add the CSRF header:
beforeSend: function(xhr) {
xhr.setRequestHeader('X-CSRF-Token', csrfToken);
},
});
Does that work for you?
By the way, one way I work around this is by using react_component for form fields, but using Rails' form_for to make the actual <form> tag. For example,
<%= form_for #item, remote: true do |f| %>
<!-- Rails will add CSRF token -->
<%= react_component("ItemFormFields", item: #item) %>
<% end %>

Resources