这里,我们需要做一些事情,这些事情意味着深度改造前台:

1:为商品增加 添加到购物车 按钮,点击后功能实现;

2:商品排序;

3:购物车预览,以及添加 结算 按钮;

4:一个显式 购物车中有*个 商品 的widget;

一:添加到购物车 按钮

修改 Views/Parts/Product.cshtml:

@{
    var price = (decimal)Model.Price;
    var sku = (string)Model.Sku;
}
<article>
    Price: @price<br />
    Sku: @sku
    <footer>
        <button>Add to shoppingcart</button>
    </footer>
</article>

现在,商品列表上已经有了这个按钮:

包括商品详细页面,也有这个按钮。

1.1 问题

问题来了,我们发现,该按钮在商品介绍上面,如果我们想放到下面,该怎么做呢?

1.2 修改 Display 方法

protected override DriverResult Display(ProductPart part, string displayType, dynamic shapeHelper)
{
    //return ContentShape("Parts_Product", () => shapeHelper.Parts_Product(
    //        Price: part.UnitPrice,
    //        Sku: part.Sku
    //    ));
    return Combined(

// Shape 1: Parts_Product
        ContentShape("Parts_Product", () => shapeHelper.Parts_Product(
            Price: part.UnitPrice,
            Sku: part.Sku
        )),

// Shape 2: Parts_Product_AddButton
        ContentShape("Parts_Product_AddButton", () => shapeHelper.Parts_Product_AddButton())
        );
}

在修改后的方法内,我们创造了一个新的 Shape,叫做 Parts_Product_AddButton,同时,我们使用 Combined 方法,它返回的是依旧是 DriverResult,只不过,它组合了两个形状。

这个时候,我们知道,需要创建一个 Views/Parts/Product.Addbutton.cshtml 文件:

<button>@T("Add to shoppingcart")</button>

当然,我们得把原先的 Product.cshtml 中的代码给恢复过来。

然后,修改 placement.info,为:

<Placement>
  <Place Parts_Product_Edit="Content:1" />
  <Place Parts_Product="Content:0" />
  <Place Parts_Product_AddButton="Content:after" />
</Placement>

然后,看到不一样了:

二:实现 添加到购物车 功能

2.1 前台准备

首先,我们创建文件夹 Controllers,然后控制器:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Mvc;

namespace TMinji.Shop.Controllers
{
    public class ShoppingCartController : Controller
    {

[HttpPost]
        public ActionResult Add(int id)
        {
            return RedirectToAction("Index");
        }
    }

}

然后,修改 Views/Parts/Product.AddButton.cshtml:

@using (Html.BeginForm("Add", "ShoppingCart", new { id = -1 }))
{
    <button type="submit">@T("Add to shoppingcart")</button>
}

让它变成一个表单,并且指向到 Add 方法。

注意到,我们这里传递的 id = -1,所以,我们还需要做一件事情,那就是把真是的商品 id 传递过来,才能添加商品。

于是乎,我们首先应该把 id 传递到 shape 中,修改,我们的 Display 方法,如下:

protected override DriverResult Display(ProductPart part, string displayType, dynamic shapeHelper)
{
    //return ContentShape("Parts_Product", () => shapeHelper.Parts_Product(
    //        Price: part.UnitPrice,
    //        Sku: part.Sku
    //    ));
    return Combined(

// Shape 1: Parts_Product
        ContentShape("Parts_Product", () => shapeHelper.Parts_Product(
            Price: part.UnitPrice,
            Sku: part.Sku
        )),

// Shape 2: Parts_Product_AddButton
        ContentShape("Parts_Product_AddButton", () => shapeHelper.Parts_Product_AddButton(
            ProductId: part.Id))
        );
}

然后,前台 Views/Parts/Product.AddButton.cshtml 如下:

@{
    var productId = (int)Model.ProductId;
}
@using (Html.BeginForm("Add", "ShoppingCart", new { id = productId }))
{
    <button type="submit">@T("Add to shoppingcart")</button>
}

现在,就可以把值传递到控制器了,现在,我们需要实现业务逻辑部分。

2.2 业务逻辑之 Orchard Service

在 Models 目录下,增加 ShoppingCartItem 实体类:

[Serializable]
public sealed class ShoppingCartItem
{
    public int ProductId { get; private set; }

private int _quantity;
    public int Quantity
    {
        get { return _quantity; }
        set
        {
            if (value < 0)
                throw new IndexOutOfRangeException();

_quantity = value;
        }
    }

public ShoppingCartItem()
    {
    }

public ShoppingCartItem(int productId, int quantity = 1)
    {
        ProductId = productId;
        Quantity = quantity;
    }
}

创建 Services 文件夹,然后创建 IShoppingCart 接口,

public interface IShoppingCart : IDependency
{
IEnumerable<ShoppingCartItem> Items { get; }
void Add(int productId, int quantity = 1);
void Remove(int productId);
ProductPart GetProduct(int productId);
decimal Subtotal();
decimal Vat();
decimal Total();
int ItemCount();
void UpdateItems();
}

然后,其实现类:

using Orchard;
using Orchard.ContentManagement;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using TMinji.Shop.Models;

namespace TMinji.Shop.Services
{
    public class ShoppingCart : IShoppingCart
    {
        private readonly IWorkContextAccessor _workContextAccessor;
        private readonly IContentManager _contentManager;
        public IEnumerable<ShoppingCartItem> Items { get { return ItemsInternal.AsReadOnly(); } }

private HttpContextBase HttpContext
        {
            get { return _workContextAccessor.GetContext().HttpContext; }
        }

private List<ShoppingCartItem> ItemsInternal
        {
            get
            {
                var items = (List<ShoppingCartItem>)HttpContext.Session["ShoppingCart"];

if (items == null)
                {
                    items = new List<ShoppingCartItem>();
                    HttpContext.Session["ShoppingCart"] = items;
                }

return items;
            }
        }

public ShoppingCart(IWorkContextAccessor workContextAccessor, IContentManager contentManager)
        {
            _workContextAccessor = workContextAccessor;
            _contentManager = contentManager;
        }

public void Add(int productId, int quantity = 1)
        {
            var item = Items.SingleOrDefault(x => x.ProductId == productId);

if (item == null)
            {
                item = new ShoppingCartItem(productId, quantity);
                ItemsInternal.Add(item);
            }
            else
            {
                item.Quantity += quantity;
            }
        }

public void Remove(int productId)
        {
            var item = Items.SingleOrDefault(x => x.ProductId == productId);

if (item == null)
                return;

ItemsInternal.Remove(item);
        }

public ProductPart GetProduct(int productId)
        {
            return _contentManager.Get<ProductPart>(productId);
        }

public void UpdateItems()
        {
            ItemsInternal.RemoveAll(x => x.Quantity == 0);
        }

public decimal Subtotal()
        {
            return Items.Select(x => GetProduct(x.ProductId).UnitPrice * x.Quantity).Sum();
        }

public decimal Vat()
        {
            return Subtotal() * .19m;
        }

public decimal Total()
        {
            return Subtotal() + Vat();
        }

public int ItemCount()
        {
            return Items.Sum(x => x.Quantity);
        }

private void Clear()
        {
            ItemsInternal.Clear();
            UpdateItems();
        }
    }
}

以上代码不再一一解释,相信大家能看明白,然后,相应的,修改控制器吧:

using Orchard;
using System;
using Orchard.Mvc;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Mvc;
using TMinji.Shop.Services;

namespace TMinji.Shop.Controllers
{
    public class ShoppingCartController : Controller
    {

private readonly IShoppingCart _shoppingCart;
        private readonly IOrchardServices _services;

public ShoppingCartController(IShoppingCart shoppingCart, IOrchardServices services)
        {
            _shoppingCart = shoppingCart;
            _services = services;
        }

[HttpPost]
        public ActionResult Add(int id)
        {

// Add the specified content id to the shopping cart with a quantity of 1.
            _shoppingCart.Add(id, 1);

// Redirect the user to the Index action (yet to be created)
            return RedirectToAction("Index");
        }

public ActionResult Index()
        {

// Create a new shape using the "New" property of IOrchardServices.
            var shape = _services.New.ShoppingCart();

// Return a ShapeResult
            return new ShapeResult(this, shape);
        }

}
}

在控制器中,我们看到了三点变化:

1:构造器接受了两个对象,它们是被注入的,这会由 Orchard 完成;

2:Add 方法可以添加商品到购物车了;

3:增加了一个 Index 方法。可以看到在这个方法中,我们又创建了一个 Shape,而这个 Shape 的名字叫做 ShoppingCart。注意哦,它和后台创建 Shape 不一样(记得吗,在 Display 方法 中),这个 shape 对于那个的 cshtml 文件为 Views/ShoppingCart.cshtml:

TODO: display our shopping cart contents!

如果我们这个时候运行代码,会发现点击 添加到购物车 后,变成 404 。这是因为,我们没有为路由加上 area,它是什么,是模块名,Orchard 会根据这个 area,路由到对于的模块中的 controller。故,我们应该修改 Views/Parts/Product.AddButton.cshtml

@{
    var productId = (int)Model.ProductId;
}
@using (Html.BeginForm("Add", "ShoppingCart", new { id = productId, area = "TMinji.Shop" }))
{
    <button type="submit">@T("Add to shoppingcart")</button>
}

现在,继续运行代码,现在,我们得到错误:

The required anti-forgery form field "__RequestVerificationToken" is not present.

当然,这个错误,熟悉 MVC 的我们,已经知道怎么修改了,修改之:

@{
    var productId = (int)Model.ProductId;
}
@using (Html.BeginFormAntiForgeryPost(Url.Action("Add", "ShoppingCart", new { id = productId, area = "TMinji.Shop" })))
{
    <button type="submit">@T("Add to shoppingcart")</button>
}

再次运行:

我们看到,这个页面没有包含当前的 Theme 的母版页,回到控制器,加一个 ThemedAttribute(当然,我们得 using Orchard.Themes;),如下:

[Themed]
public ActionResult Index()
{
    // Create a new shape using the "New" property of IOrchardServices.
    var shape = _services.New.ShoppingCart();

// Return a ShapeResult
    return new ShapeResult(this, shape);
}

这个时候,再次运行,得到了如下的理想效果:

总结

1:前台的展现,主要通过 MVC 的控制器来实现的,而后台则绕开了控制器;

2:业务逻辑,使用 Service 机制,而不是我们自己瞎写的类和逻辑(当然,也可以),但 Service 机制看上去更 Orchard;

3:前后台创建 Shape 的方式是不一样的。

Orchard模块开发全接触4:深度改造前台的更多相关文章

  1. Orchard模块开发全接触8:改造后台

    后台默认提供了 Content 的管理,但是,所有的内容类型揉杂在一起,而我们需要更深度的定制一些功能,比如,我们只想管理订单,又比如,我们需要对注册用户进行管理.本篇的内容包括: 1:自定义 adm ...

  2. Orchard模块开发全接触1:起步

    在<http://www.cnblogs.com/luminji/p/3831281.html>中简单介绍了 Orchard 的模块开发,接下来,我们需要做个更复杂的例子,Orchard ...

  3. Orchard模块开发全接触7:订单与支付之Event Bus

    在这部分,我们要完成的工作有: 1:将购物车内的商品变成真正的订单: 2:理解 父子及一对多关系: 3:写一个针对 Event Bus 的扩展点: 4:实现一个针对该扩展点的模拟的 支付服务: 一:创 ...

  4. Orchard模块开发全接触5:深度改造前台第二部分

    在这一部分,我们继续完善我们的购物车,我们要做以下一些事情: 1:完成 shoppingcart.cshtml: 2:让用户可以更新数量及从购物车删除商品: 3:创建一个 widget,在上面可以看到 ...

  5. Orchard模块开发全接触3:分类的实现及内容呈现(Display)

    一:分类用现有技术怎么实现? 实际就是创建 Query 和 Projection,如果不知道怎么做,参考:Orchard之在前台显式一个属于自己的列表(在这篇里,还进行了稍稍拓展),当然,基础的知道, ...

  6. Orchard模块开发全接触2:新建 ProductPart

    一:创建 Part 1:项目引用 Orchard.Framework: 2:创建 Models 文件夹: 3:在 Models 文件夹下创建类 ProductPartRecord,如下: public ...

  7. Orchard模块开发全接触6:自定义用户注册

    我们都知道 Orchard 的用户注册相当简单,现在,我们需要一个自定义的用户注册,现在,开始吧. 一:定义实体 Models/CustomerPartRecord.cs: public class ...

  8. Orchard 模块开发学习笔记 (1)

    创建模块 首先,打开Bin目录下的Orchard.exe 等到出现orchard>后, 看看命令列表中是否存在 codegen module 如果不存在,则需要先执行:feature enabl ...

  9. Odoo9.0模块开发全流程

    构建Odoo模块 模块组成 业务对象 业务对象声明为Python类, 由Odoo自己主动加载. 数据文件 XML或CSV文件格式, 在当中声明了元数据(视图或工作流).配置数据(模块參数).演示数据等 ...

随机推荐

  1. 数学之美——HMM模型(一)介绍

    一直想写点关于数学方面的blog,这对于数据挖掘分析,NLP处理等都有着比较重要的作用,之前在CSDN上想写点HMM方面的文章,一直没写成,最近几天终于抽点时间完成了HMM的文章,加以整理,遂有这个系 ...

  2. WebApi 接口参数详解

    WebApi 接口参数不再困惑:传参详解   阅读目录 一.get请求 1.基础类型参数 2.实体作为参数 3.数组作为参数 4.“怪异”的get请求 二.post请求 1.基础类型参数 2.实体作为 ...

  3. fiddler抓https 关于证书一项

    由于我之前电脑装过fiddler然后这次弄证书也没有理解明白,导致失败了.然后就百度到了下面的教程. https://www.cnblogs.com/joshua317/p/8670923.html ...

  4. spark sql中将数据保存成parquet,json格式

    val df = sqlContext.load("/opt/modules/spark1.3.1/examples/src/main/resources/people.json" ...

  5. “漂亮的”排序算法 Stooge Sort 如何完成排序

    Stooge Sort 是一种低效的递归排序算法,甚至慢于冒泡排序.在<算法导论>第二版第7章(快速排序)的思考题中被提到,是由Howard.Fine等教授提出的所谓“漂亮的”排序算法. ...

  6. Javassist学习总结

    今天在弄dubbo时出现了一个依赖缺少问题,就好奇研究一下,这个依赖是啥. javassist是一个字节码类库,可以用他来动态生成类,动态修改类等等 1.介绍Javassist 要想将编译时不存在的类 ...

  7. 一个ScheduledExecutorService启动的Java线程无故挂掉引发的思考

    2018年12月12日18:44:53 一个ScheduledExecutorService启动的Java线程无故挂掉引发的思考 案件现场 不久前,在开发改造公司一个端到端监控日志系统的时候,出现了一 ...

  8. webpack1.x环境配置与打包基础【附带各种 "坑" 与解决方案!持续更新中...】

    首先介绍传统模块化开发的主流方案: 1.基与CMD的sea.js,玉伯提出的解决方案,据说原来京东团队在使用.用时才定义,就近加载. 2.基于AMD的require.js,之前在用.提前声明与定义.国 ...

  9. 一个页面从输入URL 到页面加载显示完成,这个过程中都发生了什么?

    1.当发送一个URL请求时,浏览器会开启一个线程来处理这个请求,同时在远程DNS服务器上启动一个DNS查询,解析获取网址的IP地址:2.浏览器与远程Web服务器通过TCP三次握手协商来建立一个TCP/ ...

  10. 牛客OI赛制测试赛3游记

    A - 数字权重 题目大意: 一个\(n\)位的数字.设第\(i\)位的数为\(a_i\),其中\(a_1\)为最高位,\(a_n\)为最低位,\(k\)为给定的数字.求同时满足满足以下两个条件的数的 ...