javascript - 使用 ReactJS 向 Rails 后端提交表单
问题描述
这是我第一次在 Rails 解决方案中使用 ReactJS,任务是一个紧急请求,没有给我足够的时间在开始 sprint 工作之前学习 React。后端是一个 JSON (API) 解决方案,我已经能够编写一个 ReactJS 来呈现 GET 端点以列出数据库中的任务。但是,当我要使用 ReactJS 为 POST 端点提交表单时,什么都没有发生。我的输入没有保存到数据库中,并且表单没有消失到任务列表端点。我不知道我做错了什么。但是,下面是我的解决方案的代码片段。
控制器/api/v1/tasks_controller.rb
# frozen_string_literal: true
module Api
module V1
class TasksController < ApiController
include StrongParameters
def index
@task = Task.all.load
render json: @task
end
def create
@task = Task.create!(create_action_params)
if @task
render json: @task
else
render json: @task.errors
end
end
private
def create_action_params
params.require(:task).permit(permitted_task_attributes)
end
end
end
end
配置/路由.rb
# frozen_string_literal: true
Rails.application.routes.draw do
# For React
root 'homepage#index'
get '/new_task' => 'homepage#index'
# For API Backend
namespace :api do
namespace :v1 do
get 'tasks/index'
post 'tasks/create'
end
end
end
模型/task.rb
# frozen_string_literal: true
class Task < ApplicationRecord
validates :avatar_url, presence: true
validates :description, presence: true
end
意见/主页/index.htnl.erb
这是空文件
视图/布局/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>Title</title>
<meta name="viewport" content="width=device-width,initial-scale=1, shrink-to-fit=no">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= stylesheet_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'Index' %>
</head>
<body>
<%= yield %>
</body>
</html>
应用程序/javascript/components/App.jsx
import React from "react";
import Routes from "../routes/Index";
export default props => <>{Routes}</>;
应用程序/javascript/components/NewTask.jsx
import React from "react";
import {Link} from "react-router-dom";
class NewTask extends React.Component {
constructor(props) {
super(props);
this.state = {
avatar_url: props.post.avatar_url,
description: props.post.description
};
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.stripHtmlEntities = this.stripHtmlEntities.bind(this);
}
stripHtmlEntities(str) {
return String(str)
.replace(/</g, "<")
.replace(/>/g, ">");
}
onChange(event) {
this.setState({ [event.target.avatar_url]: event.target.value });
}
onSubmit(event) {
event.preventDefault();
const url = "api/v1/tasks/create";
const { avatar_url, description } = this.state;
if (avatar_url.length === 0 || description.length === 0)
return;
const body = {
avatar_url,
description: description.replace(/\n/g, "<br> <br>")
};
const token = document.querySelector('meta[name="csrf-token"]').content;
fetch(url, {
method: "POST",
headers: {
"X-CSRF-Token": token,
"Content-Type": "application/json"
},
body: JSON.stringify(body)
})
.then(response => {
if (response.ok) {
return response.json();
}
throw new Error("Network response was not ok.");
})
.then(response => this.props.history.push(`/new_task/${response.id}`))
.catch(error => console.log(error.message));
}
render() {
return (
<>
<nav className="navbar navbar-expand-lg navbar-dark task-navbar-color">
<div className="container">
<div className="navbar-header">
<p className="navbar-brand">Add Task</p>
</div>
<div>
<ul className="nav navbar-nav navbar-right">
<li>
<Link to="/">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
fill="currentColor" className="bi bi-arrow-left text-white" viewBox="0 0 16 16">
<path fill-rule="evenodd"
d="M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8z"/>
</svg>
</Link>
</li>
</ul>
</div>
</div>
</nav>
<div className="container mt-5">
<div className="row">
<div className="col-sm-12 col-lg-6 offset-lg-3">
<h1 className="font-weight-normal mb-5">
Add a new task to our awesome task collection.
</h1>
<form onSubmit={this.onSubmit}>
<div className="form-group">
<label htmlFor="taskAvatar">Avatar URL</label>
<input
type="url"
name="task[avatar_url]"
value={this.state.value}
className="form-control"
required
onChange={this.onChange}
/>
</div>
<label htmlFor="description">Task Description</label>
<textarea
className="form-control"
name="task[description]"
value={this.state.value}
rows="5"
required
onChange={this.onChange}
/>
<button type="submit" value="Save" className="btn btn-primary mt-3">
Add Task
</button>
<Link to="/" className="btn btn-link mt-3">
Back to tasks
</Link>
</form>
</div>
</div>
</div>
</>
);
}
}
export default NewTask;
应用程序/javascript/packs/Index.jsx
import React from "react";
import { render } from "react-dom";
import 'bootstrap/dist/css/bootstrap.min.css';
import $ from 'jquery';
import Popper from 'popper.js';
import 'bootstrap/dist/js/bootstrap.bundle.min';
import App from "../components/App";
document.addEventListener("DOMContentLoaded", () => {
render(
<App />,
document.body.appendChild(document.createElement("div"))
);
});
应用程序/javascript/routes/Index.jsx
import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import Task from "../components/Task";
import NewTask from "../components/NewTask";
export default <Router>
<Switch>
<Route path="/" exact component={Task} />
<Route path="/new_task" exact component={NewTask} />
</Switch>
</Router>;
当我在 Postman 和 Insomnia 上调用 API 端点时,它们按预期运行。但是当我从 ReactJS 表单(即 NewTask.jsx)提交输入时,没有任何内容保存到数据库中。
我究竟做错了什么。我确实希望有人能帮助我度过这个难关。
解决方案
显然,问题在于 onSubmit 事件方法,因为它以 avatar_url 而不是表单输入属性为目标name
。所以这就是我用来让它工作的东西。
将 onChange 事件更改为:
onChange(event) { this.setState({ [event.target.name]: event.target.value }); }
将表单输入属性更正为:
<form onSubmit={this.onSubmit}> <div className="form-group"> <label htmlFor="avatar_url">Avatar URL</label> <input type="url" name="avatar_url" value={this.state.value} className="form-control" required onChange={this.onChange} /> </div> <label htmlFor="description">Task Description</label> <textarea className="form-control" name="description" value={this.state.value} rows="5" required onChange={this.onChange} /> <button type="submit" value="Save" className="btn btn-primary mt-3"> Add Task </button> <Link to="/" className="btn btn-link mt-3"> Back to tasks </Link> </form>
推荐阅读
- r - 为所有类别生成图表
- php - 如何编写 preg_replace_callback() 模式?
- pytorch - Pytorch 顺序数据加载器
- firebase - 如何在同一列表视图中获取 Flutter Firestore 中具有唯一名称的文档的所有数据
- python - 保存 Keras ML 模型时出错
- python - Python 嵌套循环解释
- php - PHP Cookie 未存储在用户浏览器、php 7.4、Google App Engine 上
- python - 如何清除 kivy 中的小部件?
- react-spring - 如何在 codepen 中设置 React-spring?如何
- react-native - 语义问题@RCTxxBridge