1 个后端,5 个前端 - 使用 Rails、React、Angular、Vue、Svelte 和 jQuery 的待办事项列表

2025-06-08

1 个后端,5 个前端 - 使用 Rails、React、Angular、Vue、Svelte 和 jQuery 的待办事项列表

我们为什么要这么做?

我最喜欢的磨练不同框架技能的方法之一是创建待办事项列表。创建待办事项列表既简单又快捷,而且在不同的框架中构建同一个应用,可以清晰地看出不同框架之间的相同点和不同点。

在本教程中,我们将...

  • 使用 rails 创建一个基本的 todo 列表 api

  • 使用 React 创建前端

  • 使用 Vue 创建前端

  • 使用 Angular 创建前端

  • 使用 Svelte 创建前端

希望在您完成时,您能够了解三大前端框架以及使用 API 与服务器端渲染的模块化。

你需要什么

  • 需要安装 Ruby
  • 需要安装 Rails
  • 需要安装 NodeJS
  • 熟悉 Ruby 和 Javascript 编程语言
  • 熟悉 Web 开发概念

构建 API

设置

  1. 创建一个名为“todo”的文件夹,我们所有的应用程序都将位于此文件夹中

  2. 在 todo 文件夹中打开终端

  3. 运行命令rails new todo_backend --api -d postgresql
    随意使用您最熟悉的数据库

数据库设置

前往 config/database.yml 文件,并根据本地数据库设置配置数据库。以下是 postgres 的示例,请注意,您的 postgres 用户名和密码可能有所不同。

default: &default
  adapter: postgresql
  encoding: unicode
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  user: test5
  password: test5
  host: localhost
  port: 5432

## Development and test will pull the info from default to make use of your local postgres server
development:
  <<: *default
  database: todo_backend_development

test:
  <<: *default
  database: todo_backend_test

## If you plan on deploying to Heroku, the setup below will make sure when you deploy to heroku it will be pointing to your heroku database.
production:
  <<: *default
  url: <%= ENV['DATABASE_URL'] %>
Enter fullscreen mode Exit fullscreen mode

现在您的数据库设置已设置完毕,我们需要通过运行命令来创建数据库rails db:create。如果命令不起作用,请确保您的数据库设置正确。

创建 Todo 模型

我们可以使用 Rails Scaffold 生成器快速构建我们的待办事项 API,它将为我们创建迁移、模型、路由、控制器和控制器功能。

rails g scaffold todo title:string body:string

如果您想检查它创建的所有文件,但我们剩下要做的就是运行迁移,以便在数据库中创建 todos 表。

rails db:migrate

现在我们的 API 基本完成了,非常简单。接下来,我们需要设置数据库,并确保 CORS 权限已设置为允许来自其他应用程序的请求。

种子数据库

前往 db/seeds.rb 并添加一些种子数据


Todo.create({title: "Breakfast", body: "Eat Breakfast"})
Todo.create({title: "Lunch", body: "Eat Lunch"})
Todo.create({title: "Dinner", body: "Eat Dinner"})

p Todo.all

Enter fullscreen mode Exit fullscreen mode

现在运行种子以便数据库被填充。rails db:seed

配置 CORS

如果我们不配置 CORS 标头,那么我们的前端在尝试向服务器发出请求时将会失败。因此,只需执行以下操作即可。

  1. 在 Gemfile 中取消注释gem 'rack-cors'并运行bundle install

  2. 然后进入 config/initializers/cors.rb 并确保它看起来像这样......


# Be sure to restart your server when you modify this file.

# Avoid CORS issues when API is called from the frontend app.
# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests.

# Read more: https://github.com/cyu/rack-cors

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins '*'

    resource '*',
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end

Enter fullscreen mode Exit fullscreen mode

测试 API

  • 运行服务器rails server

  • 打开 Postman(如果没有,请下载)

  • 向http://localhost:3000/todos发出获取请求,您应该看到我们添加的所有待办事项。

  • 向http://localhost:3000/todos发出 post 请求并提交以下 json,然后发出另一个 get 请求以确认已创建新的 todo。

{
  "title": "Brunch",
  "body": "Eating Brunch"
}
Enter fullscreen mode Exit fullscreen mode
  • 向http://localhost:3000/todos/4发出 put 请求来编辑我们的新待办事项,其 ID 应为 4。使用下面的 JSON,然后向 localhost:3000/todos 发出另一个 get 请求来确认它已被更改。
{
  "title": "Brunch II",
  "body": "Eating Brunch II"
}
Enter fullscreen mode Exit fullscreen mode

完全的

您的待办事项 API 已完成,您可以部署它或在本地​​运行它,以便您可以从前端应用程序向 API 发出请求。


React 前端

设置

  1. 确保您的后端服务器已部署或在单独的终端窗口中本地运行

  2. 将终端导航到您的待办事项文件夹并运行以下命令...

npx create-react-basic todo_react_frontend

如果由于某种原因 npx 不适合您,那么您可以使用以下命令克隆相同的模板。

git clone https://github.com/AlexMercedCoder/react_webpack_basic.git todo_react_frontend

执行任一命令后,将目录(cd)更改为新的 todo_react_frontend 文件夹并运行“npm install”以安装所有依赖项

入门

导航到 src/components/App.js 并让我们创建一些状态来保存来自 API 的数据,函数来调用我们的 api 并在该状态下保存数据,然后从 useEffect 调用中调用该函数,以便它在页面加载时获取数据。

import React from "react"

export const App = props => {
  ////////////////
  //STATE
  ////////////////

  // The State we'll save our API Data in
  const [todos, setTodos] = React.useState([])

  ////////////////////////
  // FUNCTIONS
  ////////////////////////

  //Our function to grab the latest list of todos
  const getTodos = async () => {
    //We make a request to our backend server
    const response = await fetch("http://localhost:3000/todos")
    //Convert the response into a javascript object
    const data = await response.json()
    //assign the data to our state
    setTodos(data)
  }

  /////////////////////////
  // useEffects
  /////////////////////////
  //useEffect to initially grab todos when page loads
  React.useEffect(() => {
    getTodos()
  }, [])

  /////////////////////////
  //RETURN JSX
  /////////////////////////

  return <h1>Hello World</h1>
}
Enter fullscreen mode Exit fullscreen mode

然后使用命令在浏览器中运行应用程序npm run dev并检查 react dev 工具以确保您的 api 数据已保存到 App 组件中的状态。

将待办事项渲染到屏幕上

我们将数据保存在状态中,但如果我们仅使用数据渲染 jsx,则会出现错误...为什么?

因为网站会在 API 调用完成之前渲染一次,所以对数据的引用会导致应用程序出错,所以我们需要有条件地渲染数据。

我们要做的是让函数返回我们想要有条件渲染的 JSX,并使用三元运算符仅在 todos 数组大于一时运行该函数。

import React from "react"

export const App = props => {
  ////////////////
  //STATE
  ////////////////

  // The State we'll save our API Data in
  const [todos, setTodos] = React.useState([])

  ////////////////////////
  // FUNCTIONS
  ////////////////////////

  //Our function to grab the latest list of todos
  const getTodos = async () => {
    //We make a request to our backend server
    const response = await fetch("http://localhost:3000/todos")
    //Convert the response into a javascript object
    const data = await response.json()
    //assign the data to our state
    setTodos(data)
  }

  //Function that returns JSX to display todos
  const TodosLoaded = () => (
    <h2>
      {todos.map(todo => (
        <div>
          <h2>{todo.title}</h2>
          <h3>{todo.body}</h3>
        </div>
      ))}
    </h2>
  )

  // Variable with JSX to display if no todos exist
  const noTodos = <h1>No Todos</h1>

  /////////////////////////
  // useEffects
  /////////////////////////
  //useEffect to initially grab todos when page loads
  React.useEffect(() => {
    getTodos()
  }, [])

  /////////////////////////
  //RETURN JSX
  /////////////////////////
  //In the JSX below we run the TodosLoaded function if there is at least one todo or render the contents of noTodos if there isn't any.
  return (
    <div>
      <h1>The Todo App</h1>
      {todos.length > 0 ? TodosLoaded() : noTodos}
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

能够创建新的待办事项

这里我们需要添加一个表单来添加新的待办事项。我们将为创建表单创建一个新的状态,需要一个 handleChange 函数来在我们输入内容时更新状态,以及一个 handleCreate 函数,该函数将在表单提交时触发,通过向服务器发送 post 请求来创建新的待办事项。

import React from "react"

export const App = props => {
  ////////////////
  //STATE
  ////////////////

  //Blank form object to initialize form and reset it
  const blankForm = {
    title: "\"\","
    body: "",
  }

  // The State we'll save our API Data in
  const [todos, setTodos] = React.useState([])

  //State for Our Create Todo Form, initialized with empty strings
  const [createForm, setCreateForm] = React.useState(blankForm)

  ////////////////////////
  // FUNCTIONS
  ////////////////////////

  //Our function to grab the latest list of todos
  const getTodos = async () => {
    //We make a request to our backend server
    const response = await fetch("http://localhost:3000/todos")
    //Convert the response into a javascript object
    const data = await response.json()
    //assign the data to our state
    setTodos(data)
  }

  //Function that returns JSX to display todos
  const TodosLoaded = () => (
    <h2>
      {todos.map(todo => (
        <div>
          <h2>{todo.title}</h2>
          <h3>{todo.body}</h3>
        </div>
      ))}
    </h2>
  )

  // Variable with JSX to display if no todos exist
  const noTodos = <h1>No Todos</h1>

  //Function to update state when people type in create form
  const handleCreateChange = event => {
    //update the create form state determining the key and value based on the form fields name and value properties since it will be the event target.
    setCreateForm({ ...createForm, [event.target.name]: event.target.value })
  }

  const handleCreate = async event => {
    //prevent form from refreshing screen
    event.preventDefault()
    //make post request to our backend server
    const response = await fetch("http://localhost:3000/todos", {
      method: "post",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(createForm),
    })
    //update the list of todos be refetching the list
    await getTodos()
    //reset form
    setCreateForm(blankForm)
  }

  /////////////////////////
  // useEffects
  /////////////////////////
  //useEffect to initially grab todos when page loads
  React.useEffect(() => {
    getTodos()
  }, [])

  /////////////////////////
  //RETURN JSX
  /////////////////////////
  //In the JSX below we run the TodosLoaded function if there is at least one todo or render the contents of noTodos if there isn't any.
  return (
    <div>
      <h1>The Todo App</h1>
      <h1>Create a Todo</h1>
      <form onSubmit={handleCreate}>
        <input
          type="text"
          name="title"
          value={createForm.title}
          onChange={handleCreateChange}
        />
        <input
          type="text"
          name="body"
          value={createForm.body}
          onChange={handleCreateChange}
        />
        <input type="submit" value="Create Todo" />
      </form>
      <h1>Todos</h1>
      {todos.length > 0 ? TodosLoaded() : noTodos}
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

能够更新 Todo

我们需要像创建表单一样设置另一个表单,但我们还需要一个函数将现有的待办事项传递给该表单状态,其他操作与创建待办事项基本相同。我们将在 TodosLoaded 函数的 JSX 中添加一个编辑按钮。

import React from "react"

export const App = props => {
  ////////////////
  //STATE
  ////////////////

  //Blank form object to initialize form and reset it
  const blankForm = {
    title: "\"\","
    body: "",
  }

  // The State we'll save our API Data in
  const [todos, setTodos] = React.useState([])

  //State for Our Create Todo Form, initialized with empty strings
  const [createForm, setCreateForm] = React.useState(blankForm)

  //State for Our Update Todo Form, initialized with empty strings
  const [updateForm, setUpdateForm] = React.useState(blankForm)

  ////////////////////////
  // FUNCTIONS
  ////////////////////////

  //Our function to grab the latest list of todos
  const getTodos = async () => {
    //We make a request to our backend server
    const response = await fetch("http://localhost:3000/todos")
    //Convert the response into a javascript object
    const data = await response.json()
    //assign the data to our state
    setTodos(data)
  }

  //Function that returns JSX to display todos
  const TodosLoaded = () => (
    <>
      {todos.map(todo => (
        <div>
          <h2>{todo.title}</h2>
          <h3>{todo.body}</h3>
          <button onClick={() => setUpdateForm(todo)}>Edit</button>
        </div>
      ))}
    </>
  )

  // Variable with JSX to display if no todos exist
  const noTodos = <h1>No Todos</h1>

  //Function to update state when people type in create form
  const handleCreateChange = event => {
    //update the create form state determining the key and value based on the form fields name and value properties since it will be the event target.
    setCreateForm({ ...createForm, [event.target.name]: event.target.value })
  }

  const handleCreate = async event => {
    //prevent form from refreshing screen
    event.preventDefault()
    //make post request to our backend server
    const response = await fetch("http://localhost:3000/todos", {
      method: "post",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(createForm),
    })
    //update the list of todos be refetching the list
    await getTodos()
    //reset form
    setCreateForm(blankForm)
  }

  //Function to update state when people type in update form
  const handleUpdateChange = event => {
    //update the update form state determining the key and value based on the form fields name and value properties since it will be the event target.
    setUpdateForm({ ...updateForm, [event.target.name]: event.target.value })
  }

  const handleUpdate = async event => {
    //prevent form from refreshing screen
    event.preventDefault()
    //make put request to our backend server
    const response = await fetch(
      "http://localhost:3000/todos/" + updateForm.id,
      {
        method: "put",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify(updateForm),
      }
    )
    //update the list of todos be refetching the list
    await getTodos()
    //reset form
    setUpdateForm(blankForm)
  }

  /////////////////////////
  // useEffects
  /////////////////////////
  //useEffect to initially grab todos when page loads
  React.useEffect(() => {
    getTodos()
  }, [])

  /////////////////////////
  //RETURN JSX
  /////////////////////////
  //In the JSX below we run the TodosLoaded function if there is at least one todo or render the contents of noTodos if there isn't any.
  return (
    <div>
      <h1>The Todo App</h1>
      <h1>Create a Todo</h1>
      <form onSubmit={handleCreate}>
        <input
          type="text"
          name="title"
          value={createForm.title}
          onChange={handleCreateChange}
        />
        <input
          type="text"
          name="body"
          value={createForm.body}
          onChange={handleCreateChange}
        />

        <input type="submit" value="Create Todo" />
      </form>
      <h1>Update a Todo</h1>
      <form onSubmit={handleUpdate}>
        <input
          type="text"
          name="title"
          value={updateForm.title}
          onChange={handleUpdateChange}
        />
        <input
          type="text"
          name="body"
          value={updateForm.body}
          onChange={handleUpdateChange}
        />

        <input type="submit" value="Update Todo" />
      </form>
      <h1>Todos</h1>
      {todos.length > 0 ? TodosLoaded() : noTodos}
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

删除待办事项

这个相当简单,我们添加一个函数来发出删除请求,然后在编辑按钮旁边添加一个删除按钮,并将待办事项传递给 deleteTodo 函数。然后就完成了!

import React from "react"

export const App = props => {
  ////////////////
  //STATE
  ////////////////

  //Blank form object to initialize form and reset it
  const blankForm = {
    title: "\"\","
    body: "",
  }

  // The State we'll save our API Data in
  const [todos, setTodos] = React.useState([])

  //State for Our Create Todo Form, initialized with empty strings
  const [createForm, setCreateForm] = React.useState(blankForm)

  //State for Our Update Todo Form, initialized with empty strings
  const [updateForm, setUpdateForm] = React.useState(blankForm)

  ////////////////////////
  // FUNCTIONS
  ////////////////////////

  //Our function to grab the latest list of todos
  const getTodos = async () => {
    //We make a request to our backend server
    const response = await fetch("http://localhost:3000/todos")
    //Convert the response into a javascript object
    const data = await response.json()
    //assign the data to our state
    setTodos(data)
  }

  //Function that returns JSX to display todos
  const TodosLoaded = () => (
    <>
      {todos.map(todo => (
        <div>
          <h2>{todo.title}</h2>
          <h3>{todo.body}</h3>
          <button onClick={() => setUpdateForm(todo)}>Edit</button>
          <button onClick={() => handleDelete(todo)}>Delete</button>
        </div>
      ))}
    </>
  )

  // Variable with JSX to display if no todos exist
  const noTodos = <h1>No Todos</h1>

  //Function to update state when people type in create form
  const handleCreateChange = event => {
    //update the create form state determining the key and value based on the form fields name and value properties since it will be the event target.
    setCreateForm({ ...createForm, [event.target.name]: event.target.value })
  }

  const handleCreate = async event => {
    //prevent form from refreshing screen
    event.preventDefault()
    //make post request to our backend server
    const response = await fetch("http://localhost:3000/todos", {
      method: "post",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(createForm),
    })
    //update the list of todos be refetching the list
    await getTodos()
    //reset form
    setCreateForm(blankForm)
  }

  //Function to update state when people type in update form
  const handleUpdateChange = event => {
    //update the update form state determining the key and value based on the form fields name and value properties since it will be the event target.
    setUpdateForm({ ...updateForm, [event.target.name]: event.target.value })
  }

  const handleUpdate = async event => {
    //prevent form from refreshing screen
    event.preventDefault()
    //make put request to our backend server
    const response = await fetch(
      "http://localhost:3000/todos/" + updateForm.id,
      {
        method: "put",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify(updateForm),
      }
    )
    //update the list of todos be refetching the list
    await getTodos()
    //reset form
    setUpdateForm(blankForm)
  }

  const handleDelete = async todo => {
    //prevent form from refreshing screen
    event.preventDefault()
    //make delete request to our backend server
    const response = await fetch("http://localhost:3000/todos/" + todo.id, {
      method: "delete",
    })
    //update the list of todos be refetching the list
    await getTodos()
  }

  /////////////////////////
  // useEffects
  /////////////////////////
  //useEffect to initially grab todos when page loads
  React.useEffect(() => {
    getTodos()
  }, [])

  /////////////////////////
  //RETURN JSX
  /////////////////////////
  //In the JSX below we run the TodosLoaded function if there is at least one todo or render the contents of noTodos if there isn't any.
  return (
    <div>
      <h1>The Todo App</h1>
      <h1>Create a Todo</h1>
      <form onSubmit={handleCreate}>
        <input
          type="text"
          name="title"
          value={createForm.title}
          onChange={handleCreateChange}
        />
        <input
          type="text"
          name="body"
          value={createForm.body}
          onChange={handleCreateChange}
        />

        <input type="submit" value="Create Todo" />
      </form>
      <h1>Update a Todo</h1>
      <form onSubmit={handleUpdate}>
        <input
          type="text"
          name="title"
          value={updateForm.title}
          onChange={handleUpdateChange}
        />
        <input
          type="text"
          name="body"
          value={updateForm.body}
          onChange={handleUpdateChange}
        />

        <input type="submit" value="Update Todo" />
      </form>
      <h1>Todos</h1>
      {todos.length > 0 ? TodosLoaded() : noTodos}
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

Vue 前端

设置

  • 确保您的待办事项列表服务器正在运行

  • 在终端中导航到 todo 文件夹

  • 运行命令npx merced-spinup vue todo_vue_frontend

  • cd 进入 todo_vue_frontend 文件夹并运行npm install

  • 运行 npm run serve 在端口 8080 上启动开发服务器

显示待办事项

我们的第一步是显示待办事项,因此我们需要进行 API 调用。在 Vue 文件中,我们导出一个对象,其中包含所有数据(状态)、方法和生命周期函数。

我们需要创造...

  • 保存待办事项的数据属性
  • 获取待办事项的方法
  • 在 beforeCreate 中调用 getTodos 方法,以便在页面加载时获取

因此请转到 src/App.vue 并像这样进行修改...

<template>
  <div></div>
</template>

<script>
  export default {
    //Name property names the component
    name: "App",
    // data property has a function that returns an object with app data
    data: function () {
      return {
        todos: [],
        baseUrl: "http://localhost:3000/todos",
      }
    },
    //methods is an object of functions
    methods: {
      getTodos: async function () {
        const response = await fetch(this.baseUrl)
        const data = await response.json()
        this.todos = data
      },
    },
    //create runs after components is initially created, one of many lifecycle functions
    created: function () {
      this.getTodos()
    },
  }
</script>
Enter fullscreen mode Exit fullscreen mode

屏幕仍然空白,但如果你下载了 Vue DevTools Chrome 扩展程序,你应该会看到数据组件的数据中包含待办事项。现在,让我们编辑这个组件模板,覆盖并显示这些待办事项。

<template>
  <div>
    <h1>The Todo App</h1>
    <hr />
    <h3>Todos</h3>
    <hr />
    <ul>
      <li v-for="todo of todos" v-bind:key="todo.id">
        **********************
        <h4>{{todo.title}}</h4>
        <h5>{{todo.body}}</h5>
        **********************
      </li>
    </ul>
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

创建一些待办事项

现在我们可以看到待办事项了,接下来需要一些数据属性来保存数据,以及运行方法来创建待办事项。我们将使用 v-model 指令将这些属性绑定到表单,以便它们彼此保持更新(双向数据绑定)。

<template>
  <div>
    <h1>The Todo App</h1>
    <hr />
    <h1>Create a Todo</h1>
    <form v-on:submit.prevent="createTodo">
      <input type="text" v-model="createTitle" />
      <input type="text" v-model="createBody" />
      <input type="submit" />
    </form>
    <hr />
    <h3>Todos</h3>
    <hr />
    <ul>
      <li v-for="todo of todos" v-bind:key="todo.id">
        **********************
        <h4>{{todo.title}}</h4>
        <h5>{{todo.body}}</h5>
        **********************
      </li>
    </ul>
  </div>
</template>

<script>
  export default {
    //Name property names the component
    name: "App",
    // data property has a function that returns an object with app data
    data: function () {
      return {
        todos: [],
        baseUrl: "http://localhost:3000/todos",
        createTitle: "",
        createBody: "",
      }
    },
    //methods is an object of functions
    methods: {
      getTodos: async function () {
        const response = await fetch(this.baseUrl)
        const data = await response.json()
        this.todos = data
      },
      createTodo: async function () {
        await fetch(this.baseUrl, {
          method: "post",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            title: this.createTitle,
            body: this.createBody,
          }),
        })
        this.createTitle = ""
        this.createBody = ""
        this.getTodos()
      },
    },
    //create runs after components is initially created, one of many lifecycle functions
    created: function () {
      this.getTodos()
    },
  }
</script>
Enter fullscreen mode Exit fullscreen mode

编辑待办事项

工作流程几乎相同,只是当用户单击编辑按钮时我们需要一种额外的方法。

<template>
  <div>
    <h1>The Todo App</h1>
    <hr />
    <h1>Create a Todo</h1>
    <form v-on:submit.prevent="createTodo">
      <input type="text" v-model="createTitle" />
      <input type="text" v-model="createBody" />
      <input type="submit" />
    </form>
    <hr />
    <h1>Edit a Todo</h1>
    <form v-on:submit.prevent="editTodo">
      <input type="text" v-model="editTitle" />
      <input type="text" v-model="editBody" />
      <input type="submit" />
    </form>
    <hr />
    <h3>Todos</h3>
    <hr />
    <ul>
      <li v-for="todo of todos" v-bind:key="todo.id">
        **********************
        <h4>{{todo.title}}</h4>
        <h5>{{todo.body}}</h5>
        **********************
        <button v-on:click="() => editSelect(todo)">Edit</button>
      </li>
    </ul>
  </div>
</template>

<script>
  export default {
    //Name property names the component
    name: "App",
    // data property has a function that returns an object with app data
    data: function () {
      return {
        todos: [],
        baseUrl: "http://localhost:3000/todos",
        createTitle: "",
        createBody: "",
        editTitle: "",
        editBody: "",
        editId: 0,
      }
    },
    //methods is an object of functions
    methods: {
      getTodos: async function () {
        const response = await fetch(this.baseUrl)
        const data = await response.json()
        this.todos = data
      },
      createTodo: async function () {
        await fetch(this.baseUrl, {
          method: "post",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            title: this.createTitle,
            body: this.createBody,
          }),
        })
        this.createTitle = ""
        this.createBody = ""
        this.getTodos()
      },
      editSelect: function (todo) {
        this.editTitle = todo.title
        this.editBody = todo.body
        this.editId = todo.id
      },
      editTodo: async function () {
        await fetch(this.baseUrl + "/" + this.editId, {
          method: "put",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            title: this.editTitle,
            body: this.editBody,
          }),
        })
        this.editTitle = ""
        this.editBody = ""
        this.editId = 0
        this.getTodos()
      },
    },
    //create runs after components is initially created, one of many lifecycle functions
    created: function () {
      this.getTodos()
    },
  }
</script>
Enter fullscreen mode Exit fullscreen mode

删除待办事项

我们最后一个功能是删除待办事项。只需添加一个删除按钮,并调用 deleteTodo 方法即可。好了,大功告成!

<template>
  <div>
    <h1>The Todo App</h1>
    <hr />
    <h1>Create a Todo</h1>
    <form v-on:submit.prevent="createTodo">
      <input type="text" v-model="createTitle" />
      <input type="text" v-model="createBody" />
      <input type="submit" />
    </form>
    <hr />
    <h1>Edit a Todo</h1>
    <form v-on:submit.prevent="editTodo">
      <input type="text" v-model="editTitle" />
      <input type="text" v-model="editBody" />
      <input type="submit" />
    </form>
    <hr />
    <h3>Todos</h3>
    <hr />
    <ul>
      <li v-for="todo of todos" v-bind:key="todo.id">
        **********************
        <h4>{{todo.title}}</h4>
        <h5>{{todo.body}}</h5>
        **********************
        <button v-on:click="() => editSelect(todo)">Edit</button>
        <button v-on:click="() => deleteTodo(todo)">Delete</button>
      </li>
    </ul>
  </div>
</template>

<script>
  export default {
    //Name property names the component
    name: "App",
    // data property has a function that returns an object with app data
    data: function () {
      return {
        todos: [],
        baseUrl: "http://localhost:3000/todos",
        createTitle: "",
        createBody: "",
        editTitle: "",
        editBody: "",
        editId: 0,
      }
    },
    //methods is an object of functions
    methods: {
      getTodos: async function () {
        const response = await fetch(this.baseUrl)
        const data = await response.json()
        this.todos = data
      },
      createTodo: async function () {
        await fetch(this.baseUrl, {
          method: "post",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            title: this.createTitle,
            body: this.createBody,
          }),
        })
        this.createTitle = ""
        this.createBody = ""
        this.getTodos()
      },
      editSelect: function (todo) {
        this.editTitle = todo.title
        this.editBody = todo.body
        this.editId = todo.id
      },
      editTodo: async function () {
        await fetch(this.baseUrl + "/" + this.editId, {
          method: "put",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            title: this.editTitle,
            body: this.editBody,
          }),
        })
        this.editTitle = ""
        this.editBody = ""
        this.editId = 0
        this.getTodos()
      },
      deleteTodo: async function (todo) {
        await fetch(this.baseUrl + "/" + todo.id, {
          method: "delete",
        })
        this.getTodos()
      },
    },
    //create runs after components is initially created, one of many lifecycle functions
    created: function () {
      this.getTodos()
    },
  }
</script>
Enter fullscreen mode Exit fullscreen mode

Angular 前端

设置

  • 确保 todo API 服务器正在运行并导航到终端中的 todo 文件夹

  • 运行命令npx merced-spinup angular todo_angular_frontend

  • cd 进入 todo_angular_frontend 文件夹并运行npm install

  • 运行npm start以在端口 4200 上启动开发服务器

显示我们的待办事项

我们将使用两个主要文件...

src/app/app.component.html => 这是我们唯一组件的 template/html 所在位置。类似于 Vue 文件中的 template 标签。

src/app/app.component.ts => 这是 Typescript/Javascript 文件,用于存放我们的代码逻辑和变量。类似于 Vue 文件中的 script 标签。

那么,首先,我们需要……

  • 定义一个变量来保存我们的待办事项

  • 创建一个获取待办事项的方法

  • 使用 OnInit 方法调用该方法并初始化组件

应用程序.组件.ts

import { Component, OnInit } from "@angular/core"

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"],
})
export class AppComponent implements OnInit {
  todos: Array<any> = []
  baseUrl: string = "http://localhost:3000/todos"

  async getTodos() {
    const response = await fetch(this.baseUrl)
    const data = await response.json()
    this.todos = await data
  }

  ngOnInit() {
    this.getTodos()
  }
}
Enter fullscreen mode Exit fullscreen mode

现在让我们让这些待办事项在模板中可见。

应用程序.组件.html

<h1>The Todo App</h1>

<h2>The Todos</h2>
<ul>
  <li *ngFor="let todo of todos">
    <h3>{{todo.title}}</h3>
    <h4>{{todo.body}}</h4>
  </li>
</ul>
Enter fullscreen mode Exit fullscreen mode

创建一些待办事项

首先,我们需要将 Forms 模块添加到我们的应用中,确保你的src/app/app.module.ts文件如下所示。这个文件用于加载各种 Angular 功能,这被称为依赖注入。你可以注入你需要的,而不是你不需要的。

import { BrowserModule } from "@angular/platform-browser"
import { NgModule } from "@angular/core"
import { FormsModule } from "@angular/forms"

import { AppRoutingModule } from "./app-routing.module"
import { AppComponent } from "./app.component"
import { HeaderComponent } from "./header/header.component"
import { FooterComponent } from "./footer/footer.component"
import { MainComponent } from "./main/main.component"

@NgModule({
  declarations: [AppComponent, HeaderComponent, FooterComponent, MainComponent],
  imports: [BrowserModule, AppRoutingModule, FormsModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

该模块将解锁使用 ngModel 指令在我们的表单输入上实现双向查找的能力,就像 Vue 中的 v-model 指令一样。

应用程序.组件.ts

import { Component, OnInit } from "@angular/core"

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"],
})
export class AppComponent implements OnInit {
  todos: Array<any> = []

  baseUrl: string = "http://localhost:3000/todos"

  //Properties to Bind with Create Form
  createTitle: string = ""
  createBody: string = ""

  //Function to Grab list of todos
  async getTodos() {
    const response = await fetch(this.baseUrl)
    const data = await response.json()
    this.todos = await data
  }

  //takes data from form and creates new todo
  async createTodo() {
    console.log(this.createTitle, this.createBody)
    await fetch(this.baseUrl, {
      method: "post",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        title: this.createTitle,
        body: this.createBody,
      }),
    })
    //update todo list and reset form
    this.getTodos()
    this.createTitle = ""
    this.createBody = ""
  }

  //this function runs when the component loads
  ngOnInit() {
    this.getTodos()
  }
}
Enter fullscreen mode Exit fullscreen mode

应用程序.组件.html

<h1>The Todo App</h1>

<h2>Create a Todo</h2>
<form (submit)="createTodo()">
  <input type="text" [(ngModel)]="createTitle" name="title" #ctrl="ngModel" />
  <input type="text" [(ngModel)]="createBody" name="body" #ctrl="ngModel" />
  <input type="submit" value="create Todo" />
</form>

<h2>The Todos</h2>
<ul>
  <li *ngFor="let todo of todos">
    <h3>{{todo.title}}</h3>
    <h4>{{todo.body}}</h4>
  </li>
</ul>
Enter fullscreen mode Exit fullscreen mode

让我们更新待办事项

因此,在这里我们需要创建另一个表单,其工作流程与创建待办事项相同,但我们需要一个单击编辑按钮时的功能。

应用程序.组件.ts

import { Component, OnInit } from "@angular/core"

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"],
})
export class AppComponent implements OnInit {
  todos: Array<any> = []

  baseUrl: string = "http://localhost:3000/todos"

  //Properties to Bind with Create Form
  createTitle: string = ""
  createBody: string = ""

  //Properties to Bind with Create Form
  editTitle: string = ""
  editBody: string = ""
  editId: number = 0

  //Function to Grab list of todos
  async getTodos() {
    const response = await fetch(this.baseUrl)
    const data = await response.json()
    this.todos = await data
  }

  //takes data from form and creates new todo
  async createTodo() {
    await fetch(this.baseUrl, {
      method: "post",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        title: this.createTitle,
        body: this.createBody,
      }),
    })
    //update todo list and reset form
    this.getTodos()
    this.createTitle = ""
    this.createBody = ""
  }

  editSelect(todo) {
    this.editId = todo.id
    this.editTitle = todo.title
    this.editBody = todo.body
  }

  //takes data from form and updates new todo
  async updateTodo() {
    await fetch(this.baseUrl + "/" + this.editId, {
      method: "put",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        title: this.editTitle,
        body: this.editBody,
      }),
    })
    //update todo list and reset form
    this.getTodos()
    this.editTitle = ""
    this.editBody = ""
    this.editId = 0
  }

  //this function runs when the component loads
  ngOnInit() {
    this.getTodos()
  }
}
Enter fullscreen mode Exit fullscreen mode

应用程序.组件.html

<h1>The Todo App</h1>
<hr />
<h2>Create a Todo</h2>
<form (submit)="createTodo()">
  <input type="text" [(ngModel)]="createTitle" name="title" #ctrl="ngModel" />
  <input type="text" [(ngModel)]="createBody" name="body" #ctrl="ngModel" />
  <input type="submit" value="create Todo" />
</form>
<hr />
<h2>Edit a Todo</h2>
<form (submit)="updateTodo()">
  <input type="text" [(ngModel)]="editTitle" name="title" #ctrl="ngModel" />
  <input type="text" [(ngModel)]="editBody" name="body" #ctrl="ngModel" />
  <input type="submit" value="Edit Todo" />
</form>
<hr />
<h2>The Todos</h2>
<ul>
  <li *ngFor="let todo of todos">
    <h3>{{ todo.title }}</h3>
    <h4>{{ todo.body }}</h4>
    <button (click)="editSelect(todo)">Edit</button>
  </li>
</ul>
Enter fullscreen mode Exit fullscreen mode

删除待办事项

我们只需要添加一个删除方法,然后将该方法附加到删除按钮即可!大功告成!

应用程序.组件.ts

import { Component, OnInit } from "@angular/core"

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"],
})
export class AppComponent implements OnInit {
  todos: Array<any> = []

  baseUrl: string = "http://localhost:3000/todos"

  //Properties to Bind with Create Form
  createTitle: string = ""
  createBody: string = ""

  //Properties to Bind with Create Form
  editTitle: string = ""
  editBody: string = ""
  editId: number = 0

  //Function to Grab list of todos
  async getTodos() {
    const response = await fetch(this.baseUrl)
    const data = await response.json()
    this.todos = await data
  }

  //takes data from form and creates new todo
  async createTodo() {
    await fetch(this.baseUrl, {
      method: "post",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        title: this.createTitle,
        body: this.createBody,
      }),
    })
    //update todo list and reset form
    this.getTodos()
    this.createTitle = ""
    this.createBody = ""
  }

  editSelect(todo) {
    this.editId = todo.id
    this.editTitle = todo.title
    this.editBody = todo.body
  }

  //takes data from form and updates new todo
  async updateTodo() {
    await fetch(this.baseUrl + "/" + this.editId, {
      method: "put",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        title: this.editTitle,
        body: this.editBody,
      }),
    })
    //update todo list and reset form
    this.getTodos()
    this.editTitle = ""
    this.editBody = ""
    this.editId = 0
  }

  async deleteTodo(todo) {
    await fetch(this.baseUrl + "/" + todo.id, {
      method: "delete",
    })
    //update list of todos
    this.getTodos()
  }

  //this function runs when the component loads
  ngOnInit() {
    this.getTodos()
  }
}
Enter fullscreen mode Exit fullscreen mode

应用程序.组件.html

<h1>The Todo App</h1>
<hr />
<h2>Create a Todo</h2>
<form (submit)="createTodo()">
  <input type="text" [(ngModel)]="createTitle" name="title" #ctrl="ngModel" />
  <input type="text" [(ngModel)]="createBody" name="body" #ctrl="ngModel" />
  <input type="submit" value="create Todo" />
</form>
<hr />
<h2>Edit a Todo</h2>
<form (submit)="updateTodo()">
  <input type="text" [(ngModel)]="editTitle" name="title" #ctrl="ngModel" />
  <input type="text" [(ngModel)]="editBody" name="body" #ctrl="ngModel" />
  <input type="submit" value="Edit Todo" />
</form>
<hr />
<h2>The Todos</h2>
<ul>
  <li *ngFor="let todo of todos">
    <h3>{{ todo.title }}</h3>
    <h4>{{ todo.body }}</h4>
    <button (click)="editSelect(todo)">Edit</button>
    <button (click)="deleteTodo(todo)">Delete</button>
  </li>
</ul>
Enter fullscreen mode Exit fullscreen mode

Svelte 前端

设置

  • 确保 todo api 服务器正在运行,并将终端导航到 todo 文件夹

  • 运行命令npx merced-spinup svelte todo_svelte_frontend

  • cd 进入 todo_svelte_frontend 文件夹并运行npm install

  • 运行npm run dev以在端口 5000 上启动开发服务器

显示我们的待办事项

Svelte 与 Vue 非常相似,所有组件的所有内容都放在一个文件中。因此,我们将像之前的运行一样,只使用一个组件,即 src/App.svelte。

就像之前一样,我们需要方法来提取数据并使用模板来呈现它们,就像这样……

<script>
  import { onMount } from "svelte"

  //Variable to hold todos
  let todos = []

  //base URL
  const baseURL = "http://localhost:3000/todos"

  //Method to pull data
  const getTodos = async () => {
    const response = await fetch(baseURL)
    const data = await response.json()
    todos = await data
  }

  onMount(() => {
    getTodos()
  })
</script>

<main>
  <h1>The Todo App</h1>
  {#each todos as todo}
  <div>
    <h2>{todo.title}</h2>
    <h3>{todo.body}</h3>
  </div>
  {/each}
</main>

<style></style>
Enter fullscreen mode Exit fullscreen mode

创建待办事项

再次,与往常相同的逻辑

  • 创建表单
  • 将表单绑定到值
  • 在提交表单并发出 post 请求时运行的函数

App.svelte

<script>
  import { onMount } from "svelte"

  //Variable to hold todos
  let todos = []

  //base URL
  const baseURL = "http://localhost:3000/todos"

  //Method to pull data
  const getTodos = async () => {
    const response = await fetch(baseURL)
    const data = await response.json()
    todos = await data
  }

  //Runs when component loads
  onMount(() => {
    getTodos()
  })

  //properties for create form
  let createTitle
  let createBody

  //create function for form submission
  const createTodo = async event => {
    event.preventDefault()
    await fetch(baseURL, {
      method: "post",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        title: createTitle,
        body: createBody,
      }),
    })

    //refetch todos
    getTodos()

    //reset form
    createTitle = ""
    createBody = ""
  }
</script>

<main>
  <h1>The Todo App</h1>
  <hr />
  <h2>Create a Todo</h2>
  <form on:submit="{createTodo}">
    <input type="text" bind:value="{createTitle}" />
    <input type="text" bind:value="{createBody}" />
    <input type="submit" value="Create Todo" />
  </form>
  <hr />
  <h2>The Todos</h2>
  {#each todos as todo}
  <div>
    <h2>{todo.title}</h2>
    <h3>{todo.body}</h3>
  </div>
  {/each}
</main>

<style></style>
Enter fullscreen mode Exit fullscreen mode

更新待办事项

  • 添加编辑表单的属性
  • 添加编辑表单
  • 添加方法来选择要编辑的项目
  • 绑定方法编辑按钮

<script>
    import {onMount} from 'svelte'

    //Variable to hold todos
    let todos = []

    //base URL
    const baseURL = "http://localhost:3000/todos"

    //Method to pull data
    const getTodos = async () => {
        const response = await fetch(baseURL)
        const data = await response.json()
        todos = await data
    }

    //Runs when component loads
    onMount(()=>{
        getTodos()
    })

    //properties for create form
    let createTitle;
    let createBody;

    //create function for form submission
    const createTodo = async (event) => {
        event.preventDefault()
        await fetch(baseURL, {
            method: "post",
            headers: {
                "Content-Type":"application/json"
            },
            body: JSON.stringify({
                title: createTitle,
                body: createBody
            })
        })

        //refetch todos
        getTodos()

        //reset form
        createTitle = ""
        createBody = ""
    }

    //properties for edit form
    let editTitle;
    let editBody;
    let editId

    //create function for form submission
    const updateTodo = async (event) => {
        event.preventDefault()
        await fetch(baseURL + "/" + editId, {
            method: "put",
            headers: {
                "Content-Type":"application/json"
            },
            body: JSON.stringify({
                title: editTitle,
                body: editBody
            })
        })

        //refetch todos
        getTodos()

        //reset form
        editTitle = ""
        editBody = ""
        editId = 0
    }

    const editSelect = (todo) => {
        editTitle = todo.title
        editBody = todo.body
        editId = todo.id
    }



</script>




<main>

<h1>The Todo App</h1>
<hr>
<h2>Create a Todo</h2>
    <form on:submit={createTodo}>
        <input type="text" bind:value={createTitle}/>
        <input type="text" bind:value={createBody}/>
        <input type="submit" value="Create Todo"/>
    </form>
<hr>
<h2>Edit a Todo</h2>
    <form on:submit={updateTodo}>
        <input type="text" bind:value={editTitle}/>
        <input type="text" bind:value={editBody}/>
        <input type="submit" value="Update Todo"/>
    </form>
<hr>
<h2>The Todos</h2>
{#each todos as todo}
    <div>
        <h2>{todo.title}</h2>
        <h3>{todo.body}</h3>
        <button on:click={(e) => editSelect(todo)}>Edit</button>
    </div>
{/each}

</main>




<style>
</style>

Enter fullscreen mode Exit fullscreen mode

删除待办事项

现在我们只需要创建一个删除方法,并通过内联函数将其连接到删除按钮,就完成了!


<script>
    import {onMount} from 'svelte'

    //Variable to hold todos
    let todos = []

    //base URL
    const baseURL = "http://localhost:3000/todos"

    //Method to pull data
    const getTodos = async () => {
        const response = await fetch(baseURL)
        const data = await response.json()
        todos = await data
    }

    //Runs when component loads
    onMount(()=>{
        getTodos()
    })

    //properties for create form
    let createTitle;
    let createBody;

    //create function for form submission
    const createTodo = async (event) => {
        event.preventDefault()
        await fetch(baseURL, {
            method: "post",
            headers: {
                "Content-Type":"application/json"
            },
            body: JSON.stringify({
                title: createTitle,
                body: createBody
            })
        })

        //refetch todos
        getTodos()

        //reset form
        createTitle = ""
        createBody = ""
    }

    //properties for edit form
    let editTitle;
    let editBody;
    let editId

    //create function for form submission
    const updateTodo = async (event) => {
        event.preventDefault()
        await fetch(baseURL + "/" + editId, {
            method: "put",
            headers: {
                "Content-Type":"application/json"
            },
            body: JSON.stringify({
                title: editTitle,
                body: editBody
            })
        })

        //refetch todos
        getTodos()

        //reset form
        editTitle = ""
        editBody = ""
        editId = 0
    }

    const editSelect = (todo) => {
        editTitle = todo.title
        editBody = todo.body
        editId = todo.id
    }

        const deleteTodo = async (todo) => {
        event.preventDefault()
        await fetch(baseURL + "/" + todo.id, {
            method: "delete",
        })

        //refetch todos
        getTodos()
    }



</script>




<main>

<h1>The Todo App</h1>
<hr>
<h2>Create a Todo</h2>
    <form on:submit={createTodo}>
        <input type="text" bind:value={createTitle}/>
        <input type="text" bind:value={createBody}/>
        <input type="submit" value="Create Todo"/>
    </form>
<hr>
<h2>Edit a Todo</h2>
    <form on:submit={updateTodo}>
        <input type="text" bind:value={editTitle}/>
        <input type="text" bind:value={editBody}/>
        <input type="submit" value="Update Todo"/>
    </form>
<hr>
<h2>The Todos</h2>
{#each todos as todo}
    <div>
        <h2>{todo.title}</h2>
        <h3>{todo.body}</h3>
        <button on:click={(e) => editSelect(todo)}>Edit</button>
        <button on:click={(e) => deleteTodo(todo)}>Delete</button>
    </div>
{/each}

</main>




<style>
</style>

Enter fullscreen mode Exit fullscreen mode

jQuery 前端

因此,让我们做一些不同的事情,使用 webpack 的 jQuery 怎么样!

设置

  • 确保您的 todo api 正在运行,并在终端中导航到您的 todo 文件夹

  • 运行命令npx merced-spinup jquerywebpack todo_jquery_frontend

  • cd 进入 todo_jquery_frontend 文件夹并运行npm install

  • npm run dev启动开发服务器

显示待办事项

  • 创建一个从 API 中提取待办事项的函数

  • 创建一个函数来获取这些待办事项并渲染列表

  • 调用后一个函数

src/index.js

import $ from "jquery"
import _ from "lodash"

//Adding the initial HTML to the body
$("body").append(`
<h1>The Todo App</h1>
<hr>
<h2>The Todos</h2>
<ul id="todolist">

</ul>
`)

//The UL for the Todo List
const $todoList = $("#todolist")

const baseURL = "http://localhost:3000/todos"

//function to get todos
const fetchTodos = async () => {
  const response = await fetch(baseURL)
  const data = await response.json()
  //return promise of data
  return data
}

//render todos to DOM
const renderTodos = async () => {
  const todos = await fetchTodos()

  todos.forEach(todo => {
    const $li = $("<li>")

    $li.html(`
        <h3>${todo.title}</h3>
        <h4>${todo.body}</h4>
        `)

    $todoList.append($li)
  })
}

// Initial Fetch of Todos
renderTodos()
Enter fullscreen mode Exit fullscreen mode

创建待办事项

  • 创建表单和变量来保存表单和输入
  • 为表单提交时创建一个函数
import $ from "jquery";
import _ from "lodash";

//Adding the initial HTML to the body
$("body").append(`
<h1>The Todo App</h1>
<hr>
<h2>Create a Todo</h2>
<form id="createForm">
<input type="text" name="createTitle"/>
<input type="text" name="createBody"/>
<input type="submit" value="Create Todo">
</form>
<hr>
<h2>The Todos</h2>
<ul id="todolist">

</ul>
`);

//The UL for the Todo List
const $todoList = $("#todolist");

//Create Form Variables
const $createForm = $("#createForm");
const $createTitle = $('input[name="createTitle');
const $createBody = $('input[name="createBody');
const baseURL = "http://localhost:3000/todos";

//function to get todos
const fetchTodos = async () => {
  const response = await fetch(baseURL);
  const data = await response.json();
  //return promise of data
  return data;
};

//render todos to DOM
const renderTodos = async () => {
  const todos = await fetchTodos();
  $todoList.empty();

  todos.forEach((todo) => {
    const $li = $("<li>");

    $li.html(`
        <h3>${todo.title}</h3>
        <h4>${todo.body}</h4>
        `);

    $todoList.append($li);
  });
};

//Function to Create a to do
const createTodo = async (event) => {
  event.preventDefault();
  await fetch(baseURL, {
    method: "post",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      title: $createTitle.val(),
      body: $createBody.val(),
    }),
  });
  renderTodos();
  $createTitle.val("");
  $createBody.val("");
};

//Add Event Listener to Form
$createForm.on("submit", createTodo);

// Initial Fetch of Todos
renderTodos();
Enter fullscreen mode Exit fullscreen mode

更新待办事项

  • 添加编辑按钮
  • 添加编辑表单
  • 为表单和输入创建变量
  • 创建函数来为选定的待办事项设置变量
  • 创建函数以在提交表单时发出发布请求。

import $ from "jquery";
import _ from "lodash";

//Adding the initial HTML to the body
$("body").append(`
<h1>The Todo App</h1>
<hr>
<h2>Create a Todo</h2>
<form id="createForm">
<input type="text" name="createTitle"/>
<input type="text" name="createBody"/>
<input type="submit" value="Create Todo">
</form>
<hr>
<form id="editForm">
<input type="text" name="editTitle"/>
<input type="text" name="editBody"/>
<input type="submit" value="Update Todo">
</form>
<hr>
<h2>The Todos</h2>
<ul id="todolist">

</ul>
`);

//The UL for the Todo List
const $todoList = $("#todolist");

//Create Form Variables
const $createForm = $("#createForm");
const $createTitle = $('input[name="createTitle"]');
const $createBody = $('input[name="createBody"]');

//Create Form Variables
const $editForm = $("#editForm");
const $editTitle = $('input[name="editTitle"]');
const $editBody = $('input[name="editBody"]');
let editId = 0

//API URL
const baseURL = "http://localhost:3000/todos";

//function to get todos
const fetchTodos = async () => {
  const response = await fetch(baseURL);
  const data = await response.json();
  //return promise of data
  return data;
};

//render todos to DOM
const renderTodos = async () => {
  const todos = await fetchTodos();
  $todoList.empty();

  todos.forEach((todo) => {
    const $li = $("<li>");

    $li.html(`
        <h3>${todo.title}</h3>
        <h4>${todo.body}</h4>
        <button id="${todo.id}editbutton">Edit</button>
        `);

    $todoList.append($li);

    $(`#${todo.id}editbutton`).on('click', () => {
        $editTitle.val(todo.title)
        $editBody.val(todo.body)
        editId = todo.id
    })
  });
};

//Function to Create a to do
const createTodo = async (event) => {
  event.preventDefault();
  await fetch(baseURL, {
    method: "post",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      title: $createTitle.val(),
      body: $createBody.val(),
    }),
  });
  renderTodos();
  $createTitle.val("");
  $createBody.val("");
};

//Function to update a to do
const updateTodo = async (event) => {
    event.preventDefault();
    await fetch(baseURL + "/" + editId, {
      method: "put",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        title: $editTitle.val(),
        body: $editBody.val(),
      }),
    });
    renderTodos();
    $editTitle.val("");
    $editBody.val("");
  };

//Add Event Listener to Form
$createForm.on("submit", createTodo);

//Add Event Listener to Form
$editForm.on("submit", updateTodo);

// Initial Fetch of Todos
renderTodos();11

Enter fullscreen mode Exit fullscreen mode

删除待办事项

终于……我们上次构建的最后一个函数了。我们只需要创建一个触发删除函数的删除按钮,就大功告成了!我们会在 renderTodos 函数中添加监听器时定义该函数,这样 todo 就包含在作用域中了。

import $ from "jquery";
import _ from "lodash";

//Adding the initial HTML to the body
$("body").append(`
<h1>The Todo App</h1>
<hr>
<h2>Create a Todo</h2>
<form id="createForm">
<input type="text" name="createTitle"/>
<input type="text" name="createBody"/>
<input type="submit" value="Create Todo">
</form>
<hr>
<form id="editForm">
<input type="text" name="editTitle"/>
<input type="text" name="editBody"/>
<input type="submit" value="Update Todo">
</form>
<hr>
<h2>The Todos</h2>
<ul id="todolist">

</ul>
`);

//The UL for the Todo List
const $todoList = $("#todolist");

//Create Form Variables
const $createForm = $("#createForm");
const $createTitle = $('input[name="createTitle"]');
const $createBody = $('input[name="createBody"]');

//Create Form Variables
const $editForm = $("#editForm");
const $editTitle = $('input[name="editTitle"]');
const $editBody = $('input[name="editBody"]');
let editId = 0

//API URL
const baseURL = "http://localhost:3000/todos";

//function to get todos
const fetchTodos = async () => {
  const response = await fetch(baseURL);
  const data = await response.json();
  //return promise of data
  return data;
};

//render todos to DOM
const renderTodos = async () => {
  const todos = await fetchTodos();
  $todoList.empty();

  todos.forEach((todo) => {
    const $li = $("<li>");

    $li.html(`
        <h3>${todo.title}</h3>
        <h4>${todo.body}</h4>
        <button id="${todo.id}editbutton">Edit</button>
        <button id="${todo.id}deletebutton">Delete</button>
        `);

    $todoList.append($li);

    //add function to edit button
    $(`#${todo.id}editbutton`).on('click', () => {
        $editTitle.val(todo.title)
        $editBody.val(todo.body)
        editId = todo.id
    })

    //add function to delete button
    $(`#${todo.id}deletebutton`).on('click', async () => {
        await fetch(baseURL + "/" + todo.id, {
            method: "delete"
        })
        renderTodos()
    })
  });
};

//Function to Create a to do
const createTodo = async (event) => {
  event.preventDefault();
  await fetch(baseURL, {
    method: "post",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      title: $createTitle.val(),
      body: $createBody.val(),
    }),
  });
  renderTodos();
  $createTitle.val("");
  $createBody.val("");
};

//Function to update a to do
const updateTodo = async (event) => {
    event.preventDefault();
    await fetch(baseURL + "/" + editId, {
      method: "put",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        title: $editTitle.val(),
        body: $editBody.val(),
      }),
    });
    renderTodos();
    $editTitle.val("");
    $editBody.val("");
  };

//Add Event Listener to Form
$createForm.on("submit", createTodo);

//Add Event Listener to Form
$editForm.on("submit", updateTodo);

// Initial Fetch of Todos
renderTodos();
Enter fullscreen mode Exit fullscreen mode

恭喜

你只需一个 API,就能构建出 5 个独立的前端应用!希望这能让你更深入地理解构建前端应用的不同方式以及 API 的模块化。


鏂囩珷鏉ユ簮锛�https://dev.to/alexmercedcoder/1-backend-5-frontends-todo-list-with-rails-react-angular-vue-svelte-and-jquery-18kp
PREV
开始使用开源无人机 CI GenAI LIVE!| 2025 年 6 月 4 日
NEXT
与 Alex Morton 一起无所畏惧地学习编码