• Nancy简单实战之NancyMusicStore(二):打造首页


    前言

    继上一篇搭建好项目之后,我们在这一篇中将把我们NancyMusicStore的首页打造出来。

    布局

    开始首页之前,我们要先为我们的整个应用添加一个通用的布局页面,WebForm中母版页的概念。

    添加一个Views/Shared文件夹,同MVC应用程序一样,我们将布局 _Layout.cshtml 放到这个文件夹下面,具体内容如下:

    @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<dynamic>
    <!DOCTYPE html>
    <html>
    <head>
        <title>@ViewBag.Title</title>  
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />    
        <link href="~/Content/Site.css" rel="stylesheet" type="text/css" />    
    </head>
    <body>
        <div id="header">
            <h1><a href="/">NancyFx MUSIC STORE</a></h1>
            <ul id="navlist">
                <li class="first"><a href="/" id="current">Home</a></li>
                <li><a href="/store">Store</a></li>            
                <li><a id="cart-status" href="/shoppingcart/index"></a></li>
                <li><a href="/storemanager">Admin</a></li>
            </ul>
        </div>
    
        <ul id="categories">       
        </ul>
    
        <div id="main">
            @RenderBody()
        </div>
    
        <div id="footer">
            built with <a href="http://nancyfx.org">Nancy 1.4.3</a>
        </div>
    
        <script src="~/Scripts/jquery-1.5.1.min.js" type="text/javascript"></script>
        @RenderSection("scripts", required: false)
    </body>
    </html>
    

    下面解释一下这个布局页,

    首先是页面最上面的这句代码: @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<dynamic>

    这句代码看上去很像我们在一般MVC视图中写的 @model namespace.modelname ,但是我们却没有在布局页面写过这个吧!

    其实我们可以从字面意义来理解,inherits是继承的意思,后面的是Nancy自己实现的Razor引擎的某个类

    所以表明这个页面是用Razor来解析的,并且后面用到的RenderBodyRenderSection都是要加上这句代码之后才能用的。

    其次,RenderBodyRenderSection这两个东东,大家应该是很熟悉的了。

    • RenderBody说明的是这里预留了一个点位符,用到这个布局的视图就是在这里呈现的

    • RenderSection指明了在页面中存在一个块(一般用于写JS),名字是scripts,required:false说明这一个块不是必须的,当在视图中用到了这个块的时候,在页面写的内容就在这里呈现

    最后还要说明的是:导航菜单里面购物车的数量和商品分类列表在MVCMusicStore中是用Html.RenderAction来实现的,但是在Nancy中,

    并没有这个HtmlHelper,我们在这里也不去扩展这个Helper,所以这两个地方后面会用ajax来简单处理一下。

    到这里,我们的布局是已经写好了。

    不知道大家是否还记得 _ViewStart.cshtml 这个文件?这个文件指定了我们Razor引擎的默认视图母版页面。在MVC中它的内容一般是

    @{
        Layout = "~/Views/Shared/_Layout.cshtml";
    }
    

    在Nancy中,还是有一点点区别的,在Views文件夹下面,我们新建了一个_ViewStart.cshtml文件,其内容如下:

    @{
        Layout = "Views/Shared/_Layout.cshtml";
    }
    

    各位应该也看到两者之间的区别了,Nancy中没有带 ~/ 字符!这个是两者之间路径处理的问题,这个是很值得注意的地方!

    到_ViewStart.cshtml这一步,才真正的算是完成了我们的布局。

    Module

    接下来就是Module层的实现了,这一层可以看作是,MVC中的控制器。

    添加一个Modules文件夹,用来存放我们的modules。

    先在Modules文件夹添加一个HomeModule.cs,用于展示我们的首页。

    其内容如下:

    using Nancy;
    using NancyMusicStore.Common;
    using NancyMusicStore.Models;
    using System.Collections.Generic;
    using System.Data;
    using System.Linq;
    
    namespace NancyMusicStore.Modules
    {
        public class HomeModule : NancyModule
        {
            public HomeModule() : base("/")
            {
                Get["/"] = _ =>
                {
                    var albums = GetTopSellingAlbums(5);
                    return View["Index", albums];
                };
            }
    
            /// <summary>
            /// get top count selling albums 
            /// </summary>
            /// <param name="count"></param>
            /// <returns></returns>
            private List<Album> GetTopSellingAlbums(int count)
            {
                string sql = "public.get_top_selling_albums";
                var list = DBHelper.Query<Album>(sql, new
                {
                    num = count
                }, null, true, null, CommandType.StoredProcedure).ToList();
                return list;
            }
        }
    }
    

    这里要明确几点:

    • 我们写的module都要继承nancy的nancymodule

    • 所有的路由入口都是在构造函数中指定的

    可以看到我们的HomeModule只能接收一个GET请求,这个GET请求就是我们的首页地址,它返回了一个视图

    这个视图还带了一个强类型的最高销售专辑列表,这点与MVC可以说是无缝对接的。

    这个最高销售列表是用PG的存储过程来取的数据,内容如下:

    -- FUNCTION: public.get_top_selling_albums(integer)
    
    -- DROP FUNCTION public.get_top_selling_albums(integer);
    
    CREATE OR REPLACE FUNCTION public.get_top_selling_albums(
    	num integer)
        RETURNS SETOF "TABLE(albumid integer, genreid integer, artistid integer, title character varying, price numeric, albumarturl character varying)"
        LANGUAGE 'plpgsql'
        COST 100.0
        VOLATILE NOT LEAKPROOF 
        ROWS 1000.0
    AS $function$
    
    begin        
        RETURN QUERY SELECT 
        A.albumid,
        A.genreid,
        A.artistid,
        A.title,
        A.price,
        A.albumarturl 
        FROM albums A
        LEFT JOIN orderdetails O ON A.albumid = O.albumid
        GROUP BY A.albumid,A.genreid,A.artistid,A.title,A.price,A.albumarturl
        ORDER BY count(O.albumid) desc LIMIT num;          
    end;
    
    $function$;
    
    ALTER FUNCTION public.get_top_selling_albums(integer)
        OWNER TO dev;
    

    需要说明的是,pg里面过程是用funtionn来体现的。

    后台处理逻辑有了,下面就是视图这一块了,说到视图,Nancy的形式也和ASP.NET MVC的类似。

    我们在Views文件夹建一个Home文件夹,再在Home文件夹下面新建一个Index.cshtml。(不是一定要按这样来做!!)

    下面来看看Index.cshtml的具体内容:

    @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<List<NancyMusicStore.Models.Album>>
    @{
        ViewBag.Title = "NancyFx Music Store";    
    }
    <div id="promotion">
    </div>
    
    <h3><em>Fresh</em> off the grill</h3>
    
    <ul id="album-list">
        @foreach (var album in Model)
        {
            <li>
                <a href="javascript:;">
                    <img alt="@album.Title" src="@album.AlbumArtUrl" />
                    <span>@album.Title</span>
                </a>
            </li>
        }
    </ul>
    

    是一些比较常规的页面操作,只要是符合Razor的语法即可。

    此时效果如下:

    index1

    前面还提到分类列表和购物车数量的问题。我们在这里先处理分类列表的问题。

    回到_Layout.cshtml中,在页面底部添加一段js,让它在加载的时候就通过异步请求把分类加载出来,具体如下:

    <script type="text/javascript">
        $(function () {
            $.ajax({
                url: "/store/genremenu",
                method: "get",
                dataType: "json",
                success: function (res) {
                    for (var i = 0; i < res.length; i++) {
                        $("#categories").append('<li><a href="javascript:;">' + res[i].name + '</a></li>');
                    }
                }
            });
        });
    </script>
    

    这个url是和MVC MusicStore保持一致的!

    所以我们在Modules文件夹下面再建了一个StoreModule用于接收这个ajax请求,具体如下:

    using Nancy;
    using NancyMusicStore.Common;
    using NancyMusicStore.Models;
    using NancyMusicStore.ViewModels;
    using System.Collections.Generic;
    using System.Data;
    using System.Linq;
    
    namespace NancyMusicStore.Modules
    {
        public class StoreModule : NancyModule
        {
            public StoreModule() : base("/store")
            {
                Get["/genremenu"] = _ =>
                {
                    return Response.AsJson(GetGenreList());
                };
            }
    
            private IList<Genre> GetGenreList()
            {
                string cmd = "public.get_all_genres";
                return DBHelper.Query<Genre>(cmd, null, null, true, null, CommandType.StoredProcedure);
            }
        }
    }
    

    在StoreModule的构造函数有一个base("/store"),这一个就是表明这个Module的路由起点是 http://yourdomain.com/store

    我们发起的GET请求的URL是/store/genremenu,所以要在构造函数中添加一个GET请求来返回结果。

    至此,我们的首页已经可以拿的出手给人看了:

    index2

    下一篇将是完善商品浏览相关的内容,以及商品的管理。

    本文也已经同步到 Nancy之大杂烩

  • 相关阅读:
    C++ 使用老牌库xzip & unzip对文件进行压缩解压
    第一次玩蛇,有点紧张。
    fiddler 抓取手机http/https包
    disk或者Partition镜像的制作
    VS2013+phread.h环境配置
    C++ 浅谈 strlen 与 sizeof的区别
    Qt 显示网页的控件
    Qt error: C2236: 意外的标记“class”。是否忘记了“;”?
    初识MySQL——人生若如初相逢
    【学习笔记】HTML基础:列表、表格与媒体元素
  • 原文地址:https://www.cnblogs.com/catcher1994/p/6308600.html
Copyright © 2020-2023  润新知