Getting undefined parrameters i passed from another component - ruby-on-rails

Okay, so, i have this Post component and i want to pass the id to the postView component, and then from there to the postPage, the problem is whatever i pass it gives me undefined, as im new to vue i have been bashing my head about this a whole day and have no idea how to go around it.
Thanks in forward!
Post component:
<template>
<div class="mb-3">
<ul v-for="post in posts" :key="post.id">
<div #click="goToPost(post.id)" class="byPost card">
<li class="card-tittle">{{ post.headline }}</li>
<li class="card-text">{{ post.description }}</li>
</div>
</ul>
</div>
</template>
<script>
import axios from "axios";
export default {
props: ["endpoint"],
data() {
return {
posts: [],
};
},
methods: {
goToPost(id){
this.$router.push({ name: 'post', params: { id } });
}
},
mounted() {
axios
.get(this.endpoint)
.then((response) => (this.posts = response.data))
.catch((error) => console.log(error));
},
};
</script>
PostView component:
<template>
<div class="home">
<PostPage
:headline="post.headline"
:description="post.description"
:likes="post.likes"
:topic="post.topic"
/>
</div>
</template>
<script>
import PostPage from "#/components/pages/post-page.vue";
import axios from "axios";
export default {
name: "Post",
data() {
return {
id: this.$route.params.id,
post: {}
};
},
components: {
PostPage,
},
mounted(){
axios.get(`http://localhost:3000/api/project_pages/${this.id}`)
.then(response => (this.post = response.data))
.catch(error => console.log(error));
}
};
</script>
First started by making the axios call in the post component, didn't go as it will do a good backend call and give me 200ok, but when passed to the postView i would get undefined, if anyone can help im open to hear it!

Related

React: Trying to set selection to drop down option

I setup a component that is basically a drop down and I am trying to figure out how to set it to where when I submit the form....its set on that one option. When I submit it now, it sends all the options to the backend instead of just the one I selected.
Here is my Category component
import React, { Component } from 'react'
class Categories extends Component{
handleCatChange = (event) => {
this.setState({category: event.target.value}) <------this should set the state to whatever is selected
}
render(){
let categories = this.props.category
let value = this.props.value
let optionItems = categories.map((cat,index) => {
return <option key={index} value={value}>{cat.category}</option>
})
return (
<div>
<select onchange={this.handleCatChange} value={this.props.category}>
{this.props.category ? optionItems : <p>Loading....</p>}
</select>
</div>
)
}
}
export default Categories
And here is RecipeInput Component with form
import React, { Component } from 'react'
import Categories from './Categories.js'
class RecipeInput extends Component{
constructor(props){
super(props)
this.state = {
category: [],
name:'',
ingredients: '',
chef_name: '',
origin: ''
}
}
componentDidMount(){
let initialCats = [];
const BASE_URL = `http://localhost:10524`
const CATEGORIES_URL =`${BASE_URL}/categories`
fetch(CATEGORIES_URL)
.then(resp => resp.json())
.then(data => {
initialCats = data.map((category) => {
return category
})
this.setState({
category: initialCats
})
});
}
handleSubmit = (event) =>{
event.preventDefault();
this.props.postRecipes(this.state)
this.setState({
name:'',
ingredients: '',
chef_name: '',
origin: ''
})
}
render(){
return(
<div>
<form onSubmit={this.handleSubmit}>
<Categories category={this.state.category} value={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
And here is the error that is produced on submission(Its Rails btw)
I tired a few ways but haven't quite wrapped my head around using a component as a dropdown. What do I need to do?
Here is my backend code that creates the record on the api
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
Also my postRecipe function
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))
}
}
Code Edit due to change in question:
Access selectedValue while sending to the server
class Categories extends Component {
render() {
...
let optionItems = categories.map((cat, index) => {
return (
<option key={index} value={index}>
{cat.category}
</option>
);
});
...
}
}
class RecipeInput extends Component{
constructor(props){
super(props)
this.state = {
category: [],
name:'',
ingredients: '',
chef_name: '',
origin: ''
selectedValue: {}
}
}
handleSubmit(id){
this.setState({
selectedValue: this.state.category[id]
)}
}
...
}
You're passing onChange and value from Input Component but you're not using them in Categories Component.
Add onChange and value property to tag.
here is reference
import "./styles.css";
import React, { Component } from "react";
class Categories extends Component {
render() {
let categories = this.props.category;
let onChange = this.props.onChange;
let optionItems = categories.map((cat, index) => {
return (
<option key={index} value={cat.category}>
{cat.category}
</option>
);
});
return (
<div>
<select onChange={(e) => onChange(e.target.value)}>
{this.props.category.length ? optionItems : null}
</select>
</div>
);
}
}
export default function App() {
const onChange = (value) => {
console.log(value);
};
return (
<Categories
onChange={onChange}
category={[{ category: "1st" }, { category: "2nd" }]}
/>
);
}
I've updated the code.
If you need to try it online you can refer my Sandbox
https://codesandbox.io/s/stackoverflow-qno-65730813-j32ce

Angular Material navbar - can't see labels

Here is my code for navbar:
<nav mat-tab-nav-bar>
<a mat-tab-link
*ngFor="let link of navLinks"
[routerLink]="link.link"
routerLinkActive #rla="routerLinkActive"
[active]="rla.isActive">
</a>
</nav>
And component:
export class SettingsComponent implements OnInit {
navLinks: any[];
activeLinkIndex = -1;
constructor(private router: Router) {
this.navLinks = [
{
label: 'First',
link: 'syspref',
index: 0
}, {
label: 'Second',
link: 'userpref',
index: 1
}
];
}
ngOnInit(): void {
this.router.events.subscribe((res) => {
this.activeLinkIndex = this.navLinks.indexOf(this.navLinks.find(tab => tab.link === '.' + this.router.url));
});
}
}
It works fine except that I dont see labels. Any idea why?
Thanks
Found it. I was missing {{link.label}}:
<nav mat-tab-nav-bar>
<a mat-tab-link
*ngFor="let link of navLinks"
[routerLink]="link.link"
routerLinkActive #rla="routerLinkActive"
[active]="rla.isActive">{{link.label}}
</a>
</nav>

Click button not triggering "handleSubmit" function in React

I am building a react on rails app. I have a button on the page that user can indicate whether they want to join a meet up or not. Clicking "join" button should create a rsvp relation between the current user and an event, and the button will be switched to "Leave", if user then click on the "Leave" button, this relationship will be deleted from the rails backend. After messing around my react component, my "Join" button doesn't trigger the "onSubmit" function, and the "Leave" button seems to return an error saying "submission form cancelled because form is not connected". I'd appreciated a lot if any one can help me clean my logic.
import React from 'react'
class EventsIndexContainer extends React.Component {
constructor(props) {
super(props)
this.state = {
active: props.rsvp
}
this.toggleButton = this.toggleButton.bind(this)
this.handleRsvpSubmit = this.handleRsvpSubmit.bind(this)
this.handleRsvpDelete = this.handleRsvpDelete.bind(this)
}
toggleButton() {
this.setState({active: !this.state.active})
}
handleRsvpSubmit(event) {
event.preventDefault()
let formPayLoad = {
user_id: this.props.current_user.id,
event_id: this.props.selectedId
}
this.props.addRsvp(formPayLoad)
}
handleRsvpDelete() {
fetch(`/api/v1/rsvps/${this.props.selectedId}`, {
method: 'DELETE'}
)
}
render() {
let button
let joinButton =
<form onSubmit={this.handleRsvpSubmit}>
<button type="button" onClick={() => (this.props.handleSelect(),
this.toggleButton())}>Join</button>
</form>
let leaveButton =
<button type="button" onClick={() => (this.toggleButton(),
this.handleRsvpDelete)}>Leave</button>
button = this.state.active? leaveButton : joinButton
return(
<div>
<h4>{this.props.location} - {this.props.meal_type} at
{this.props.time}</h4>
<p>{this.props.group.name}</p>
{button}
<button>See who is going</button>
</div>
)
}
}
export default EventsIndexContainer
This is the parent container:
import React from 'react'
import GroupIndexContainer from './GroupIndexContainer'
import EventsIndexContainer from './EventsIndexContainer'
class MainContainer extends React.Component {
constructor(props) {
super(props)
this.state = {
groups: [],
current_user: null,
events: [],
rsvps: [],
selectedId: null
}
this.fetchGroups = this.fetchGroups.bind(this)
this.fetchEvents = this.fetchEvents.bind(this)
this.handleSelect = this.handleSelect.bind(this)
this.addRsvp = this.addRsvp.bind(this)
this.fetchRsvps = this.fetchRsvps.bind(this)
}
componentDidMount() {
fetch('api/v1/users.json', {
credentials: 'same-origin',
method: "GET",
headers: { 'Content-Type': 'application/json' }
})
.then(response => response.json())
.then(data => {
this.setState ({current_user: data.user})
})
.then(this.fetchGroups())
.then(this.fetchEvents())
.then(this.fetchRsvps())
}
fetchGroups() {
fetch('/api/v1/groups', {
credentials: 'same-origin',
method: "GET",
headers: { 'Content-Type': 'application/json' }
})
.then(response => response.json())
.then(data => {
this.setState({groups: data.groups})
})
}
fetchEvents() {
fetch('/api/v1/events', {
credentials: 'same-origin',
method: "GET",
headers: { 'Content-Type': 'application/json' }
})
.then(response => response.json())
.then(data => {
this.setState({events: data})
})
}
fetchRsvps() {
fetch('/api/v1/rsvps', {
credentials: 'same-origin',
method: "GET",
headers: { 'Content-Type': 'application/json' }
})
.then(response => response.json())
.then(data => {
this.setState({rsvps: data.rsvps})
})
}
handleSelect(id) {
this.setState({selectedId: id})
}
addRsvp(formPayLoad) {
fetch('/api/v1/rsvps', {
method: 'POST',
credentials: 'same-origin',
headers: { 'Accept': 'application/json', 'Content-Type': 'application/json'},
body: JSON.stringify(formPayLoad)
})
}
render() {
let groups = this.state.groups.map((group) => {
return (
<GroupIndexContainer
key={group.id}
id={group.id}
name={group.name}
/>
)
})
let rsvp_ids = this.state.rsvps.map(rsvp => rsvp.event_id)
let events = this.state.events.map((event) => {
return(
<EventsIndexContainer
key={event.id}
id={event.id}
rsvp={rsvp_ids.some(rsvp_id => rsvp_id == event.id) ? true : false}
location={event.location}
meal_type={event.meal_type}
time={event.time}
group={event.group}
current_user={this.state.current_user}
user={event.user}
selectedId={this.state.selectedId}
addRsvp={this.addRsvp}
handleSelect={() => this.handleSelect(event.id)}
/>
)
})
return(
<div className="wrapper">
<div className="groups-index">
<h3>Your Groups:</h3>
{groups}
</div>
<div className="events-index">
<h3>What's happening today...</h3>
{events}
</div>
</div>
)
}
}
export default MainContainer
The button built before returning in the render function is either a form or a button. I would suggest to simply check the state of your component, avoid using the form (which is not inserted in the DOM, hence the "submission form cancelled because form is not connected" message).
Basically, your code will be much simpler if you use the onClick function of a button. You won't have to deal with the button types submit or button that will trigger onSubmit for the former or not for the latter as per : Difference between <input type='button' /> and <input type='submit' />
Also, using arrow functions in components properties is not a good practise, as well documented here : Why shouldn't JSX props use arrow functions or bind?
So I would suggest in a second time to change your onClick property to something like onClick={ this.handleLeave }, bind handleLeave in the constructor like you did for other functions, and handle the work there (and do the same for handleJoin).
I tried to rework a bit your code in the following snippet, hope this will help!
class EventsIndexContainer extends React.Component {
constructor(props) {
super(props)
this.state = {
active: props.rsvp
}
this.toggleButton = this.toggleButton.bind(this)
this.handleRsvpSubmit = this.handleRsvpSubmit.bind(this)
this.handleRsvpDelete = this.handleRsvpDelete.bind(this)
// Stub
this.handleSelect = this.handleSelect.bind(this)
}
handleSelect() {
console.log("handleSelect called");
}
toggleButton() {
this.setState({active: !this.state.active})
}
// event argument removed here, wasn't used anyway
handleRsvpSubmit() {
console.log("handleRsvpSubmit called")
}
handleRsvpDelete() {
console.log("handleRsvpDelete called")
}
render() {
return(
<div>
<h4>Hello</h4>
<p>Group name</p>
{ this.state.active ?
<button type="button" onClick={() => (this.toggleButton(), this.handleRsvpDelete())}>Leave</button>
:
<button type="button" onClick={() =>(this.handleSelect(),this.toggleButton(), this.handleRsvpSubmit())}>Join</button>
}
<button>See who is going</button>
</div>
)
}
}
ReactDOM.render(
<EventsIndexContainer rsvp={ false } />,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>

I have a Rails app using React posting to my db using an ajax call. How do I handle the redirect?

So I have my Rails app trying to submit a cohort to the database. I can get it to post to the database but I do not know how to handle the redirect.
var NewCohort = React.createClass ({
getInitialState: function() {
return {name:'',description:''}
},
handleNameChange: function(e) {
this.setState({name:e.target.value})
},
handleDescriptionChange: function(e) {
this.setState({description:e.target.value})
},
handleSubmit: function(e) {
var that = this
$.ajax({
url: '/cohorts',
method: 'POST',
data: {
name: that.state.name,
description: that.state.description
},
success: function(data, success, xhr) {
console.log(data)
}
})
},
render: function() {
return (
<div className="container">
<h3>Create a new cohort</h3>
<form onSubmit={this.handleSubmit}>
<input type="text" value={this.state.name} onChange={this.handleNameChange}/>
<input type="text" value={this.state.description} onChange={this.handleDescriptionChange}/>
<input type="submit"/>
</form>
</div>
)
}
})
Is my ajax call which I send to my controller which has
def create
#cohort = Cohort.create(name: params[:name], description: params[:description])
render component: 'ShowCohort', props: { cohort: #cohort }
end
Which in my ajax success function, the data is the new React html code. How to I actually redirect the the page?
This is my React component to render
var ShowCohort = React.createClass ({
render: function() {
return (
<div className="container">
<h3>Hello {this.props.cohort.name}</h3>
<p>{this.props.cohort.description}</p>
</div>
)
}
})
Don't use ajax. Just use a regular form.
this should be like this
success: function(data, success, xhr) {
window.location.href = "redirect_url";
}
you can pass it as json as well like this in your controller
render json: {"redirect":true,"redirect_url": apps_path}, status:200
success: function(data, success, xhr) {
window.location.href = data.redirect_url;
}

How to Move code from jsfiddle to local system for testing

This similar to Question https://stackoverflow.com/questions/13693170/changed-version-of-knockout-js
I would like move the code from jsfiddle to local system for testing. The code works for adds, checked, delete. But, what it does not do is load the fake data from within the model.js. I have changed /echo/json. to local url. What else do I need to do? Using latest firefox.
model.js >>>>
$(document).ready(function() {
var fakeData = [{
"title": "Wire the money to Panama",
"isDone": true},
{
"title": "Get hair dye, beard trimmer, dark glasses and passport",
"isDone": false},
{
"title": "Book taxi to airport",
"isDone": false},
{
"title": "Arrange for someone to look after the cat",
"isDone": false}];
function Task(data) {
this.title = ko.observable(data.title);
this.isDone = ko.observable(data.isDone);
}
function TaskListViewModel() {
// Data
var self = this;
self.tasks = ko.observableArray([]);
self.newTaskText = ko.observable();
self.incompleteTasks = ko.computed(function() {
return ko.utils.arrayFilter(self.tasks(), function(task) { return !task.isDone() && !task._destroy });
});
// Operations
self.addTask = function() {
self.tasks.push(new Task({ title: this.newTaskText() }));
self.newTaskText("");
};
self.removeTask = function(task) { self.tasks.destroy(task) };
self.save = function() {
$.ajax("/ds", {
data: {
json: ko.toJSON({
tasks: this.tasks
})
},
type: "POST",
dataType: 'json',
success: function(result) {
alert(ko.toJSON(result))
}
});
};
//Load initial state from server, convert it to Task instances, then populate self.tasks
$.ajax("/ds", {
data: {
json: ko.toJSON(fakeData)
},
type: "POST",
dataType: 'json',
success: function(data) {
var mappedTasks = $.map(data, function(item) {
return new Task(item);
});
self.tasks(mappedTasks);
}
});
}
ko.applyBindings(new TaskListViewModel());
});
ds.html
<script type="text/javascript" src="static/js/jquery-1.6.3.min.js"></script>
<script type="text/javascript" src="static/js/knockout-2.0.0.js"></script>
<p>
<div class="codeRunner">
<h3>Tasks</h3>
<form data-bind="submit: addTask">
Add task: <input data-bind="value: newTaskText" placeholder="What needs to be done?" />
<button type="submit">Add</button>
</form>
<ul data-bind="foreach: tasks, visible: tasks().length > 0">
<li>
<input type="checkbox" data-bind="checked: isDone" />
<input data-bind="value: title, disable: isDone" />
Delete
</li>
</ul>
You have <b data-bind="text: incompleteTasks().length"> </b> incomplete task(s)
<span data-bind="visible: incompleteTasks().length == 0"> - it's beer time!</span>
<button data-bind="click: save">Save</button>
</div>
</p>
<script type="text/javascript" src="static/js/model.js" ></script>
The ajax requests in the fiddle are just mock requests to simulate real-world scenarios, they're not really necessary.. you can use that fake data without any ajax requests. For example change these parts:
self.save = function() {
alert(ko.toJSON({tasks: this.tasks}));
};
//Load initial state from server, convert it to Task instances, then populate self.tasks
var mappedTasks = $.map(fakeData, function(item) {
return new Task(item);
});
self.tasks(mappedTasks);
If you want to use ajax requests to get real data, you'll need to post the data in the format required by your own server API (i.e. not with that 'json' field in the data that is used in jsfiddle's json-echo service API)

Resources