在网上购物

分享于 

38分钟阅读

Web开发

  繁體

按以下步骤执行收费:

介绍

在本文中,你将能够了解如何将响应技术应用到新的或者已经存在的ASP.NET MVC应用程序。 这里没有关注与 ASP.NET MVC集成的文章,所以我希望这个项目能够帮助读者搜索它。

背景

上个月我成为一个失业的高级软件开发人员,试图在市场上找到我的位置。 不幸的是这些小项目都没有被雇用,所以我写本文的主要动机是展示我最近研究的一些代码。 我认为最好与社区共享代码,帮助别人解决我发现的一些问题。

软件要求

对于本文的开发,我使用了:

  • SQL Server Express ( 在这里下载 )
  • Visual Studio 社区 2015更新 3或者更高级( 在这里下载它 )
  • Asp.Net 是基于established设计模式和 ASP.NET 和. NET 框架的强大设计模式,用于构建可以伸缩的基于标准的web应用程序。
  • 微软 ASP.NET 网页优化框架( 通过 nuget ) ASP.NET 优化引入了一种捆绑和优化CSS和JavaScript文件的方法。
  • Web分析器( https://visualstudiogallery.msdn.microsoft.com/6edc26d4-47d8-4987-82ee-7c820d79be1d ) 为 JavaScript。app。JSX。CSS等提供了 Visual Studio 中的static 分析
  • Web扩展包( https://visualstudiogallery.msdn.microsoft.com/f3b504c6-0095-42f1-a989-51d5fc2a8459 ) 最简单的方法是为最终的Web开发体验设置 Visual Studio。
  • ReactJS.NET 核心( 通过 nuget ) React.js 和. NET 工具。 重要:这个包本身并不做很多事情;你可能需要集成包(。React.Web. Mvc4 )。 有关详细信息,用法示例和示例代码,请参考项目站点 (http://reactjs.net/)。
  • ReactJS.NET (。4和 5 ) ( 通过 nuget )
  • Web站点优化框架( 通过 nuget ) 允许你在 ASP.NET Web优化框架中通过Babel来实现 JavaScript。
  • 反向( 通过 nuget ) Showdown是 Markdown ( 标记语言)的一个javascript端口。

代码第一个- Entity Framework

我选择了 Entity Framework 6提供的代码第一项技术来创建一个 Context,它映射了我们的实体( C# classes/properties/types) 到数据库对象的(tables/columns/data 类型)。 甚至数据库创建也是由代码提供的,所以我们可以避免这个项目中的任何事务sql脚本。

publicclass Context : DbContext
{
 public DbSet<CartItem> CartItem { get; set; }
 public DbSet<Product> Product { get; set; }
}

清单 1.显示 Entity Framework 代码第一上下文类的Context.cs 文件。

生成数据库的数据实体集相当简单,只包含 CartItemProduct 实体。 其他( 客户信息,总计,分类汇总,折扣,等等 ) 要么硬编码在后端,要么从 CartItem 值自动计算。

[Table("Product")]publicclass Product
{
 [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
 publicint Id { get; set; }
 publicstring SKU { get; set; }
 publicstring Description { get; set; }
 publicstring SmallImagePath { get; set; }
 publicstring LargeImagePath { get; set; }
 publicdecimal Price { get; set; }
}
[Table("CartItem")]publicclass CartItem
{
 [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
 publicint Id { get; set; }
 [ForeignKey("Product")]
 publicint ProductId { get; set; }
 [Required]
 publicvirtual Product Product { get; set; }
 publicint Quantity { get; set; }
}

清单 2.我们项目的两个实体: 产品和 CartItem。

应用程序启动时,它检查是否已经存在在connectionstring中定义的SQL Server 数据库。 如果不是,它使用代码首先描述 上面,使用 CartItemProduct 表创建新数据库并相应地填充产品:

publicclass Global : HttpApplication
{
 void Application_Start(object sender, EventArgs e)
 {
. . .
 var checkoutManager = AutoFacHelper.Resolve<icheckoutmanager>();
 checkoutManager.InitializeDB();
 }
}</icheckoutmanager>

清单 3。Global.asax. cs中的数据库初始化

public Context InitializeDB()
{
 var db = new Context();
 if (!db.Database.Exists())
 {
 db.Database.CreateIfNotExists();
 var products = new string[]
 {
 "10 Million Member CodeProject T-Shirt|3399",
 "Women's T-Shirt|3399",
 "CodeProject.com Body Suit|1399",
 "CodeProject Mug Mugs|1099",
 "RootAdmin Mug|1099",
 "Drinking Glass|1099",
 "Stein|1399",
 "Mousepad|1099",
 "Square Sticker|299",
 };
 var index = 1;
 foreach (var p in products)
 {
 var description = p.Split('|')[0];
 var price = decimal.Parse(p.Split('|')[1])/100M;
 var product =
 db.Product.Add(new Product
 {
 SKU = Guid.NewGuid().ToString(),
 SmallImagePath = string.Format("Images/Products/small_{0}.jpg", index),
 LargeImagePath = string.Format("Images/Products/large_{0}.jpg", index),
 Description = description,
 Price = price
 });
 var cartItem =
 db.CartItem.Add(new CartItem
 {
 Product = product,
 Quantity = 1 });
 index++;
 }
 db.SaveChanges();
 }
 return db;
}

清单 4.CheckoutManager.cs 中的数据库设置

React.js

response是一个JavaScript库,用于构建由Facebook创建的用户界面。 其他JavaScript框架/库实现了 MV* Pattern ( 像在 Knockjout JS或者MVC中一样,像在 Angular JS中一样),React是一个只关注第五部分的库。 作为 Backbone的一个完全特色的JavaScript,ReactJS的目标只是做一件事,并且做得很好。

If技术雷达领域中的2016的热门趋势,如果你问大多数科技专家,他们很可能会提到 React.js 技术的发展。

引用:

由于前端JavaScript框架的雪崩,React.js 因它的围绕React性数据流的设计而脱颖而出。 只允许单向数据绑定大大简化了呈现逻辑,并避免了许多常见的困扰其他框架编写的应用程序的问题。 我们看到了 React.js 在越来越多的项目上的好处,同时我们仍然关心它的他流行框架。 这已经导致 React.js 成为我们的JavaScript框架的默认选择。

单元测试

应用程序中的单元测试旨在满足以下业务要求:

  • 在 $500.00和 $599.99之间购买应授予 5 %的贴现率
  • 在 $600.00和 $699.99之间购买应授予 10 %的贴现率
  • 购买 上面 $700.00应授予 15 %的贴现率

注意每个限制值如何针对上面的需求 table 进行测试:

[TestClass]publicclass DiscountManagerTest
{
 public DiscountManager discountManager;
 [TestInitialize]
 publicvoid Initialize()
 {
 discountManager = new DiscountManager();
 }
 [TestMethod]
 publicvoid GetDiscount_0_Should_Return_Rate_0_And_Value_0()
 {
 var rule = discountManager.GetDiscount(0);
 Assert.AreEqual(0, rule.Rate);
 Assert.AreEqual(0, rule.CalculatedDiscount);
 }
 [TestMethod]
 publicvoid GetDiscount_499_99_Should_Return_Rate_0_And_Value_0()
 {
 var rule = discountManager.GetDiscount(499.99M);
 Assert.AreEqual(0, rule.Rate);
 Assert.AreEqual(0, rule.CalculatedDiscount);
 }
 [TestMethod]
 publicvoid GetDiscount_500M_Should_Return_Rate_5_And_Value_25()
 {
 var rule = discountManager.GetDiscount(500M);
 Assert.AreEqual(.05M, rule.Rate);
 Assert.AreEqual(25, rule.CalculatedDiscount);
 }
 [TestMethod]
 publicvoid GetDiscount_599_99M_Should_Return_Rate_5_And_Value_25()
 {
 var rule = discountManager.GetDiscount(599.99M);
 Assert.AreEqual(.05M, rule.Rate);
 Assert.AreEqual(30M, rule.CalculatedDiscount);
 }
 [TestMethod]
 publicvoid GetDiscount_600M_Should_Return_Rate_10_And_Value_60()
 {
 var rule = discountManager.GetDiscount(600M);
 Assert.AreEqual(.10M, rule.Rate);
 Assert.AreEqual(60M, rule.CalculatedDiscount);
 }
 [TestMethod]
 publicvoid GetDiscount_699_99M_Should_Return_Rate_10_And_Value_70M()
 {
 var rule = discountManager.GetDiscount(699.99M);
 Assert.AreEqual(.10M, rule.Rate);
 Assert.AreEqual(70M, rule.CalculatedDiscount);
 }
 [TestMethod]
 publicvoid GetDiscount_700M_Should_Return_Rate_15_And_Value_105()
 {
 var rule = discountManager.GetDiscount(700M);
 Assert.AreEqual(.15M, rule.Rate);
 Assert.AreEqual(105M, rule.CalculatedDiscount);
 }
 [TestMethod]
 publicvoid GetDiscount_10000M_Should_Return_Rate_15_And_Value_1500()
 {
 var rule = discountManager.GetDiscount(10000M);
 Assert.AreEqual(.15M, rule.Rate);
 Assert.AreEqual(1500M, rule.CalculatedDiscount);
 }

清单 5 DiscountManagerTest.cs.的. The 内容

图 1.为DiscountManagerTest类运行单元测试。

产品目录

图 2.产品目录视图。

产品目录视图具有简单的产品 carousel 控件。 如果使用搜索产品 carousel,则最终会找到类似的解决方案,比如

产品 carousel 非常棒,因为无尽的动画和能够只使用一小部分的网页空间显示许多产品。

产品 carousel 通过 Razor 视图引擎在服务器端呈现。 carousel 同时显示4 个产品,因此 Index.cshtml 视图中的代码定义了一个 foreach 循环,循环遍历 4个产品的"页面"。

@using ReactShop.Core;
@model List<ReactShop.Core.DTOs.ProductDTO>@{
 ViewBag.Title ="ReactShop";
}<divclass="container"><divclass="row"><divclass="row"><divclass="col-md-9"><h3> Product Catalog
 </h3></div><divclass="col-md-3"><!-- Controls --><divclass="controls pull-right hidden-xs"><aclass="left fa fa-chevron-left btn btn-success"href="#carousel-example"data-slide="prev"></a><aclass="right fa fa-chevron-right btn btn-success"href="#carousel-example"data-slide="next"></a></div></div></div><divid="carousel-example"class="carousel slide hidden-xs"data-ride="carousel"><!-- Wrapper for slides --><divclass="carousel-inner"> @foreach (var pageIndex in Enumerable.Range(0, (Model.Count() - 1)/4))
 {
 <divclass="item@(pageIndex == 0?"active":"")"><divclass="row"> @using(Html.BeginForm("AddToCart","Home"))
 { 
 @Html.AntiForgeryToken() 
 foreach (var product in Model.Skip(pageIndex * 4).Take(4))
 {
 <divclass="col-sm-3"><divclass="col-item"><divclass="photo"><imgsrc="~/@product.SmallImagePath"class="img-responsive"alt="a"width="350"height="260"/></div><divclass="info"><divclass="row"><divclass="price col-md-6"><h5class="truncate"> @product.Description
 </h5><h5class="price-line"> $<spanclass="price-text-color">@product.Price</span></h5></div></div><divclass="separator clear-left"><pclass="btn-add"><buttontype="submit"class="btn btn-link"name="SKU"value="@product.SKU"><iclass="fa fa-shopping-cart"aria-hidden="false"></i> Add to Cart
 </button></p><pclass="btn-details"><buttontype="button"class="btn btn-link"name="SKU"value="@product.SKU"><iclass="fa fa-list"></i><ahref=""class="hidden-sm"></a> Details
 </button></p></div><divclass="clearfix"></div></div></div></div> } 
 }
 </div></div> }
 </div></div></div></div>

清单 6 Index.cshtml 视图的. The 内容。

添加到购物车

注意防伪造令牌的使用。 这是 ASP.NET MVC提供的一个安全措施,作为保护你应用于跨站点请求伪造( CSRF )的手段。 反向伪造令牌传递给客户端页,然后回到由控制器接收的提交动作来确保请求。 记住永远不要信任任何人,在你的应用程序中总是使用 AntiForgeryToken

[AcceptVerbs(HttpVerbs.Post)][ValidateAntiForgeryToken]public ActionResult AddToCart(string SKU)
{
 checkoutManager.SaveCart(new Core.DTOs.CartItemDTO
 {
 SKU = SKU,
 Quantity = 1 });
 return RedirectToAction("Cart", "Home");
}

清单 5.从 HomeController.cs 截断代码,显示 AddToCart 方法上的ValidateAntiForgeryToken 属性。

然后 CartItemDTO 将传递给 CheckoutManager 类的SaveCart 方法,它将根据 CartItemDTO 对象中提供的数量执行数据库操作( 更新或者删除)。

publicvoid SaveCart(CartItemDTO newOrEditItem)
{
 try {
 if (newOrEditItem.Quantity <0)
 newOrEditItem.Quantity = 0;
 using (var db = new Context())
 {
 var product = db.Product.Where(p => p.SKU == newOrEditItem.SKU).Single();
 var cartItem =
 (from ci in db.CartItem
 join p in db.Product on ci.ProductId equals p.Id
 where p.SKU == newOrEditItem.SKU
 select ci)
. SingleOrDefault();
 if (cartItem!= null)
 {
 if (newOrEditItem.Quantity == 0)
 db.CartItem.Remove(cartItem);
 else {
 cartItem.Quantity = newOrEditItem.Quantity;
 cartItem.Product = product;
 }
 }
 else {
 db.CartItem.Add(new CartItem
 {
 Product = product,
 Quantity = newOrEditItem.Quantity
 });
 }
 db.SaveChanges();
 }
 }
 catch (DbEntityValidationException dbEx)
 {
 foreach (var validationErrors in dbEx.EntityValidationErrors)
 {
 foreach (var validationError in validationErrors.ValidationErrors)
 {
 Trace.TraceInformation("Property: {0} Error: {1}",
 validationError.PropertyName,
 validationError.ErrorMessage);
 }
 }
 }
}

清单 8.显示 CheckoutManager 类的SaveCart 方法的代码 Fragment。

购物车

图 3。购物车视图。

产品目录相比,购物车页面以非常不同的方式呈现。 首先,Razor 引擎不用于直接呈现视图。 相反,Razor 调用 React.Web. Mvc.HtmlHelperExtensions 类的React 方法并将模型传递给它。 Razor 接着呈现被声明为响应组件的CartView

var CartItem = React.createClass({
 getInitialState: function () {
 var item = this.props.model;
 return {
 SKU: item.SKU,
 SmallImagePath: item.SmallImagePath,
 LargeImagePath: item.LargeImagePath,
 Description: item.Description,
 SoldAndDeliveredBy: item.SoldAndDeliveredBy,
 Price: item.Price,
 Quantity: item.Quantity,
 Subtotal: item.Subtotal
 };
 },
 updateState: function (change) {
 this.setState(Object.assign({}, this.state, change))
 },
 handleIncrement: function () {
 this.postQuantity(this.state.Quantity + 1);
 },
 handleDecrement: function () {
 this.postQuantity(this.state.Quantity - 1);
 },
 removeItem: function () {
 this.postQuantity(0);
 },
 postQuantity: function (quantity, callback) {
 $('.overlay').show();
 $.post('/api/Cart',
 {
 SKU: this.props.model.SKU,
 Quantity: quantity,
 Price: this.props.model.Price
 })
. done(function (data) {
 for (var item of data.CartItems) {
 if (item.SKU == this.props.model.SKU) {
 this.updateState({ Quantity: item.Quantity, Subtotal: item.Subtotal });
 this.props.handleCartChange(data, item);
 return;
 }
 }
 }.bind(this))
. always(function () {
 $('.overlay').hide();
 });;
 },
 handleQuantityChanged: function (event) {
 var newQty = 1;
 var val = event.target.value;
 if (val &&!isNaN(val))
 newQty = parseInt(val);
 this.postQuantity(newQty);
 },
 render: function () {
 return (
 <Row className="vertical-align"> <Column md={2} className="justify-left"> <Row className="fullwidth"> <Column md={3}> <img src={'../' + this.state.SmallImagePath} width="80" height="80"/>
 </Column></Row></Column> <Column md={4} className="justify-left"> <Row className="fullwidth"> <Column md={9}> <span>{this.state.Description}</span></Column></Row></Column> <Column md={2} className="green justify-center"> <Dollars val={this.state.Price }/>
 </Column> <Column md={2} className="justify-center"> <div className="text-center"> <ButtonGroup>
 <input type="button" className="btn btn-default"value="-" onClick={this.handleDecrement}/>
 <input type="text" className="btn"value={this.state.Quantity} onChange={this.handleQuantityChanged }/>
 <input type="button" className="btn btn-default"value="+" onClick={this.handleIncrement}/>
 </ButtonGroup> <a onClick={this.removeItem} className="remove pointer">Remove</a></div></Column> <Column md={2} className="green justify-right"> <Dollars val={this.state.Subtotal}/>
 </Column></Row> );
 }
})class CartView extends React.Component {
 constructor(props) {
 super(props);
 this.state = {};
 var items = [];
 for (var i = 0; i <this.props.model.CartItems.length; i++) {
 var item = this.props.model.CartItems[i];
 items.push({
 SKU: item.SKU,
 SmallImagePath: item.SmallImagePath,
 LargeImagePath: item.LargeImagePath,
 Description: item.Description,
 SoldAndDeliveredBy: item.SoldAndDeliveredBy,
 Price: item.Price,
 Quantity: item.Quantity,
 Subtotal: item.Subtotal
 });
 }
 this.state = {
 canFinishOrder: true,
 items: items,
 Subtotal: this.props.model.Subtotal,
 DiscountRate: this.props.model.DiscountRate,
 DiscountValue: this.props.model.DiscountValue,
 Total: this.props.model.Total
 };
 }
 handleCartChange(cart, cartItem) {
 var newState = Object.assign({}, this.state, {
 Subtotal: cart.Subtotal,
 DiscountRate: cart.DiscountRate,
 DiscountValue: cart.DiscountValue,
 Total: cart.Total
 });
 if (cartItem.Quantity == 0) {
 newState.items.splice(newState.items.findIndex(i => i.SKU == cartItem.SKU), 1);
 }
 this.setState(newState);
 }
 render() {
 const header = (<Row className="vertical-align"> <Column md={6} className="justify-left">item(s)</Column> <Column md={2} className="justify-center">unit price</Column> <Column md={2} className="justify-center">quantity</Column> <Column md={2} className="justify-right">subtotal</Column></Row>);
 const body = (this.state.items.map(item => {
 return <CartItem key={item.SKU} model={item}
 handleCartChange={this.handleCartChange.bind(this)}/>;
 }
 ));
 const footer = (<Row>
 <Column md={7}></Column> <Column md={5} className="my-children-have-dividers"> <Row className="vertical-align"> <Column md={8} className="justify-right"> Subtotal ({this.state.items.length} <Pluralize value={this.state.items.length} singular="item" plural="items"/>):
 </Column> <Column md={4} className="green justify-right"> <span>
 <Dollars val={this.state.Subtotal}/>
 </span></Column></Row> { this.state.DiscountRate
?
 <Row className="vertical-align"> <Column md={8} className="justify-right"> Discount (<span>{this.state.DiscountRate}</span>%):
 </Column> <Column md={4} className="green justify-right"> <span>
 <Dollars val={this.state.DiscountValue}/>
 </span></Column></Row> : null }
 <Row className="vertical-align"> <Column md={12} className="justify-right"> <h3>
 Total: 
 <span className="green"> <Dollars val={this.state.Total}/>
 </span></h3></Column></Row></Column></Row>);
 return (
 <div className="cart"> {
 this.state.items.length == 0? null :
 <div>
 {/* TITLE */}
 <h3>Your shopping cart ({ this.state.items.length} <Pluralize value={this.state.items.length} singular="item" plural="items"/>)</h3> {/* NAVIGATION BUTTONS */}
 <Row>
 <Column md={3}> <a href={this.props.urlNewProduct}> <button type="button" className="btn btn-success">Add new product</button></a></Column> <Column md={3} className="pull-right"> <a href={this.props.urlCheckoutSuccess}> <button type="button" className="btn btn-success pull-right">Proceed to checkout</button></a></Column></Row> {/* NAVIGATION BUTTONS */}
 <br/>
 {/* CART PANEL */}
 <Panel header={header} footer={footer}> {body}
 </Panel> {/* CART PANEL */}
 {/* NAVIGATION BUTTONS */}
 <Row>
 <Column md={3}> <a href={this.props.urlNewProduct}> <button type="button" className="btn btn-success">Add new product</button></a></Column> <Column md={3} className="pull-right"> <a href={this.props.urlCheckoutSuccess}> <button type="button" className="btn btn-success pull-right">Proceed to checkout</button></a></Column></Row> {/* NAVIGATION BUTTONS */}
 </div> }
 {
 this.state.items.length >0? null :
 <div>
 <h1><br/><br/>:(</h1> <div>
 <h1>
 Oops! Your shopping cart is empty.
 </h1> <br/>
 <div className="empty-cart-content-message"> Enter more products and resume shopping.
 </div> <br/>
 <div>
 {
 this.state.canFinishOrder
?
 <a href={this.props.urlNewProduct}> <button type="button" className="btn btn-success">Enter new product</button></a> : null }
 </div></div></div> }
 </div> );
 }
}

清单 9 Cart.jsx 文件的. The 内容。

JSX文件

首先,JSX文件看起来有点奇怪,当你不习惯它的时候。 它看起来就像是在同一个地方混合了JavaScript和 HTML。 现在与 Angular JS工作方式比较。 Angular 似乎将JavaScript带到HTML中,而React似乎带来了 HTML。

当看到JSX文件时,可能会倾向于认为那些HTML块直接注入到HTML页面中。 但在现实中,这并不是。 相反,这些HTML部分是"transpiled"到真正的JavaScript代码中。 实际上,HTML标记只是JavaScript的一种不同表示形式。 最后,JSX代码作为普通旧JavaScript代码部署到客户端,并且这些HTML标记呈现为 React.createElement 方法的结构化链。 想象一下,大量重复的工作是一个一个一个地创建所有这些元素。 这就是为什么在最后React是纯 JavaScript。

图 4.jsx如何将"transpiled"转换为代码。 最后,它是所有的JavaScript。

扩展 React.Component

React允许你通过将复杂视图分解为较小的组件来创建复杂视图。

CartItem组件代表购物车(。显示产品说明,数量和价格) 中的每一行,购物车组件包含所有剩余的行。

每个响应组件必须实现 render 函数,后者又必须返回响应组件的树。 呈现函数返回的这里组件树可能是:

  • 一个非常简单的组件,如用于呈现单个 div HTML标记的代码,
  • 一个复杂的组件,如整个页面或者整个页面
  • 空值

在我们的例子中,购物车视图的唯一组件是:

  • 购物车
    • CartItem

ReactJS.Net,为什么会如此出色

我们的.NET 开发人员很幸运,可以让Facebook显示我们的平台爱好。 web服务集成了 ASP.NET MVC与后端的React,允许服务器端呈现,并对JavaScript代码进行了捆绑和缩小。

ReactJS.Net 为JavaScript编译提供 On-the-fly JSX。 这意味着. jsx 扩展的任何文件都将自动编译为JavaScript并在服务器端缓存,而不需要预编译文件。 查看直接浏览 CheckoutSuccess.jsx 文件时发生的情况:

图 5.由 ReactJS.NET 自动创建的由 CheckoutSuccess.jsx, 自动生成的JavaScript

ReactJS.NET 还提供服务器端组件呈现。 在常见的场景中,我们将创建我们的. jsx 组件,并将它的发送到浏览器。 浏览器可以采取一些预加载的JSON对象来表示车票项或者通过AJAX调用获取数据。 <a href="https://facebook.github.io/react/blog/2015/10/01/react-render-and-top-level-api.html" target="_blank">ReactDOM.render()</a> 使用初始状态呈现购物车。 但是借助 ReactJS.NET,,我们可以预先呈现已经在服务器端的cart ( 也就是说,物品,价值,折扣和总数)的初始状态,因为 ReactJS.Net 能够理解响应组件的响应语法。 xml文件,并知道如何处理JavaScript表达式 ( 也就是说,表达式的inside 花括号( {} ) ) 在将组件发送到浏览器之前,将 Model 和呈现的初始状态呈现给它。 这种方法非常方便,在许多情况下可以最小化视图引擎( 如 Razor 或者 aspx )的使用。

@Html.React("CheckoutSuccessView", new{
 title = "React JS.Net + React-Bootstrap",
 model = Model
})

清单 10. The 模型被传递给 CheckoutSuccessView 组件,ReactJS.Net pre呈现服务器端的所有内容。 绝对不可思议 !

React引导

我们还可以通过 Bootstrap release和响应启动整个框架,但它包含了一个完整的组件集,它包含了一个完整的组件集。

尽管 Bootstrap 是一个非常有用的网页设计工具箱,但有时也很难读和写,因为在最后,它变成了一个 div 标签,许多不同的css类难以记住。

这就是引导引导的作用。 看看下面的React引导剪断从jsx提取。

<RowclassName="vertical-align"><Columnmd={2} className="justify-left"><RowclassName="fullwidth"><Columnmd={3}><imgsrc={'../'+this.state.SmallImagePath}width="80"height="80"/></Column></Row></Column>

"。"。并将它的与编写相同视图所需的常规 HTML Bootstrap 标记进行比较:

<divclass="row vertical-align"><divclass="col-md-2 justify-left"><divclass="row fullwidth"><divclass="col-md-3"><imgsrc="../Images/Products/small_7.jpg"width="80"height="80"></div></div></div>

道具 vs 状态

关于响应实现最常见的askes问题之一是: "我怎么知道何时使用道具或者状态""?

每个响应组件都使用两个特殊的obejcts: ""( 来自"属性") 和"状态"。 它们都被用作原始数据,富HTML将在页面中呈现。

我们可以在这里找到关于 propsstate之间的差异的深度解释:

Questionpropsstate
可以从父组件获取初始值?是的是的
可以由父组件更改?是的不是
可以设置默认值 inside 组件?是的是的
可以更改 inside 组件?不是是的
可以设置子组件的初始值?是的是的
在子部件中可以更改?是的不是

源:https://github.com/uberVU/react-guide/blob/master/props-vs-state.md

从React doc:

引用:

道具是不可变的:它们是从父节点传递的,由父对象为"拥有"。 为了实现交互,我们向组件引入可变状态。 this.state 是指向组件的private,可以通过调用 this.setState() 来更改。 当状态被更新时,组件会呈现自身。

自定义组件

在这个项目中我们实现了两个小组件,它们作为助手工作,不做很多事情,但是帮助我们的其他主要组件变得混乱。

更新数量

购物车项目的数量可以 4种不同的方式更新:

  • 单击缩小按钮
  • 点击增加按钮
  • 通过在文本框中直接键入数量
  • 通过单击删除链接

每个操作都会触发调用/api/Cart Web API 方法的postQuantity 函数,然后改变数据库中的数量并返回新的快照。 在 post ajax方法结束时,postQuantity 函数通过调用 updateState 函数将视图的state 更新为新的状态。

在产品移除时,通过将项目的数量更改为零来删除项目。 这将触发与 上面 相同的流。 唯一的区别是,在更新的结尾,cart项将从组件状态中删除。

签出详细信息

图 4.签出成功视图。

虽然 CheckoutSuccess 视图不包含任何交互( 除了"回到产品目录"按钮),但它完全是作为单个响应组件实现的。 我们可以利用react库的组件提供的简单语法,我们已经解释过了。 所有绑定值都通过 props 传递,并且不需要使用响应对象的state

@using System.Web.Optimization
@using System.Collections.Generic
@model ReactShop.Core.DTOs.CheckoutSummaryDTO
@{
 Layout = "~/Views/Shared/_Layout.cshtml";
}
@{
 ViewBag.Title = "ReactShop";
}
@Html.React("CheckoutSuccessView", new{
 title = "React JS.Net + React-Bootstrap",
 model = Model
})
@Scripts.Render(" ~/bundles/js")

清单 10 CheckoutSuccess.cshtml 文件的. The 内容。

class CheckoutSuccessView extends React.Component {
 constructor(props) {
 super(props);
 }
 render() {
 return (
 <div>
 <br/>
 <Row>
 <span className="fa fa-check-circle"></span></Row> 
 <h3 className="text-center"> <span>Your order has been received. Thank you for your purchase!</span></h3> <h4 className="text-center"> <span>Your order # is:</span> <span className="green">{this.props.model.OrderNumber}</span></h4> <br/>
 <h3>
 Order Information
 </h3> <Panel>
 <Row>
 <Column md={6}> <h4>
 <span>Order No:</span> <span className="green">{this.props.model.OrderNumber}</span></h4> <p>
 You will receive a confirmation e-mail with the details of your order. Please verify your AntiSpam settings of your e-mail provider.
 </p></Column> <Column md={6}> <h4>Payment term</h4> <div className="boleto"> <p><i className="fa fa-paypal leading-icon" aria-hidden="true"></i> Paypal</p> <p className="offset30"><Dollars val={this.props.model.Total}/></p></div></Column></Row> <Row className="gray row-eq-height border-top border-bottom"> <Column md={3}> <h4><span className="fa fa-user leading-icon"></span>Your info</h4> <p className="offset30">{this.props.model.CustomerInfo.CustomerName}</p> <p className="offset30">{this.props.model.CustomerInfo.PhoneNumber}</p></Column> <Column md={3} className="border-right"> <br/>
 <br/>
 <p>{this.props.model.CustomerInfo.Email}</p></Column> <Column md={6}> <h4><span className="fa fa-home leading-icon"></span>Shipping address</h4> <p className="offset30">{this.props.model.CustomerInfo.DeliveryAddress}</p></Column></Row> <Row className="gray"> <Column md={6}> <h4><span className="fa fa-gift leading-icon"></span>Delivery</h4></Column> <Column md={6}> <br/>
 <p className="float-right"> Delivery time is {this.props.model.DeliveryUpTo} days
 </p></Column></Row> <Row className="gray"> <Column md={6}> <p className="offset30"><b>Product description</b></p></Column> <Column md={6} className="pull-right"> <p><b className="float-right">Quantity</b></p></Column></Row> { this.props.model.CartItems.map(item => <Row className="gray"> <Column md={6}> <div className="offset30 truncate"> <span>•</span> <span>{item.Description}</span></div></Column> <Column md={6} className="pull-right"> <p className="float-right">{item.Quantity}</p></Column></Row> )
 }
 </Panel> <Row>
 <Column md={9}></Column> <a href="/"> <Column md={2}> <Button bsStyle="success">Back to product catalog</Button></Column></a></Row></div> );
 }
}

清单 11 CheckoutSuccess.jsx 文件的. The 内容。

最终注意事项

如果你花时间和耐心到达这条线,感谢你的阅读。 如果你对该代码或者文章有任何抱怨或者建议,请让我知道。 不要忘记在下面的评论部分留下你的观点。

历史记录

2016/08/31: 第一个版本

1: 文件已经说明

2016/09/04: 服务器端渲染解释


COM  COMM  react  tiny  SHOP  Commerce  
相关文章