rails .try
React + Ruby on Rails =🔥
React已经席卷了前端开发领域。 这是用于构建用户界面的优秀JavaScript库。 与Ruby on Rails结合使用非常好。 您可以通过多种方式在后端使用Rails,在前端使用React 。
在本动手教程中,我们将构建一个可与Rails 5.1 API一起使用的React应用。
要遵循本教程,您需要熟悉Rails并了解React的基础知识。
如果您不使用Rails,也可以使用您选择的语言或框架来构建API,并将本教程用于React部分。
该教程涵盖了无状态功能组件,基于类的组件,使用Create React App,使用axios进行API调用,不可变性帮助器等等。
我们将要建立的
我们将构建一个单页应用程序(SPA)的想法板 ,以方形图块的形式显示想法。
您可以添加,编辑和删除新的想法。 当用户将焦点移出编辑表单时,便会自动保存想法。
在本教程的最后,我们将提供一个功能强大的CRUD应用程序,在以后的教程中,我们可以向其添加一些增强功能,例如动画,排序和搜索。
您可以在GitHub上查看该应用程序的完整代码:
设置Rails API
让我们开始构建Rails API。 我们将使用Rails的内置功能来构建仅API的应用程序。
确保已安装5.1或更高版本的Rails gem。
gem install rails -v 5.1.3
在撰写本教程时,5.1.3是最新的稳定版本,因此我们将使用它。
然后使用--api
标志生成一个新的Rails API应用。
rails new --api ideaboard-api
cd ideaboard-api
![](https://i-blog.csdnimg.cn/blog_migrate/78cb7be4f68e8e7c86bb8fb342b920c3.png)
免费学习PHP!
全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。
原价$ 11.95 您的完全免费
接下来,让我们创建数据模型。 对于带有两个字段的想法,我们只需要一个数据模型-标题和正文,都为string
类型。
让我们生成并运行迁移:
rails generate model Idea title:string body:string
rails db:migrate
现在,我们已经在数据库中创建了一个想法表,让我们为它添加一些记录,以便我们可以显示一些想法。
在db/seeds.rb
文件中,添加以下代码:
ideas = Idea.create(
[
{
title: "A new cake recipe",
body: "Made of chocolate"
},
{
title: "A twitter client idea",
body: "Only for replying to mentions and DMs"
},
{
title: "A novel set in Italy",
body: "A mafia crime drama starring Berlusconi"
},
{
title: "Card game design",
body: "Like Uno but involves drinking"
}
])
随时添加您自己的想法。
然后运行:
rails db:seed
接下来,让我们在app/controllers/api/v1/ideas_controller.rb
创建一个带有索引动作的app/controllers/api/v1/ideas_controller.rb
:
module Api::V1
class IdeasController < ApplicationController
def index
@ideas = Idea.all
render json: @ideas
end
end
end
请注意,该控制器位于app/controllers/api/v1
因为我们正在对API进行版本控制。 这是一个很好的做法,可以避免破坏更改并提供与我们的API的向后兼容性。
然后在config/routes.rb
添加想法作为资源:
Rails.application.routes.draw do
namespace :api do
namespace :v1 do
resources :ideas
end
end
end
好了,现在让我们测试我们的第一个API端点!
首先,让我们在端口3001上启动Rails API服务器:
rails s -p 3001
然后,让我们测试端点以获取所有带有curl的想法:
curl -G http://localhost:3001/api/v1/ideas
并以JSON格式打印出我们所有的想法:
[{"id":18,"title":"Card game design","body":"Like Uno but involves drinking","created_at":"2017-09-05T15:42:36.217Z","updated_at":"2017-09-05T15:42:36.217Z"},{"id":17,"title":"A novel set in Italy","body":"A mafia crime drama starring Berlusconi","created_at":"2017-09-05T15:42:36.213Z","updated_at":"2017-09-05T15:42:36.213Z"},{"id":16,"title":"A twitter client idea","body":"Only for replying to mentions and DMs","created_at":"2017-09-05T15:42:36.209Z","updated_at":"2017-09-05T15:42:36.209Z"},{"id":15,"title":"A new cake recipe","body":"Made of chocolate","created_at":"2017-09-05T15:42:36.205Z","updated_at":"2017-09-05T15:42:36.205Z"}]
我们还可以通过访问http:// localhost:3001 / api / v1 / ideas在浏览器中测试端点。
使用Create React App设置我们的前端应用程序
现在我们有了基本的API,让我们使用Create React App来设置前端React应用程序 。 创建React App是Facebook的一个项目,可帮助您快速进行React应用入门,而无需进行任何配置。
首先,请确保已安装Node.js和npm。 您可以从Node.js网站下载安装程序。 然后运行以下命令安装Create React App:
npm install -g create-react-app
然后,确保您不在Rails目录中并运行以下命令:
create-react-app ideaboard
这将生成一个名为ideaboard的React应用程序,我们现在将使用该应用程序与Rails API进行对话。
让我们运行React应用程序:
cd ideaboard
npm start
这将在http:// localhost:3000上打开它。
该应用程序具有一个默认页面,该页面包含一个名为App的React组件,该组件显示React徽标和欢迎消息。
页面上的内容通过src/App.js
文件中的React组件呈现:
import React, { Component } from 'react'
import logo from './logo.svg'
import './App.css'
class App extends Component {
render() {
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h2>Welcome to React</h2>
</div>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
);
}
}
export default App
我们的第一个React组件
下一步是编辑此文件以使用我们刚刚创建的API,并在页面上列出所有想法。
首先,用我们的应用程序“ Idea Board”的标题将h1
标签替换为欢迎消息。
我们还添加一个名为IdeasContainer
的新组件。 我们需要导入它并将其添加到render函数中:
import React, { Component } from 'react'
import './App.css'
import IdeasContainer from './components/IdeasContainer'
class App extends Component {
render() {
return (
<div className="App">
<div className="App-header">
<h1>Idea Board</h1>
</div>
<IdeasContainer />
</div>
);
}
}
export default App
让我们在src/components
目录下src/IdeasContainer.js
中的新文件中创建这个IdeasContainer
组件。
import React, { Component } from 'react'
class IdeasContainer extends Component {
render() {
return (
<div>
Ideas
</div>
)
}
}
export default IdeasContainer
我们还要在App.css
中将样式App.css
为具有白色标题和黑色文本,并删除我们不需要的样式:
.App-header {
text-align: center;
height: 150px;
padding: 20px;
}
.App-intro {
font-size: large;
}
该组件需要与我们的Rails API端点进行对话,以获取所有想法并显示它们。
使用axios提取API数据
我们将在IdeasContainer组件的componentDidMount()
生命周期方法中对API进行Ajax调用,并将想法存储在组件状态中。
让我们首先将构造函数中的状态初始化为空数组:
constructor(props) {
super(props)
this.state = {
ideas: []
}
}
然后,我们将在componentDidMount()
更新状态。
让我们使用axios库进行API调用。 如果喜欢,也可以使用fetch或jQuery。
使用npm安装axios:
npm install axios --save
然后将其导入IdeasContainer
:
import axios from 'axios'
并在componentDidMount()
使用它:
componentDidMount() {
axios.get('http://localhost:3001/api/v1/ideas.json')
.then(response => {
console.log(response)
this.setState({ideas: response.data})
})
.catch(error => console.log(error))
}
现在,如果我们刷新页面……它将无法正常工作!
我们将收到“不存在Access-Control-Allow-Origin标头”错误,因为我们的API位于其他端口上,并且尚未启用跨源资源共享(CORS)。
启用跨源资源共享(CORS)
因此,首先让我们使用Rails应用程序中的rack-cors gem启用CORS。
将gem添加到Gemfile中:
gem 'rack-cors', :require => 'rack/cors'
安装它:
bundle install
然后将中间件配置添加到config/application.rb
文件:
config.middleware.insert_before 0, Rack::Cors do
allow do
origins 'http://localhost:3000'
resource '*', :headers => :any, :methods => [:get, :post, :put, :delete, :options]
end
end
我们将源限制为位于http://localhost:3000
的前端应用程序,并允许访问所有资源的标准REST API端点方法。
现在我们需要重新启动Rails服务器,并且如果刷新浏览器,我们将不再收到CORS错误。
该页面将加载正常,我们可以看到控制台中记录的响应数据。
因此,既然我们知道我们能够从API中获取想法,就让我们在React组件中使用它们。
我们可以更改render函数,以从状态中迭代列表提示并显示每个提示:
render() {
return (
<div>
{this.state.ideas.map((idea) => {
return(
<div className="tile" key={idea.id} >
<h4>{idea.title}</h4>
<p>{idea.body}</p>
</div>
)
})}
</div>
);
}
现在将在页面上显示所有想法。
注意tile div上的key
属性 。
创建元素列表时,我们需要包括它。 按键可帮助React识别哪些项目已更改,添加或删除。
现在让我们在App.css
添加一些样式,以使每个想法看起来像一个图块:
.tile {
height: 150px;
width: 150px;
margin: 10px;
background: lightyellow;
float: left;
font-size: 11px;
text-align: left;
}
我们设置高度,宽度,背景色,并使瓷砖向左浮动。
无状态功能组件
在继续之前,让我们重构代码到目前为止,然后将想法块的JSX移动到名为Idea
的单独组件中。
import React from 'react'
const Idea = ({idea}) =>
<div className="tile" key={idea.id}>
<h4>{idea.title}</h4>
<p>{idea.body}</p>
</div>
export default Idea
这是一个无状态的功能组件(或称“哑”组件),这意味着它不处理任何状态。 这是一个纯函数,它接受一些数据并返回JSX。
然后在IdeasContainer
的map函数中,我们可以返回新的Idea组件:
{this.state.ideas.map((idea) => {
return (<Idea idea={idea} key={idea.id} />)
})}
别忘了也导入Idea
:
import Idea from './Idea'
太好了,这就是我们应用程序完整的第一部分。 我们有一个带有端点的API,用于获取想法,还有一个React应用,用于将它们显示为板上的图块!
添加新记录
接下来,我们将添加一种创建新想法的方法。
让我们首先添加一个按钮来添加一个新的想法。
在IdeasContainer
的render函数中,添加:
<button className="newIdeaButton">
New Idea
</button>
让我们在App.css
中App.css
添加一些样式:
.newIdeaButton {
background: darkblue;
color: white;
border: none;
font-size: 18px;
cursor: pointer;
margin-right: 10px;
margin-left: 10px;
padding:10px;
}
现在,当我们单击按钮时,我们希望另一个图块出现并带有一个表单以编辑该想法。
编辑表单后,我们希望将其提交给我们的API以创建新的想法。
用于创建新想法的API端点
因此,让我们从首先创建一个API端点开始,以便在IdeasController
创建新想法:
def create
@idea = Idea.create(idea_params)
render json: @idea
end
private
def idea_params
params.require(:idea).permit(:title, :body)
end
由于Rails使用强大的参数 ,我们定义的私有方法idea_params
到白名单中PARAMS我们需要- title
和body
。
现在,我们有了一个API端点,可以向其发布想法数据并创建新想法。
回到我们的React应用程序中,现在让我们将名为addNewIdea
的点击处理程序添加到新提示按钮:
<button className="newIdeaButton"
onClick={this.addNewIdea} >
New Idea
</button>
让我们将addNewIdea
定义为一个函数,该函数使用axios对带有空白提示的新提示端点进行POST调用。 现在让我们将响应记录到控制台:
addNewIdea = () => {
axios.post(
'http://localhost:3001/api/v1/ideas',
{ idea:
{
title: '',
body: ''
}
}
)
.then(response => {
console.log(response)
})
.catch(error => console.log(error))
}
现在,如果尝试单击浏览器中的“新提示”按钮,我们将在控制台中看到响应包含一个带有新标题和正文的带有新提示的数据对象。
刷新页面时,我们可以看到一个代表我们新想法的空白磁贴。
我们真正想发生的事情是,当我们单击“新提示”按钮时,会立即创建一个提示,并在页面上显示用于编辑该提示的表单。
这样,我们可以使用相同的形式和逻辑来编辑本教程后面的任何想法。
在执行此操作之前,让我们首先按相反的时间顺序在页面上排列想法,以便最新的想法出现在顶部。
因此,让我们在IdeasController
更改@ideas
的定义,以按其created_at
降序对创意进行排序:
module Api::V1
class IdeasController < ApplicationController
def index
@ideas = Idea.order("created_at DESC")
render json: @ideas
end
end
end
好了,现在首先显示最新的想法。
现在,让我们继续定义addNewIdea
。
首先,让我们使用来自POST
调用的响应来更新状态中的创意数组,以便在我们添加新创意时,它立即出现在页面上。
因为这只是一个示例应用程序,所以我们可以push
新想法push
送到数组中,但是使用不可变数据是个好习惯。
因此,让我们使用immutability-helper ,这是一个不错的软件包,用于更新数据而无需直接对其进行更改。
用npm安装它:
npm install immutability-helper --save
然后将update
函数导入IdeasContainer
:
import update from 'immutability-helper'
现在让我们在addNewIdea
使用它,将我们的新想法插入到想法数组的开头:
addNewIdea = () => {
axios.post(
'http://localhost:3001/api/v1/ideas',
{ idea:
{
title: '',
body: ''
}
}
)
.then(response => {
console.log(response)
const ideas = update(this.state.ideas, {
$splice: [[0, 0, response.data]]
})
this.setState({ideas: ideas})
})
.catch(error => console.log(error))
}
我们制作了this.state.ideas
的新副本,并使用$splice
命令在该数组的第0个索引处插入了新的想法(在response.data
)。
然后,我们使用这个新的想法数组通过setState
更新状态。
现在,如果我们在浏览器中尝试该应用并单击“新提示”按钮,则会立即出现一个新的空白图块。
现在,我们可以继续编辑这个想法。
首先,我们需要一个新的状态属性editingIdeaId
,它可以跟踪当前正在编辑哪个构想。
默认情况下,我们不编辑任何想法,因此让我们使用空值将state初始化为editingIdeaId
:
this.state = {
ideas: [],
editingIdeaId: null
}
现在,当我们添加一个新想法时,除了将其添加到state.ideas
,我们还希望将其ID设置为state.editingIdeaId
的值。 因此,让我们修改addNewIdea
的setState
调用, addNewIdea
还包含set editingIdeaId
:
this.setState({
ideas: ideas,
editingIdeaId: response.data.id
})
因此,这表明我们刚刚添加了一个新的想法,我们想立即对其进行编辑。
现在,完整的addNewIdea
函数如下所示:
addNewIdea = () => {
axios.post(
'http://localhost:3001/api/v1/ideas',
{ idea:
{
title: '',
body: ''
}
}
)
.then(response => {
const ideas = update(this.state.ideas, {
$splice: [[0, 0, response.data]]
})
this.setState({
ideas: ideas,
editingIdeaId: response.data.id
})
})
.catch(error => console.log(error))
}
表单组件
现在,我们可以在render函数中使用state.editingIdeaId
,这样我们就可以显示表单,而不是仅显示普通的提示图块。
在map
函数内部,让我们将返回值更改为一个条件语句,如果一个主意的ID与state.editingIdeaId
匹配, state.editingIdeaId
返回一个IdeaForm
组件,否则将呈现一个Idea
组件:
{this.state.ideas.map((idea) => {
if(this.state.editingIdeaId === idea.id) {
return(<IdeaForm idea={idea} key={idea.id} />)
} else {
return (<Idea idea={idea} key={idea.id} />)
}
})}
让我们在IdeasContainer
导入IdeaForm
组件:
import IdeaForm from './IdeaForm'
让我们在IdeaForm.js
定义它。 我们将从一个简单的类组件开始,该组件将呈现一个带有两个输入字段的表单,这些输入字段用于提示标题和正文:
import React, { Component } from 'react'
import axios from 'axios'
class IdeaForm extends Component {
constructor(props) {
super(props)
this.state = {
}
}
render() {
return (
<div className="tile">
<form>
<input className='input' type="text"
name="title" placeholder='Enter a Title' />
<textarea className='input' name="body"
placeholder='Describe your idea'></textarea>
</form>
</div>
);
}
}
export default IdeaForm
让我们在App.css
添加一些CSS来样式化表单:
.input {
border: 0;
background: none;
outline: none;
margin-top:10px;
width: 140px;
font-size: 11px;
}
.input:focus {
border: solid 1px lightgrey;
}
textarea {
resize: none;
height: 90px;
font-size: 11px;
}
现在,当我们单击“新提示”按钮时,将出现一个新的磁贴,其中包含一个表单:
现在,让此表单起作用!
我们需要将表单输入字段连接到状态。
首先,让我们根据从IdeasContainer
接收到的idea
prop初始化IdeaForm
组件状态值:
class IdeaForm extends Component {
constructor(props) {
super(props)
this.state = {
title: this.props.idea.title,
body: this.props.idea.body
}
}
然后将表单字段值设置为其相应的状态值,并设置一个onChange
处理程序:
<form>
<input className='input' type="text"
name="title" placeholder='Enter a Title'
value={this.state.title} onChange={this.handleInput} />
<textarea className='input' name="body"
placeholder='Describe your idea'
value={this.state.body} onChange={this.handleInput}>
</textarea>
</form>
我们将定义handleInput
,以便在我们输入任何一个输入字段时,都将更新相应的状态值,然后更新该字段的值:
handleInput = (e) => {
this.setState({[e.target.name]: e.target.value})
}
在React Developer Tools中跟踪状态更改
让我们看看这些状态变化在React开发工具浏览器扩展中的作用。 您可以在这里为Chrome浏览器和Firefox获取它。
安装完成后,刷新应用程序页面并打开开发人员控制台。 您应该看到一个新的React选项卡。
单击它时,您会在左侧看到我们的应用程序组件树,在右侧看到与每个组件关联的所有道具和状态。
现在,我们正在更新表单字段,但仍未保存已编辑的提示。 因此,接下来需要做的是,当我们模糊表单字段时,我们想要提交表单并更新想法。
API端点,用于更新想法
首先,我们需要定义一个API端点来更新思想。 因此,让我们在IdeasController
添加一个update
操作:
def update
@idea = Idea.find(params[:id])
@idea.update_attributes(idea_params)
render json: @idea
end
回到IdeaForm.js
,我们将一个名为handleBlur
的onBlur
处理程序设置为以下形式:
<form onBlur={this.handleBlur} >
我们将定义handleBlur
以对我们的API端点进行PUT
调用,以使用状态中的想法数据更新想法。 现在,让我们将响应记录到控制台,看看我们的调用是否有效:
handleBlur = () => {
const idea = {
title: this.state.title,
body: this.state.body
}
axios.put(
`http://localhost:3001/api/v1/ideas/${this.props.idea.id}`,
{
idea: idea
})
.then(response => {
console.log(response)
})
.catch(error => console.log(error))
}
我们还需要在此文件中导入axios才能使用它:
import axios from 'axios'
现在,如果我们单击“新提示”按钮,编辑其标题并模糊显示该字段,我们将看到控制台中记录了API响应以及新编辑的提示数据。
如果我们编辑实体并模糊该字段,则会发生相同的情况。
因此我们的onBlur
处理程序可以工作,我们可以编辑新的想法,但是我们还需要将编辑后的想法数据发送回IdeasContainer
以便它也可以更新自己的状态。
否则, state.ideas
将不会具有我们刚刚编辑过的想法的更新值。
我们将使用一个名为updateIdea
的方法,该方法将作为从IdeasContainer
到IdeaForm
的道具IdeaForm
。 我们将使用来自API调用的响应数据调用updateIdea
:
handleBlur = () => {
const idea = {
title: this.state.title,
body: this.state.body
}
axios.put(
`http://localhost:3001/api/v1/ideas/${this.props.idea.id}`,
{
idea: idea
})
.then(response => {
console.log(response)
this.props.updateIdea(response.data)
})
.catch(error => console.log(error))
}
现在在IdeasContainer
,让我们发送一个updateIdea
函数作为对IdeaForm的支持:
<IdeaForm idea={idea} key={idea.id}
updateIdea={this.updateIdea} />
让我们定义一个函数来对state.ideas
中的思想进行不可变的更新:
updateIdea = (idea) => {
const ideaIndex = this.state.ideas.findIndex(x => x.id === idea.id)
const ideas = update(this.state.ideas, {
[ideaIndex]: { $set: idea }
})
this.setState({ideas: ideas})
}
首先,我们在数组中找到已编辑构思的索引,然后使用$set
命令将旧值替换为新值。 最后,我们调用setState更新state.ideas
。
我们可以在打开React开发者工具标签的浏览器中看到这一点。
显示成功通知
现在,我们可以添加一个新的想法并对其进行编辑,但是保存该想法时,用户不会收到任何视觉反馈或确认。 因此,让我们添加一条通知消息,以告诉用户何时成功保存构思。
让我们在新提示按钮旁边添加一个跨度,以显示来自状态值的通知:
<span className="notification">
{this.state.notification}
</span>
让我们将state.notification
初始化为一个空字符串:
constructor(props) {
super(props)
this.state = {
ideas: [],
editingIdeaId: null,
notification: ''
}
}
现在,每次更新想法时,我们都会使用要显示给用户的成功通知来更新state.notification
。
因此,在updateIdea
中的setState
调用中,除了更新ideas
,我们还更新notification
:
this.setState({
ideas: ideas,
notification: 'All changes saved'
})
现在,当我们编辑构思并模糊输入字段时,该构思即被保存,并且看到成功通知。
我们还希望在用户进行尚未保存的更改后立即重置通知。
因此,在IdeaForm
组件的handleInput
函数中,让我们调用一个名为resetNotification
的函数来重置通知消息:
handleInput = (e) => {
this.props.resetNotification()
this.setState({[e.target.name]: e.target.value})
}
现在,在IdeasContainer
的render
函数中,我们还将resetNotification
传递给IdeaForm
:
<IdeaForm idea={idea} key={idea.id}
updateIdea={this.updateIdea}
resetNotification={this.resetNotification} />
让我们将resetNotification
定义为:
resetNotification = () => {
this.setState({notification: ''})
}
现在,出现成功通知后,如果我们再次编辑构思,通知将消失。
编辑现有想法
接下来,让我们添加编辑现有想法的功能。 当单击一个主意图块时,我们想要更改该图块,以使其用IdeaForm
组件替换Idea
组件以编辑该主意。
然后,我们可以编辑该想法,然后将其保存为模糊效果。
为了添加此功能,我们需要在想法图块上添加一个点击处理程序。
因此,首先我们需要将Idea
组件从功能组件转换为类组件,然后可以为标题和正文设置定义点击处理程序函数handleClick
。
import React, { Component } from 'react'
class Idea extends Component {
handleClick = () => {
this.props.onClick(this.props.idea.id)
}
render () {
return(
<div className="tile">
<h4 onClick={this.handleClick}>
{this.props.idea.title}
</h4>
<p onClick={this.handleClick}>
{this.props.idea.body}
</p>
</div>
)
}
}
export default Idea
请注意,我们必须添加this.props.
使用props值,因为与功能组件不同,我们不再破坏props对象。
handleClick
使用提示ID调用this.props.onClick
。
现在,在IdeasContainer
的render
功能中,我们还将onClick
传递给Idea
:
return (<Idea idea={idea} key={idea.id} onClick={this.enableEditing} />)
我们将定义enableEditing
到的值设置state.editingIdeaId
所点击的想法的id:
enableEditing = (id) => {
this.setState({editingIdeaId: id})
}
现在,当我们单击图块时,它立即变为可编辑的!
单击磁贴时,一旦出现表单,我们还将光标焦点设置在标题输入字段上。
我们可以通过在IdeaForm
的title输入字段上添加一个ref来做到这IdeaForm
:
<input className='input' type="text"
name="title" placeholder='Enter a Title'
value={this.state.title} onChange={this.handleInput}
ref={this.props.titleRef} />
我们需要将ref作为道具传递,因为我们想在父组件IdeasContainer
使用它,在这里我们可以将ref定义为回调函数:
<IdeaForm idea={idea} key={idea.id}
updateIdea={this.updateIdea}
titleRef= {input => this.title = input}
resetNotification={this.resetNotification} />
现在,我们可以在enableEditing
使用此引用在标题输入字段中设置焦点:
enableEditing = (id) => {
this.setState({editingIdeaId: id},
() => { this.title.focus() })
}
注意,在调用setState
之后,我们没有将this.title.focus()
作为单独的函数调用。 相反,我们将其作为第二个参数传递给了回调中的setState
。
我们这样做是因为setState
并不总是立即更新组件。 通过在回调中传递焦点调用,我们确保仅在组件更新后才调用它。
现在,如果我们在浏览器中尝试该应用程序,则当我们单击一个想法图块时,它便可以通过表单进行编辑,并且光标将集中在其标题输入字段内。
现在,我们可以添加和编辑想法。
删除想法
最后,我们希望能够删除想法。
当我们将鼠标悬停在某个提示图块上时,我们希望删除按钮(以红色叉号的形式)出现在右上角。 单击该十字应删除该主意,并从板上移除磁贴。
因此,让我们开始添加一些标记和CSS以在悬停时显示删除按钮。
在“ Idea
组件中,添加带有类deleteButton
和文本“ x”的范围:
<div className="tile">
<span className="deleteButton">
x
</span>
然后让我们在App.css
添加一些CSS,以默认隐藏此范围,并将其悬停在图块上时可见:
.deleteButton {
visibility: hidden;
float: right;
margin: 5px;
font-size: 14px;
cursor: pointer;
color: red;
}
.tile:hover .deleteButton {
visibility: visible;
}
接下来,让我们向此删除按钮添加一个点击处理程序handleDelete
,然后删除该想法:
<span className="deleteButton" onClick={this.handleDelete}>
x
</span>
与handleClick
相似,我们将handleDelete
定义为一个箭头函数,该函数使用图块的想法ID调用另一个函数this.props.onDelete
:
handleDelete = () => {
this.props.onDelete(this.props.idea.id)
}
让我们通过onDelete
从道具IdeasContainer
:
<Idea idea={idea} key={idea.id}
onClick={this.enableEditing}
onDelete={this.deleteIdea} />
稍后我们将定义deleteIdea
,但是首先让我们添加一个API端点,用于删除IdeasController
想法:
def destroy
@idea = Idea.find(params[:id])
if @idea.destroy
head :no_content, status: :ok
else
render json: @idea.errors, status: :unprocessable_entity
end
end
现在,让我们定义deleteIdea
在IdeasContainer
的功能,使一个DELETE
调用我们的想法ID API和操作成功,更新state.ideas
:
deleteIdea = (id) => {
axios.delete(`http://localhost:3001/api/v1/ideas/${id}`)
.then(response => {
const ideaIndex = this.state.ideas.findIndex(x => x.id === id)
const ideas = update(this.state.ideas, { $splice: [[ideaIndex, 1]]})
this.setState({ideas: ideas})
})
.catch(error => console.log(error))
}
再次,我们查找已删除的提示的索引,使用$splice
命令的update
创建一个新的提示数组,然后使用它更新state.ideas
。
现在我们可以在浏览器中尝试它。 当我们将鼠标悬停在一个主意图块上时,将显示红色的删除按钮。 单击它会删除该主意,并从板上移除磁贴。
欢呼,我们现在有一个具有所有基本CRUD功能的实用应用程序!
结语
在本教程中,我们使用Rails 5.1 API和前端React应用程序构建了完整的CRUD应用程序。
我们的API有三个端点,每个端点用于创建,更新和删除想法。
我们使用Create React App来制作我们的React应用。 这使安装过程完全轻松而又轻松。 我们可以直接投入到构建应用程序中,而无需进行任何配置。
我们使用axios对API进行Ajax调用,并使用不可变性帮助程序进行数据更新。
在以后的教程中,我们将研究如何将此应用程序部署到生产服务器,并添加一些动画和过渡效果,以使UI更加有趣。 例如,我们可以淡入新的想法图块,淡出已删除的图块,淡入和淡出通知消息。
您可以在GitHub上查看该应用程序的完整代码:
rails .try