FineUI小技巧(1)简单的购物车页面
起因
最初是一位 FineUI 网友对购物车功能的需求,需要根据产品单价和数量来计算所有选中商品的总价。
这个逻辑最好在前台使用JavaScript实现,如果把这个逻辑移动到后台C#实现,则会导致过多的AJAX请求而影响用户体验。
最终效果
准备数据
在生成页面之前,我们需要准备购物车的数据,这里只是简单的用表格来模拟数据:
protected DataTable GetCartDataTable()
{
DataTable table = new DataTable();
table.Columns.Add(new DataColumn("Id", typeof(int)));
table.Columns.Add(new DataColumn("Code", typeof(String)));
table.Columns.Add(new DataColumn("Name", typeof(String)));
table.Columns.Add(new DataColumn("Desc", typeof(String)));
table.Columns.Add(new DataColumn("Price", typeof(float)));
table.Columns.Add(new DataColumn("Number", typeof(int))); DataRow row = table.NewRow();
row[] = ;
row[] = "";
row[] = "商品一";
row[] = "这是商品一的介绍";
row[] = 35.5;
row[] = ;
table.Rows.Add(row); row = table.NewRow();
row[] = ;
row[] = "";
row[] = "商品二";
row[] = "这是商品二的介绍";
row[] = 18.99;
row[] = ;
table.Rows.Add(row); row = table.NewRow();
row[] = ;
row[] = "";
row[] = "商品三";
row[] = "这是商品三的介绍";
row[] = 18.99;
row[] = ;
table.Rows.Add(row); row = table.NewRow();
row[] = ;
row[] = "";
row[] = "商品四";
row[] = "这是商品四的介绍";
row[] = 22.00;
row[] = ;
table.Rows.Add(row); return table;
}
页面标签
前台页面使用了VBox布局,用来实现底部汇总面板的高度固定,顶部表格的高度自适应页面高度的布局:
<f:PageManager ID="PageManager1" AutoSizePanelID="Panel2" runat="server" />
<f:Panel ID="Panel2" runat="server" ShowBorder="false" Layout="VBox" BoxConfigAlign="Stretch"
BoxConfigPosition="Start" BoxConfigPadding="5" BoxConfigChildMargin="0 5 0 0"
ShowHeader="false">
<Items>
<f:Grid ID="Grid1" ShowBorder="true" BoxFlex="1" ShowHeader="true" Title="购物车"
EnableCollapse="true" runat="server" EnableCheckBoxSelect="true" CheckBoxSelectOnly="true"
DataKeyNames="Id,Code,Name" EnableTextSelection="true"> </f:Grid>
<f:ContentPanel runat="server" CssClass="totalpanel" ShowBorder="true" ShowHeader="false"> </f:ContentPanel>
</Items>
</f:Panel>
VBox布局和HBox布局对于 FineUI 来说举足轻重,如果你还搞不清楚其中的参数含义,请移步FineUI教程。
这里有个小技巧,由于上下两个面板紧贴在一起,所以中间的两个边框就显得不好看了,我们只需通过简单的CSS来调整,使得下面面板的顶部边框宽度为零:
<style>
.totalpanel .x-panel-body {
border-top-width: 0 !important;
}
</style>
下面来看表格的定义:
<f:Grid>
<Columns>
<f:RowNumberField />
<f:BoundField Width="120px" DataField="Code" DataFormatString="{0}" HeaderText="商品代码" />
<f:BoundField DataField="Name" ExpandUnusedSpace="true" DataFormatString="{0}" HeaderText="商品名称" />
<f:BoundField Width="120px" DataField="Price" HeaderText="商品单价" DataFormatString="¥{0:F}" />
<f:TemplateField HeaderText="数量" Width="120px">
<ItemTemplate>
<input type="hidden" class="price" runat="server" value='<%# Eval("Price") %>' />
<asp:TextBox runat="server" Width="98%" ID="tbxNumber" CssClass="number"
TabIndex='<%# Container.DataItemIndex + 10 %>' Text='<%# Eval("Number") %>'></asp:TextBox>
</ItemTemplate>
</f:TemplateField>
<f:TemplateField HeaderText="小计" Width="120px">
<ItemTemplate>
<asp:Label runat="server" CssClass="xiaoji" Text='<%# "¥" + GetXiaoji(Eval("Price"), Eval("Number")) %>'></asp:Label>
</ItemTemplate>
</f:TemplateField>
</Columns>
</f:Grid>
一些小技巧:
- DataFormatString="¥{0:F}" 将浮点数格式化为两个小数位的字符串。
- Container.DataItemIndex 表示当前项的序号,设置TabIndex是为了启用Tab键导航
- 隐藏字段 class="price",是为了方便客户端使用JavaScript获取产品单价
- 数量的文本输入框的 CssClass="number" 同样是为了方便客户端调用
- 通过后台定义的C#函数 GetXiaoji 来计算初始产品价格小计
下面来看下 GetXiaoji 的定义:
protected string GetXiaoji(object priceobj, object numberobj)
{
float price = Convert.ToSingle(priceobj);
int number = Convert.ToInt32(numberobj); return String.Format("{0:F}", price * number);
}
接下来看下汇总面板的标签定义:
<f:ContentPanel>
<div style="text-align: right; margin: 10px;">
<div style="margin-bottom: 10px;">
<input type="hidden" id="TOTAL_NUMBER" name="TOTAL_NUMBER" />
<span id="totalNumber" style="color: red;"></span>
件商品
</div>
<div style="margin-bottom: 10px;">
<input type="hidden" id="TOTAL_PRICE" name="TOTAL_PRICE" />
总计:<span id="totalPrice" style="color: red; font-size: 1.5em; font-weight: bold;"></span>
</div>
<div>
<f:Button runat="server" Text="去结算" Enabled="false" Size="Large" ID="btnGotoPay" OnClick="btnGotoPay_Click"></f:Button>
</div>
</div>
</f:ContentPanel>
这里面的几个小技巧:
- 隐藏字段 TOTAL_NUMBER 和 TOTAL_PRICE 是为了方便在后台获取总价和商品总数
- 默认设置提交按钮的 Enabled="false",在用户更改选中商品数量时来决定是否禁用
前台JavaScript逻辑
FineUI 虽然号称 No JavaScript,但这里的真正意思是 80% 的应用场景不需要使用 JavaScript 就能轻松实现。
对于购物车这种需要前台交互的页面,还是需要开发者有一定的脚本编写功底。下面先罗列一下全部的JavaScript代码:
var gridClientID = '<%= Grid1.ClientID %>';
var btnGotoPayClientID = '<%= btnGotoPay.ClientID %>';
var numberSelector = '.f-grid-tpl input.number';
var priceSelector = '.f-grid-tpl input.price'; function getRowNumber(row) {
return parseInt(row.find(numberSelector).val(), 10);
}
function getRowPrice(row) {
return parseFloat(row.find(priceSelector).val());
} function updateTotal() {
var grid = F(gridClientID);
var selection = grid.getSelectionModel().getSelection();
var store = grid.getStore(); var total = 0;
$.each(selection, function (index, item) {
var rowIndex = store.indexOf(item);
var row = $(grid.body.el.dom).find('.x-grid-row').eq(rowIndex);
total += getRowNumber(row) * getRowPrice(row);
}); $('#totalNumber').text(selection.length);
$('#totalPrice').text("¥" + total.toFixed(2)); $('#TOTAL_NUMBER').val(selection.length);
$('#TOTAL_PRICE').val(total.toFixed(2)); var gotoPayBtn = F(btnGotoPayClientID);
if (total === 0) {
gotoPayBtn.disable();
} else {
gotoPayBtn.enable();
}
} function registerNumberChangeEvents() {
var grid = F(gridClientID); // 数量改变事件
// http://stackoverflow.com/questions/17384218/jquery-input-event
$(grid.el.dom).find(numberSelector).on('input propertychange', function (evt) {
var $this = $(this); var row = $this.parents('.x-grid-row');
var number = getRowNumber(row);
var price = getRowPrice(row);
var resultNode = row.find('.f-grid-tpl span.xiaoji'); resultNode.text("¥" + (number * price).toFixed(2)); updateTotal();
});
} function registerSelectionChangeEvents() {
var grid = F(gridClientID); grid.on('selectionchange', function (cmp, selected) {
updateTotal();
});
} // 页面第一次加载完成后调用的函数
F.ready(function () {
registerNumberChangeEvents();
registerSelectionChangeEvents();
updateTotal();
});
这里只给出一些小技巧的提醒:
- F.ready 用来初始化所有需要的JavaScript代码,包含对 updateTotal 的调用
- registerNumberChangeEvents 注册数量文本框改变的处理函数
- 文本框的 input 事件用来监视文本框的内容变化,包含键盘输入、拷贝粘贴等,IE8不支持此事件但可以使用 propertychange 代替
- registerSelectionChangeEvents 注册用户选中商品行改变的事件处理函数
- updateTotal 中根据总价来决定是否启用提交按钮
后台C#逻辑
后台来显示汇总信息,对熟悉 FineUI 的网友应该来说很简单:
protected void btnGotoPay_Click(object sender, EventArgs e)
{
StringBuilder sb = new StringBuilder();
sb.Append("<ol>");
foreach(int rowIndex in Grid1.SelectedRowIndexArray) {
System.Web.UI.WebControls.TextBox tbxNumber = (System.Web.UI.WebControls.TextBox)Grid1.Rows[rowIndex].FindControl("tbxNumber"); sb.AppendFormat("<li>{0}({1})</li>", Grid1.DataKeys[rowIndex][], tbxNumber.Text);
}
sb.Append("</ol><hr/>"); sb.AppendFormat("共 {0} 件商品,总计 ¥{1}", Request.Form["TOTAL_NUMBER"], Request.Form["TOTAL_PRICE"]); Alert.Show(sb.ToString(), MessageBoxIcon.Information);
}
源代码免费下载
这个简直就是废话!
这个示例会出现在下个版本的 FineUI(开源版)中,不过目前你可以直接从微软的 codeplex 网站下载全部源代码:
https://fineui.codeplex.com/SourceControl/list/changesets
24 张专业版截图
推荐本文
如果本文对你有一定的启发或帮助,请点击好文要顶。你也可以通过关注本博客来及时获取 FineUI 的最新信息。
《FineUI小技巧》系列文章目录
- FineUI小技巧(1)简单的购物车页面
- FineUI小技巧(2)将表单内全部字段禁用、只读、设置无效标识
- FineUI小技巧(3)表格导出与文件下载
- FineUI小技巧(4)关闭窗体那些事
- FineUI小技巧(5)向子窗口传值,向父窗口传值
FineUI小技巧(1)简单的购物车页面的更多相关文章
- FineUI小技巧(6)自定义页面回发
前言 FineUI中的绝大部分回发事件都是由控件触发了,比如按钮的点击事件,下拉列表的改变事件,表格的排序分页事件.但有时我们可能会要自己触发页面回发,这时就要知道怎么使用 JavaScript 来做 ...
- FineUI小技巧(5)向子窗口传值,向父窗口传值
前言 FineUI中经常会用到启用IFrame的Window控件,这样有助于从物理上进行代码解耦和.IFrame的引入就会涉及传值问题,如何在父窗口和子窗口之间相互传值呢? 向子窗口传值 向子窗口传值 ...
- FineUI小技巧(4)关闭窗体那些事
前言 FineUI中的Window控件常用作选择.新增或编辑内容.而关闭Window控件却有很多技巧,了解这些技巧有助于项目的快速开发. 如何关闭Window控件 第一个问题就是如何关闭Window控 ...
- FineUI小技巧(3)表格导出与文件下载
需求描述 实际应用中,我们可能需要导出表格内容,或者在页面回发时根据用户权限下载文件(注意,这里的导出与下载,都是在后台进行的,和普通的一个链接下载文件不同). 点击按钮导出表格 由于FineUI 默 ...
- FineUI小技巧(2)将表单内全部字段禁用、只读、设置无效标识
需求描述 对表单内的所有字段进行操作也是常见需求,这些操作有: 禁用:表单字段变灰,不响应用户动作. 只读:表单字段不变灰,但不接受用户输入(实际上是设置DOM节点的readonly属性),有触发器的 ...
- FineUI小技巧(7)多表头表格导出
前言 之前我们曾写过一篇文章 FineUI小技巧(3)表格导出与文件下载,对于在 FineUI 中导出表格数据进行了详细描述.今天我们要更进一步,介绍下如何导出多表头表格. 多表头表格的标签定义 在 ...
- 小技巧:在向导式页面设计中使用hidden型输入可以避免session的使用
在向导式页面设计中使用hidden型输入可以避免session的使用,从而减小内存开支. 在表单中使用隐藏输入类型<input type="hidden" name=&quo ...
- Java web开发中页面跳转小技巧——跳转后新页面在新窗口打开
最近学习Java web,在学习过程中想实现一个需求,就是在jsp页面跳转的时候,希望跳转后的新页面在新窗口中打开, 而不是覆盖原来的页面,这个需求使我困惑了好长时间,后来通过大海捞针似的在网上寻找方 ...
- win10系统使用小技巧【转】
win10的很多小技巧又简单又实用,这里给大家整理了10个小技巧,一分钟学会,秒变win10高手,看不完的先收藏再看哦. 1.改美区 在设置中时间和语言中将区域和语言改为美国就可以瞬间切换Foreca ...
随机推荐
- 关于JS变量提升的一些坑
function log(str) { // 本篇文章所有的打印都将调用此方法 console.log(str); } 函数声明和变量声明总是会被解释器悄悄地被“提升”到方法体的最顶部 变量声明.命名 ...
- Redis客户端开发包:Jedis学习-高级应用
事务 Jedis中事务的写法是将redis操作写在事物代码块中,如下所示,multi与exec之间为具体的事务. jedis.watch (key1, key2, ...); Transaction ...
- 十五天精通WCF——第四天 你一定要明白的通信单元Message
转眼你已经学了三天的wcf了,是不是很好奇wcf在传输层上面到底传递的是个什么鸟毛东西呢???应该有人知道是soap,那soap这叼毛长得是什么 样呢?这一篇我们来揭开答案... 一:soap到底长成 ...
- 这是经典的"百马百担"问题,有一百匹马,驮一百担货,大马驮3担,中马驮2担,两只小马驮1担,问有大,中,小马各几匹?
- 使用For XML PATH 会影响Cross Apply 返回
昨天在写语句的时候,遇到了一个现象,其实就是使用 Cross Apply做一个拼接字符串的而已.比如 CREATE TABLE GoodsCatalog (ID INT, Name )) CREATE ...
- js中的三个编码函数:escape,encodeURI,encodeURIComponent
1. eacape(): 该方法不会对 ASCII 字母和数字进行编码,也不会对下面这些 ASCII 标点符号进行编码: * @ - _ + . / .其他所有的字符都会被转义序列替换.其它情况下es ...
- ubuntu16.04下vim安装失败
问题? 重装了ubuntu系统,安装vim出现了以下问题: sudo apt-get install vim 正在读取软件包列表... 完成 正在分析软件包的依赖关系树 正在读取 ...
- 支付宝APP支付开发- IOException : DerInputStream.getLength(): lengthTag=127, too big.
支付宝APP支付Java开发报错: IOException : DerInputStream.getLength(): lengthTag=127, too big. 后来排查是因为没有设置私钥.
- xsd.exe的使用
xsd.exe的使用 XML文件生成XSD文件 xsd myFile.xml /outputdir:myOutputDir XSD文件生存实体类 xsd <你的XSD路径>.xsd / ...
- 动手学习TCP:总结和索引
TCP是一个十分复杂的协议,通过前面几篇文章只涉及了TCP协议中一些基本的概念. 虽然说都是一些TCP最基本的概念,但是试验过程中一直在踩坑,例如:TCP flag设置错误,seq.ack号没有计算正 ...