65.9K
CodeProject 正在变化。 阅读更多。
Home

React… 一天之内!

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2018年12月11日

CPOL

5分钟阅读

viewsIcon

14954

downloadIcon

275

在 Visual Studio 2017 中设置 React 项目。

引言

我经常在工作要求中看到“React”这个词,而且我一直在用Angular开发,我一直好奇这两个单页面应用框架究竟有多大区别。好吧,我想我等不及了,因为在一次面试中,他们让我开发一个简单的React页面。这让我措手不及,但在面试中我们永远不应该说“我不会”。

首先要提到的是项目定义,它大致是这样的:使用React创建一个简单的单页面应用程序,其中包含一个汽车零部件录入表单。

  1. 汽车零部件录入表单必须包含以下字段:
    • ID(唯一标识符,可自动生成)
    • 零件号(随机5位数字,以00001开头)
    • 零件名称(汽车零件的名称)
    • 描述(描述汽车零件)
    • 汽车制造商名称(克莱斯勒、道奇、福特等)
  2. 应用程序必须能够将汽车零部件录入保存到数据库
  3. 应用程序必须能够编辑汽车零部件录入
  4. 应用程序必须能够删除汽车零部件录入

为了完成这个项目,我决定使用Visual Studio 2017,并考虑到这些需求来探索React框架。幸运的是,互联网为任何主题提供了无数的资源。所以,我按步骤进行如下:

第一步:创建React项目

这是任何.NET开发人员都非常熟悉的步骤。但过程是这样的:

  1. 打开Visual Studio 2017并选择创建新项目。
  2. 在弹出的窗口中,选择项目类型,本例中是ASP.NET Core Web Application。这是.NET Core模板组的一部分。
  3. 选择项目的文件夹位置,分配项目名称(React Demo)和解决方案名称(React Demo)。
  4. 勾选“为解决方案创建目录”选项。
  5. 如果您有GIT账户并希望将此项目持久化到GIT中,请也勾选该选项。
  6. 单击“确定”后,下一个窗口显示不同的项目模板。选择ReactJS模板并单击“确定”。
  7. 第一个窗口应该像这样。第二个窗口非常直接,所以我没有在这里显示它,但它需要选择ReactJS选项并单击“确定”。

解决方案资源管理器将如下所示:

运行项目时,我们会得到这个:

太棒了!一个错误。如果我们选择项目名称并单击“显示所有文件”,我们会注意到没有node_modules文件夹。所以我们的第一步是打开项目文件级别的命令提示符并运行NPM Install。第二次运行项目……Bingo!

这是一个项目的好起点,其中包含一些我们可能不需要的额外东西。在我们的例子中,我们不需要Counter和Fetch Data菜单项以及相关的页面。我们需要Home,但可能需要更改其文本。为了提交一个React Demo,我决定将Home页面的文本替换为项目需求。结果将是这样的:

import * as React from 'react';
import { RouteComponentProps } from 'react-router';
 export class Home extends React.Component<RouteComponentProps<{}>, {}> {
   public render() {
       return <div>
           <h1>React Demo</h1>
           <p><u>Tasks:</u></p>
           <p>Make a simple one-page application with a car part entry form.</p>
           <p>Car part entry form must have the following fields:</p>
           <ul>
               <li>ID (unique identifier, can be automatically generated).</li>
               <li>Part Number (random 5-digit number starting with 00001.</li>
               <li>Part name (name of car part. </li>
               <li>Description (describe the car part).</li>
               <li>Car Manufacturer Name (Chrysler, Dodge, Ford, etc.)</li>
           </ul>
           <p>App must be able to save the car part entries to a database</p>
           <p>App must be able to edit car part entries</p>
           <p>App must be able to remove car part entries</p>
       </div>;
   }
}

另外,我决定删除Counter和Fetch Data,并添加React Demo。第一步很容易,只需打开Home.tsx文件并替换文本。第二步需要您打开NavMenu.tsx文件。在该文件中,删除这些行:

<li>
                           <NavLink to={ '/counter' } 
                           activeClassName='active'>
                               <span className='glyphicon 
                               glyphicon-education'></span> Counter
                           </NavLink>
                       </li>
                       <li>
                           <NavLink to={ '/fetchdata' } 
                           activeClassName='active'>
                               <span className='glyphicon 
                               glyphicon-th-list'></span> Fetch data
                           </NavLink>
                       </li>

并用这些行替换:

<li>
                           <NavLink to={ '/reactdemo' } 
                           activeClassName='active'>
                               <span className='glyphicon 
                               glyphicon-education'></span> React Demo
                           </NavLink>
                       </li>

同样,在routes.tsx文件中,删除counter和fetchdata路由,并像这样用“carpart”路由替换它们:

import * as React from 'react';
import { Route } from 'react-router-dom';
import { Layout } from './components/Layout';
import { Home } from './components/Home';;
import { CarPart } from './components/ CarPart;
export const routes = <Layout>
   <Route exact path='/' component={ Home } />
   <Route path='/ carpart component={ CarPart } />
</Layout>;

我们可以删除Counter.tsxFetchData.tsx,并添加一个新的carPart.tsx。为了添加carPart tsx文件,我只是添加了一个typescript文件并将其扩展名更改为tsx。下面的代码显示了CarPart组件的详细信息:

此组件有两个事件方法HandleSaveHandleDelete,在单击相应的按钮时触发。我们要在这些方法中观察的是React的Fetch调用。对于HandleSave,它包括标头数据,并将对象作为JSON对象的一部分传递到标头中,这样,在控制器中,SavePartData方法中类型为CarPartModel的参数carPart将被[FromModel]修饰,这意味着不需要映射任何内容即可传递对象实例。

对于HandleDelete,唯一需要的参数是要从数据库中删除的零件ID。

import 'bootstrap/dist/css/bootstrap.min.css';
import * as React from 'react';
import { RouteComponentProps } from 'react-router';
import Link, { LinkedComponent } from 'valuelink';
interface CarPartModel {
  ID: number;
  PartNumber: number;
  PartName: string;
  Description: string;
  CarManufacturer: string;
}
const carManufacturers = [
  { label: "Ford", value: 1 },
  { label: "Chevrolett", value: 2 },
  { label: "Tesla", value: 3 },
  { label: "Chrysler", value: 4 },
  { label: "Honda", value: 5 },
  { label: "Toyota", value: 6 },
];
export class CarPart extends React.Component<RouteComponentProps<{}>, CarPartModel> {
  state: CarPartModel = {
      ID: 0,
      PartNumber: 0,
      PartName: '',
      Description: '',
      CarManufacturer: ''
  };
  constructor() {
      super();
      this.state = {
          ID: 0,
          PartNumber: 0,
          PartName: '',
          Description: '',
          CarManufacturer: ''
      };
      this.handleSave = this.handleSave.bind(this);
      this.handleDelete = this.handleDelete.bind(this);
  }
  handleSave = () => {
      fetch('api/CarParts/SavePartData', {
          method: 'post',
          headers: new Headers({
              "Content-Type": "application/json",
              Accept: "application/json"
          }),
          body: JSON.stringify(this.state)
      }).then(function (response) {
          if (response.status !== 200) {
              console.log('fetch returned not ok' + response.status);
          }
      }).catch(function (err) {
          console.log(`error: ${err}`);
          })
  }
  handleDelete = () => {
      if (!confirm("Do you want to delete employee with Id: " + this.state.ID))
          return;
      else {
          fetch('api/CarParts/Delete/' + this.state.ID, {
              method: 'delete'
          }).then(data => {
              this.state =
                  {
                      ID: 0,
                      PartNumber: 0,
                      PartName: '',
                      Description: '',
                      CarManufacturer: ''
                  };
          });
      } 
  }
  public handleIdChange(event: any): void {
      this.state.ID = event.target.value;
  }
  public handlePartNumberChange(event: any): void {
      this.state.PartNumber = event.target.value;
  }
  public handlePartNameChange(event: any): void {
      this.state.PartName = event.target.value;
  }
  public handleDescriptionChange(event: any): void {
      this.state.Description = event.target.value;
  }
  public handleManufacturerChange(event: any): void {
      this.state.CarManufacturer = event.target.value;
  }
  public render() {
      return <div>
          <h1>Car Parts</h1>
          <div className='row'>
              <div className='col-md-2'>
                  <label>Part Id: </label>
              </div>
              <div className='col-md-2'>
                  <input className="form-control" type="text" 
                  name="partId" 
                  onChange={e => this.handleIdChange(e)} required />
              </div>
              <div className='col-md-8'>
              </div>
          </div>
          <div className='row'>
              <div className='col-md-2'>
                  <label>Part Number: </label>
              </div>
              <div className='col-md-2'>
                  <input className="form-control" 
                  type="text " name="partNumber" 
                  onChange={e => this.handlePartNumberChange(e)} required />
              </div>
              <div className='col-md-8'>
              </div>
          </div>
          <div className='row'>
              <div className='col-md-2'>
                  <label>Part Name: </label>
              </div>
              <div className='col-md-2'>
                  <input className="form-control" 
                  type="text" name="partName" 
                  onChange={e => this.handlePartNameChange(e)} required />
              </div>
              <div className='col-md-8'>
              </div>               
          </div>
          <div className='row'>
              <div className='col-md-2'>
                  <label>Description: </label>
              </div>
              <div className='col-md-2'>
                  <input className="form-control" 
                  type="text" name="description" 
                  onChange={e => this.handleDescriptionChange(e)} required />
              </div>
              <div className='col-md-8'>
              </div>
          </div>
          <div className='row'>
              <div className='col-md-2'>
                  <label>Car Manufacturer:</label>
              </div>
              <div className='col-md-2'>
                  <select onChange={e => this.handleManufacturerChange(e)}>
                      <option value="volvo">Volvo</option>
                      <option value="saab">Saab</option>
                      <option value="mercedes">Mercedes</option>
                      <option value="chrysler">Chrysler
                      </option><option value="saab">Saab</option>
                      <option value="dodge">Dodge</option>
                      <option value="ford">Ford</option>
                  </select>
              </div>
              <div className='col-md-8'>
              </div>               
          </div>
          <div className='row mt-3'> </div>
          <div className='row'>
              <div className='col-md-1'>
                  <button onClick={this.handleSave} className='btn 
                  btn-info btn-lg'><span className="glyphicon glyphicon-ok"></span>
                      &nbsp;&nbsp;Save
                  </button>
              </div>
              <div className='col-md-1'>
                  <button onClick={this.handleDelete} 
                  className='btn btn-info btn-lg'>
                  <span className="glyphicon glyphicon-remove"></span>
                      &nbsp;&nbsp;Delete
                  </button>
              </div>
              <div className='col-md-9'>
              </div>
          </div>
          </div>
          ;
  }
}

我添加的最后一项是项目服务。为此,我添加了一个services文件夹,并添加了一个名为CarPartService.csICarPartService.cs的类。service类包含C#中的实际代码,它将与数据访问层(DAL)交互以执行CRUD操作。我没有在演示中包含它,因为它超出了项目目的的范围,该目的是展示使用React代码的能力。但为了测试目的,请在服务方法中设置断点并运行应用程序进行调试。

ICarPartInterface.cs的代码如下:

using ReactSample.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace ReactSample.Services
{
   public interface ICarPartService
   {
       CarPartViewModel GetCartPart(int carPartId);
       bool SaveCartPart(CarPartViewModel carPart);
       bool UpdateCarPart(CarPartViewModel carPart);
       bool Delete(int carPartId);
   }
}

CarPartService.cs的代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ReactSample.ViewModels;
namespace ReactSample.Services
{
   public class CarPartsService : ICarPartService
   {
       public bool Delete(int carPartId)
       {
           try
           {
               // Find the car part associated with the given Id and delete it
               return true;
           }
           catch(Exception ex)
           {
               // Log error message in case of error
           }
           return false;
       }
       public CarPartViewModel GetCartPart(int carPartId)
       {
           var carPart = new CarPartViewModel();
           try
           {
               // Find the car part associated with the given Id, 
               // populate carPart object and return it
               return carPart;
           }
           catch (Exception ex)
           {
               // Log error message in case of error
           }
           return null;
       }
       /// <summary>
       /// Receives car part object and query database
       /// if record exists then updates
       /// if record does not exist then inserts
       /// </summary>
       /// <param name="carPart"></param>
       /// <returns></returns>
       public bool SaveCartPart(CarPartViewModel carPart)
       {
           try
           {
               // Save the car part object into database
               return true;
           }
           catch (Exception ex)
           {
               // Log error message in case of error
           }
           return false;
       }
       public bool UpdateCarPart(CarPartViewModel carPart)
       {
           try
           {
               // Find the car part associated with the given Id 
               // and update fields using parameter object
               return true;
           }
           catch (Exception ex)
           {
               // Log error message in case of error
           }
           return false;
       }
   }
}

为了使用服务层,我决定将其注入到控制器中。所以我创建了一个CarPartsControllerControllers文件夹中,并添加了以下代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using ReactSample.Services;
using ReactSample.ViewModels;
namespace ReactSample.Controllers
{
   [Produces("application/json")]
   [Route("api/[controller]")]
   public class CarPartsController : Controller
   {
       ICarPartService _carPartService;
       private readonly string[] CarManufacaturers = new[]
       {
           "Chryssler", "Dodge", "Ford", 
           "Jeep", "Chevrolett", "Honda", 
           "Toyota", "Subaru", "Nisan", "Kia"
       };
       public CarPartsController(
           ICarPartService carPartService
       )
       {
           _carPartService = carPartService;
       }
       [HttpGet("{id}", Name = "Get")]
       public CarPartViewModel Get(int id)
       {
           return _carPartService.GetCartPart(id);
       }
       [HttpPost("SavePartData")]
       public void SavePartData([FromBody]CarPartViewModel carPart)
       {
           _carPartService.SaveCartPart(carPart);
       }
       // PUT: api/Sample/5
       [HttpPut("{id}")]
       public void Update(int id, [FromBody]CarPartViewModel carPart)
       {
           _carPartService.UpdateCarPart(carPart);
       }
       // DELETE: api/ApiWithActions/5
       [HttpDelete("Delete/{id}")]
       public void Delete(int id)
       {
           _carPartService.Delete(id);
       }
   }
}

您会注意到,在演示中,我在此控制器中使用了两个方法SavePartDataDelete。我保留了其他方法,以防我决定为这个项目添加更多功能……纯属娱乐。如果您注意到此控制器构造函数有carPartService参数,它被赋给了_carPartService变量。为了使此工作正常,我们需要在Startup.cs文件的ConfigureServices方法中添加以下几行,该方法最终将如下所示:

// This method gets called by the runtime. Use this method to add services to the container.
       public void ConfigureServices(IServiceCollection services)
       {
           services.AddScoped<ICarPartService, CarPartsService>();
           services.AddMvc();
       }

添加bootstrap并运行解决方案后,我们得到这个:

祝您编码愉快!

© . All rights reserved.