React......在一天之內
在Visual Studio 2017中設定React專案
我過去曾多次在工作要求中找到“React”並在Angular中進行開發,我總是想知道這兩個單頁應用程式框架有多麼不同。好吧,我想我等不及了,在一次面試中,他們讓我開發一個簡單的React頁面。我感到很驚訝,但我們不應該在面試中說“我不能”。
首先要提到的是專案定義,如下所示:使用React建立一個帶有汽車零件輸入表單的簡單單頁應用程式。
- 汽車零件表單必須包含以下欄位
-
- ID(唯一識別符號,可以自動生成)。
- 部件號(從
- 零件名稱(汽車零件名稱)。
- 描述(描述汽車部件)。
- 汽車製造商名稱(克萊斯勒,道奇,福特等)
-
- 應用程式必須能夠將汽車零件條目儲存到資料庫
- 應用程式必須能夠編輯汽車零件條目
- 應用程式必須能夠刪除汽車零件條目
為了完成專案,我決定使用Visual Studio 2017,並在考慮這些要求的情況下探索React框架。幸運的是,網際網路在任何主題中都提供了無數的資源。所以按步驟我進行如下:
建立一個React專案
這是任何.NET開發人員都知道的一個步驟。但過程如下:
- 開啟
- 在彈出視窗中選擇專案型別,在本例中為ASP.NET Core Web Application。這是.NET Core模板組的一部分。
- 選擇專案的資料夾位置,指定專案名稱(React Demo)和解決方案名稱(React Demo)。
- 檢查“建立解決方案目錄”選項。
- 如果您有一個GIT帳戶,並且您希望將此專案保留在GIT中,那麼也要檢查該選項。
- 單擊確定後,下一個視窗將顯示不同的專案模板。選擇ReactJS模板,然後單擊“確定”。
- 第一個視窗應該是這樣的。第二個視窗非常簡單,所以我沒有在這裡顯示它,但它需要選擇
解決方案資源管理器將是這樣的:
這是我們在執行專案時得到的:
好吧!一個錯誤。如果我們選擇專案名稱並單擊“顯示所有檔案”,我們將注意到沒有node_modules資料夾。因此,我們的第一步是在專案檔案級別開啟命令提示符並執行NPM Install。第二次我們執行專案...... 搞定了!
對於一個我們可能不需要的額外內容的專案來說,這是一個很好的起點。在我們的例子中,我們不需要Counter和Fetch Data選單項和相關頁面。我們需要首頁,但我們可能需要更改該文字。為了提交React演示,我決定將主頁中的文字替換為專案要求。結果將是這樣的:
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.tsx和FetchData.tsx並新增一個新的carPart.tsx。要新增carPart tsx檔案,我剛僅新增一個typescript檔案,並將其副檔名更改為tsx。以下程式碼顯示了CarPart元件的詳細資訊。
此元件有兩個事件方法HandleSave和HandleDelete,單擊相應按鈕時會觸發這些方法。我們在這些方法中需要注意的是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>
Save
</button>
</div>
<div className='col-md-1'>
<button onClick={this.handleDelete} className='btn btn-info btn-lg'><span className="glyphicon glyphicon-remove"></span>
Delete
</button>
</div>
<div className='col-md-9'>
</div>
</div>
</div>
;
}
}
我新增的最後一項是專案服務。為此,我添加了一個services資料夾,並添加了一個名為CarPartService.cs的類和ICarPartService.cs介面。服務類具有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, polulate 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;
}
}
}
為了使用服務層,我決定將它注入控制器。所以我在Controllers資料夾中建立了一個CarPartsController並添加了以下程式碼:
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);
}
}
}
你可以注意到在演示中我在這個控制器中使用了兩個方法,SavePartData和Delete。我把了其他方法保留,以防我決定為這個專案新增更多功能......只是為了好玩。如果您注意到此控制器建構函式具有分配給_carPartService變數的carPartService引數。為了完成這項工作,我們需要將以下行新增到Starup.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並執行解決方案後,我們得到:
快樂的編碼!
原文地址:https://www.codeproject.com/Articles/1271257/React-in-one-day