Blog專案 - 首頁列表 (Controller, Model, View, css)
2020-09-09 00:51 Last Edited: 2020-09-15 21:59 943

首頁的思維是這樣的

  1. 是一個列表,預覽文章
  2. 左邊是Blog、右邊是sidebar
  3. 下方有頁碼

那麼立即開始
首先新建Controller/BlogController.cs、Models/Blog、Views/Blog/index.cshtml
給未寫過MVC的朋友簡單說明一下,Controller負責最外層接request,Model負責內部邏輯、view負責作為回傳給request的容器,可以是簡單一個int、一個JSON、或者一段html
所以完整的flow就是這樣

public class BlogController : Controller
{
    public IActionResult Index()
    {
        List<Models.Blog> blogs = BlogFactory.GetBlogsByPage(1);
        ViewData["Title"] = "";
        return View(blogs);
    }
}

這裡是Controller,Index()經BlogFactory這個class拿到List<Blog>,bind進/Views/Blog/index.cshtml,再傳回

為什麼是/Views/Blog呢其實M$很貼心地會根據Controller的名稱去找Views下面相同名稱的Folder,然後找哪個View就根據Method的名稱。你也可以不跟這個規則,比如return Views("folder2/abc.cshtml", blogs),它就會找Views/folder2/abc.cshtml

public class Blog
    {
        [Required]
        public int Id { get; set; }
        [Required]
        [StringLength(200)]
        public string Title { get; set; }
        public string Markdown_Content { get; set; }
        [Required]
        [StringLength(50)]
        public string Category { get; set; }
        [StringLength(50)]
        public string Tags { get; set; }
        public int Author_Id { get; set; }
        [StringLength(200)]
        public string Thumbnail { get; set; }
        [Required]
        public DateTime Create_Ts { get; set; }
        public DateTime Modify_Ts { get; set; }
        public byte[] Password { get; set; }
        [Required]
        public bool Deleted { get; set; }
        [Required]
        public bool NoComment { get; set; }
        [Required]
        public bool NoRobots { get; set; }
        public int view_count { get; set; }

    }

這個是Blog class,和DB的table有一樣的fields,使用dapper這個ORM framework可以將從db query到的數據直接轉換成Blog物件,有興趣可以google一下,我認為比Entity Framework方便得多

@model IEnumerable<Blog>

<link rel="stylesheet" href="~/lib/highlight/styles/vs2015.css">
<script src="~/lib/highlight/highlight.pack.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
<div id="blogBody" class="col-md-9">
    @if (ViewData["CurrentRoute"] != null)
    {
    <div class="blogPost">
        <a class="blogTitle">Searching: @ViewData["CurrentRoute"]</a>
    </div>
    }
    @foreach (Blog blog in Model)
    {
    <div class="blogPost">
        <a class="blogTitle" href="/blog/@blog.Id">@blog.Title</a>
        <div class="header">
            <small class="gray headerItems">@blog.Create_Ts.ToString("yyyy-MM-dd hh:mm")</small>
            @if(blog.Modify_Ts != null){
        <small class="gray headerItems">Last Edited: @(((DateTime)blog.Modify_Ts).ToString("yyyy-MM-dd hh:mm"))</small>
            }
            @{Random rnd = new Random();
                    int ranComment = rnd.Next(1, 13);
                    int ranReadCount = rnd.Next(1, 10000);
            }
        <small class="gray view_count headerItems">@string.Format("{0:n0}", blog.view_count)</small>
            <small class="orange comment_count headerItems">(@ranComment)</small>
        </div>
        <div class="blogContent">
            @(blog.Markdown_Content == null ? "" : new string(System.Text.RegularExpressions.Regex.Replace(blog.Markdown_Content, "<.*?>", String.Empty).Take(200).ToArray()))
            <a href="/blog/@blog.Id" class="l">@(blog.Markdown_Content != null && blog.Markdown_Content.Count() > 200 ? "...(more)" : "")</a>
        </div>
        <div class="blogFooter">
            <div class="category">
                Category:
                <a href="/category/@blog.Category" class="l">@blog.Category</a>
            </div>
            @if (!string.IsNullOrEmpty(blog.Tags))
                {
            <div class="tags">
                Tags:
                @foreach (var item in blog.Tags.Split(','))
                        {
                            if (item != "")
                            {<a href="/tag/@item.Trim()" class="l" style="margin-right:4px">@item.Trim()</a>}
                    }
            </div>
                }
        </div>
    </div>
    }
    <partial name="~/Views/Shared/_pagination.cshtml" />
</div>
<partial name="~/Views/Shared/_sidebar.cshtml" />

以上是cshtml,第一行@model IEnumerable<Blog>表示這個頁可以綁定的Model是IEnumerable,
中間可以看見我會先iterate list<model>一次
當經index進來時會透過blogFactory傳頭10行blog放進list<blog>傳進這個View
然後就foreach這個List,每個blog創生一個blogPost,裡面有header、content、footer等
Foreach完結後再加上pagination頁碼的partial view和sidebar

#blogBody {
    float: left;
    /*width: 75%;
    margin-left: 5px;
    margin-right: 5px;*/
    overflow: hidden;
}

#sidebar {
    margin-top: 40px;
    /*width: 20%;*/
    float: left;
    font-family: 'Lucida Sans', 'Lucida Grande', 'Lucida Sans Unicode', sans-serif;
}

至於layout則用css和bootstrap處理,bootstrap是一種responsive web design的framework,新增.net core mvc時已經自動包含
#blogBody的class設成col-md-9,這個"col-md-9"就是bootstrap的class,它會將目前的空間等分成12個column,col-md-9即是佔9個column的闊度,中間的md是指middle,即電腦瀏覽時使用。如果要指定手機瀏覽時的layout,可以再加一個class,比如col-xs-12,則會佔全個畫面闊度。透過不同的class指定不同device上瀏覽時的布局,就是bootstrap其中一個功能。
設定成float是為了讓兩個element都共存同一行,不會撞開。(大概是,不太懂css)
闊度設好後,我還把兩個blogBody和sidebar都設定成float align to left,當blogBody已經大到不足以給sidebar空間時,sidebar就可以自動排到下方。如果我都它設成float to right,則blogBody和sidebar會自動縮小到可以一個align to left一個align to right的大小,會變得太小不好看。

 

Category: Coding
Tags: Side Project