篇(18)-Asp.Net Core入門實戰-文章管理之文章內容管理(下拉框二級結構遞迴實現)
文章管理是CMS系統的核心表之一,儲存文章內容,特點就是欄位端,屬性多,比如是否標識為熱點、推薦等屬性,是否釋出,類別,SEO關鍵字等。我們本章講解文章內容的增刪改查。
(1).文章Sql表結構設計
CREATE TABLE [dbo].[Article]( [Id] [int] IDENTITY(1,1) NOT NULL, [CategoryId] [int] NOT NULL, [Title] [varchar](128) NOT NULL, [ImageUrl] [varchar](128) NULL, [Content] [text] NULL, [ViewCount] [int] NOT NULL, [Sort] [int] NOT NULL, [Author] [varchar](64) NULL, [Source] [varchar](128) NULL, [SeoTitle] [varchar](128) NULL, [SeoKeyword] [varchar](256) NULL, [SeoDescription] [varchar](512) NULL, [AddManagerId] [int] NOT NULL, [AddTime] [datetime] NOT NULL, [ModifyManagerId] [int] NULL, [ModifyTime] [datetime] NULL, [IsTop] [bit] NOT NULL, [IsSlide] [bit] NOT NULL, [IsRed] [bit] NOT NULL, [IsPublish] [bit] NOT NULL, [IsDeleted] [bit] NOT NULL, CONSTRAINT [PK_ARTICLE] PRIMARY KEY NONCLUSTERED ( [Id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO SET ANSI_PADDING OFF GO ALTER TABLE [dbo].[Article] ADD DEFAULT (getdate()) FOR [AddTime] GO ALTER TABLE [dbo].[Article] ADD DEFAULT ((0)) FOR [IsTop] GO ALTER TABLE [dbo].[Article] ADD DEFAULT ((0)) FOR [IsSlide] GO ALTER TABLE [dbo].[Article] ADD DEFAULT ((0)) FOR [IsRed] GO ALTER TABLE [dbo].[Article] ADD DEFAULT ((0)) FOR [IsPublish] GO ALTER TABLE [dbo].[Article] ADD DEFAULT ((0)) FOR [IsDeleted] GO ALTER TABLE [dbo].[Article] WITH CHECK ADD CONSTRAINT [FK_ARTICLE_RELATIONS_ARTICLEC] FOREIGN KEY([CategoryId]) REFERENCES [dbo].[ArticleCategory] ([Id]) GO ALTER TABLE [dbo].[Article] CHECK CONSTRAINT [FK_ARTICLE_RELATIONS_ARTICLEC] GO
那麼對應的Article Model程式碼如下:
public class Article { /// <summary> /// 主鍵 /// </summary> [Key] public Int32 Id { get; set; } /// <summary> /// 分類ID /// </summary> [Required] public Int32 CategoryId { get; set; } /// <summary> /// 文章標題 /// </summary> [Required] public String Title { get; set; } /// <summary> /// 圖片地址 /// </summary> public String ImageUrl { get; set; } /// <summary> /// 文章內容 /// </summary> public String Content { get; set; } /// <summary> /// 瀏覽次數 /// </summary> [Required] public Int32 ViewCount { get; set; } /// <summary> /// 排序 /// </summary> [Required] public Int32 Sort { get; set; } /// <summary> /// 作者 /// </summary> public String Author { get; set; } /// <summary> /// 來源 /// </summary> public String Source { get; set; } /// <summary> /// SEO標題 /// </summary> public String SeoTitle { get; set; } /// <summary> /// SEO關鍵字 /// </summary> public String SeoKeyword { get; set; } /// <summary> /// SEO描述 /// </summary> public String SeoDescription { get; set; } /// <summary> /// 新增人ID /// </summary> [Required] public Int32 AddManagerId { get; set; } /// <summary> /// 新增時間 /// </summary> [Required] public DateTime AddTime { get; set; } /// <summary> /// 修改人ID /// </summary> public Int32? ModifyManagerId { get; set; } /// <summary> /// 修改時間 /// </summary> public DateTime? ModifyTime { get; set; } /// <summary> /// 是否置頂 /// </summary> public Boolean IsTop { get; set; } /// <summary> /// 是否輪播顯示 /// </summary> public Boolean IsSlide { get; set; } /// <summary> /// 是否熱門 /// </summary> public Boolean IsRed { get; set; } /// <summary> /// 是否釋出 /// </summary> public Boolean IsPublish { get; set; } /// <summary> /// 是否刪除 /// </summary> [Required] public Boolean IsDeleted { get; set; } }
(2).檢視Create程式碼
(2.1)檢視程式碼
考慮到要同時上傳圖片,注意form表單的額 enctype型別;
@{ ViewData["Title"] = "新建文章"; } @model Article <form action="/Article/Create" method="post" enctype="multipart/form-data"> @Html.AntiForgeryToken() <div> <label asp-for="Title">標題</label> <div> <input type="text" asp-for="Title" name="Title" placeholder="請輸入標題"> </div> </div> <div> <label asp-for="CategoryId">文章型別</label> <div> @Html.DropDownList("ddl_CategoryId", ViewBag.database as IEnumerable<SelectListItem>) </div> </div> <div> <label>設定</label> <div> @*@Html.CheckBox("IsTop") 置頂 @Html.CheckBox("IsRed") 熱點 @Html.CheckBox("IsSlide") 幻燈*@ <input type="checkbox" name="IsTop" asp-for="IsTop" />置頂 <input type="checkbox" name="IsRed" asp-for="IsRed"/>熱點 <input type="checkbox" name="IsSlide" asp-for="IsSlide" />幻燈 </div> </div> <div> <label asp-for="ImageUrl">文章首頁圖</label> <div> <input type="file" asp-for="ImageUrl" name="ImageUrl"/> </div> </div> <div> <label asp-for="Content">內容</label> <div> <textarea placeholder="內容" asp-for="Content" name="Content" cols="30" rows="10"></textarea> </div> </div> <div> <label asp-for="Sort">排序</label> <div> <input type="text" placeholder="排序" asp-for="Sort" name="Sort" /> </div> </div> <div> <label asp-for="ViewCount">點選量</label> <div> <input type="text" placeholder="點選量" asp-for="ViewCount" name="ViewCount" /> </div> </div> <div> <label asp-for="IsPublish">是否釋出</label> <div> <select asp-for="IsPublish" name="IsPublish" class="IsPublish"> <option value="False">否</option> <option value="True" selected>是</option> </select> </div> </div> <div> <label asp-for="Author">作者</label> <div> <input type="text" asp-for="Author" name="Author" placeholder="作者名"> </div> </div> <div> <label asp-for="Source">來源</label> <div> <input type="text" asp-for="Source" name="Source" placeholder="文章來源"> </div> </div> <div> <label asp-for="SeoTitle">SEO標題</label> <div> <input type="text" asp-for="SeoTitle" name="SeoTitle" placeholder="SEO標題"> </div> </div> <div> <label asp-for="SeoKeyword">SEO關鍵詞</label> <div> <input type="text" asp-for="SeoKeyword" name="SeoKeyword" placeholder="SEO關鍵詞"> </div> </div> <div> <label asp-for="SeoDescription">SEO摘要描述</label> <div> <input type="text" asp-for="SeoDescription" name="SeoDescription" placeholder="SEO摘要描述"> </div> </div> <div> <div> <button type="submit">確定</button> <button type="reset">重置</button> </div> </div> </form>
(2.2)檢視中的下拉框的實現方式(遞迴和迴圈巢狀)
我想在新增文章時,實現一個具有二級層次結構的下拉框,如上圖所示。所以,在對下拉框進行資料繫結時,就要費點功夫,上個章節講文章類別管理時,的表結構就一個,分類都存在一張表中,所以要進行遞迴的獲取子選單或者通過迴圈巢狀來實現。
遞迴的主要核心函數為:
/// <summary> /// 遞迴函數,實現獲取子選單 /// </summary> /// <param name="lists">遞迴前的列表</param> /// <param name="newlists">遞迴後的新列表</param> /// <param name="parentId">父Id</param> /// <returns></returns> public static List<CategorySelectItemListView> GetChildCategory(List<CategorySelectItemListView> lists, List<CategorySelectItemListView> newlists, int parentId) { newlists = new List<CategorySelectItemListView>(); List<CategorySelectItemListView> tempList = lists.Where(c => c.ParentId == parentId).ToList(); for (int i = 0; i < tempList.Count; i++) { CategorySelectItemListView category = new CategorySelectItemListView(); category.Id = tempList[i].Id; category.ParentId = tempList[i].ParentId; category.Title = tempList[i].Title; category.Children = GetChildCategory(lists, newlists, category.Id); newlists.Add(category); } return newlists; } /// <summary> /// 迴圈巢狀,實現獲取子選單 /// </summary> /// <param name="lists">迴圈遍歷前的列表</param> /// <returns></returns> public static List<CategorySelectItemListView> GetChildCategory(List<CategorySelectItemListView> lists) { List<CategorySelectItemListView> categorylist = new List<CategorySelectItemListView>(); for (int i = 0; i < lists.Count; i++) { if (0 == lists[i].ParentId) categorylist.Add(lists[i]); for (int j = 0; j < lists.Count; j++) { if (lists[j].ParentId == lists[i].Id) lists[i].Children.Add(lists[j]); } } return categorylist; }
然後在Create和Edit的Action中去繫結對應的下拉式選單即可。
注意:List<CategorySelectItemListView> 集合的CategorySelectItemListView,這個是新建的ViewModel物件,用來專門繫結下拉式選單使用,其程式碼如下:
public class CategorySelectItemListView { public int Id { get; set; } public string Title { get; set; } public int ParentId { get; set; } public List<CategorySelectItemListView> Children { get; set; } public CategorySelectItemListView() { Children = new List<CategorySelectItemListView>(); } public CategorySelectItemListView(int id,string title,int parentid) { this.Id = id; this.Title = title; this.ParentId = parentid; Children = new List<CategorySelectItemListView>(); } public CategorySelectItemListView(int id, string title, CategorySelectItemListView parent) { this.Id = id; this.Title = title; this.ParentId = parent.Id; Children = new List<CategorySelectItemListView>(); }
(3).檢視Edit程式碼,註解部分的程式碼可以參考,我嘗試用過,也可以達到目的,演練程式碼最好是用多種方式實現,檢視其區別,這樣掌握的牢固一些。
@{ ViewData["Title"] = "編輯文章"; } @model Article @section Scripts{ <script type="text/javascript" src="~/js/jquery-3.6.1.min.js"></script> <script type="text/javascript"> $(document).ready(function () { $.ajax({ type: "GET", url: "/ArticleCategory/GetCategory", data: "{}", success: function (data) { console.log(data); var s = '<option value="0">請選擇</option>'; for (var i = 0; i < data.length; i++) { s += '<option value="' + data[i].Title + '"+>' + data[i].Id + '</option>'; } $("#ArticleCategory").html(s); } }); }); </script> } <form action="/Article/Edit" method="post" enctype="multipart/form-data"> @Html.AntiForgeryToken() <div> <label asp-for="Title">標題</label> <div> <input type="text" asp-for="Title" name="Title" placeholder="請輸入標題"> <input type="hidden" asp-for="Id" /> </div> </div> <div> <label asp-for="CategoryId">文章型別</label> <div> @Html.DropDownList("ddl_CategoryId", ViewBag.database as IEnumerable<SelectListItem>) </div> </div> <div> <label>設定</label> <div> @*@Html.CheckBox("IsTop", Model.IsTop,new { value = Model.IsTop}) 置頂 @Html.CheckBox("IsRed", Model.IsRed, new { value = Model.IsRed }) 熱點 @Html.CheckBox("IsSlide", Model.IsSlide, new { value = Model.IsSlide }) 幻燈*@ <input asp-for="IsTop" />置頂 <input asp-for="IsRed" />熱點 <input asp-for="IsSlide" />幻燈 @*<input type="checkbox" name="IsTop" @(Html.Raw(@Model.IsTop ? "checked=\"checked\"" : "")) asp-for="IsTop" />置頂 <input type="checkbox" name="IsRed" @(Html.Raw(@Model.IsRed ? "checked=\"checked\"" : "")) asp-for="IsRed" />熱點 <input type="checkbox" name="IsSlide" @(Html.Raw(@Model.IsSlide ? "checked=\"checked\"" : "")) asp-for="IsSlide" />幻燈*@ @*<input type="checkbox" name="IsTop" asp-for="IsTop" />置頂 <input type="checkbox" name="IsRed" asp-for="IsRed"/>熱點 <input type="checkbox" name="IsSlide" asp-for="IsSlide"/>幻燈*@ </div> </div> <div> <label asp-for="ImageUrl">文章首頁圖</label> <div> <input type="file" asp-for="ImageUrl" name="ImageUrl" /> <label asp-for="ImageUrl">@Model.ImageUrl</label> </div> </div> <div> <label asp-for="Content">內容</label> <div> <textarea placeholder="內容" asp-for="Content" name="Content" cols="30" rows="10"></textarea> </div> </div> <div> <label asp-for="Sort">排序</label> <div> <input type="text" placeholder="排序" asp-for="Sort" name="Sort" /> </div> </div> <div> <label asp-for="ViewCount">點選量</label> <div> <input type="text" placeholder="點選量" asp-for="ViewCount" name="ViewCount" /> </div> </div> <div> <label asp-for="IsPublish">是否釋出</label> <div> <select asp-for="IsPublish" name="IsPublish" class="IsPublish"> <option value="False">否</option> <option value="True" selected>是</option> </select> </div> </div> <div> <label asp-for="Author">作者</label> <div> <input type="text" asp-for="Author" name="Author" placeholder="作者名"> </div> </div> <div> <label asp-for="Source">來源</label> <div> <input type="text" asp-for="Source" name="Source" placeholder="文章來源"> </div> </div> <div> <label asp-for="SeoTitle">SEO標題</label> <div> <input type="text" asp-for="SeoTitle" name="SeoTitle" placeholder="SEO標題"> </div> </div> <div> <label asp-for="SeoKeyword">SEO關鍵詞</label> <div> <input type="text" asp-for="SeoKeyword" name="SeoKeyword" placeholder="SEO關鍵詞"> </div> </div> <div> <label asp-for="SeoDescription">SEO摘要描述</label> <div> <input type="text" asp-for="SeoDescription" name="SeoDescription" placeholder="SEO摘要描述"> </div> </div> <div> <div> <button type="submit">確定</button> <button type="reset">重置</button> </div> </div> </form>
(4).檢視Index列表的程式碼
針對列表的顯示,又專門編寫了ArticeView的這個ViewModel。
public class ArticleView { public int Id { get; set; } public int CategoryId { get; set; } public string CategoryName { get; set; } public string Title { get; set; } public int ViewCount { get; set; } public int Sort { get; set; } public string Author { get; set; } public string Source { get; set; } public int AddManagerId { get; set; } public DateTime AddTime { get; set; } }
@using Humanizer; @using RjWebCms.Db; @using RjWebCms.Models.Articles; @model PaginatedList<ArticleView> @{ ViewData["Title"] = "文章列表"; } @section Scripts{ <script src="~/js/jquery-2.1.0.min.js"></script> <script type="text/javascript"> function DelAll() { var ids = document.getElementsByName("#chk_ids"); var arrIds = ""; var n = 0; for (var i = 0; i < ids.length; i++) { if (ids[i].checked == true) { arrIds += ids[i].value + ","; n++; } } if (n == 0) { alert("請選擇要刪除的資訊"); return; } arrIds = arrids.substr(0, arrIds.length - 1); // if (confirm("確定要全部刪除選擇資訊嗎")) { $.ajax({ type: "post", url: "/Article/DeleteAll", data: { ids: arrIds }, success: function (data, state) { alert('刪除成功!'); window.location.href = ""; }, error: function (data, state) { alert('刪除失敗'); } }); } } </script> } <div class="panel panel-default todo-panel"> <div class="panel-heading">@ViewData["Title"]</div> @Html.AntiForgeryToken() <form asp-action="Index" method="get"> <table> <tr><td><a asp-controller="Article" asp-action="Create">新增</a></td></tr> <tr> <td>查詢關鍵詞:<input type="text" name="SearchString" value="@ViewData["CurrentFilter"]" /></td> <td><input type="submit" value="查詢" /></td> <td><a asp-action="Index">Back</a></td> <td><a asp-action="DeleteAll">批次刪除</a></td> </tr> </table> </form> <table class="table table-hover"> <thead> <tr> <td>✔</td> <td><a asp-action="Index" asp-route-sortOrder="@ViewData["NameSortParm"]" asp-route-currentFilter="@ViewData["CurrentFilter"]">標題</a></td> <td>類別</td> <td><a asp-action="Index" asp-route-sortOrder="@ViewData["DateSortParm"]" asp-route-currentFilter="@ViewData["CurrentFilter"]">新增時間</a></td> <td>作者</td> <td>操作</td> </tr> @foreach (var item in Model) { <tr> <td><input type="checkbox" class="done-checkbox" name="chk_ids" value="@item.Id"></td> <td>@item.Title</td> <td>@item.CategoryName</td> <td>@item.AddTime</td> <td>@item.Author</td> <td> <a asp-action="Details" asp-route-id="@item.Id">Details</a> <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> <a asp-action="Delete" asp-route-id="@item.Id">Delete</a> </td> </tr> } </thead> </table> @{ var prevDisabled = !Model.HasPreviousPage ? "disabled" : ""; var nextDisabled = !Model.HasNextPage ? "disabled" : ""; ; } <a asp-action="Index" asp-route-sortOrder="@ViewData["CurrentSort"]" asp-route-pageNumber="@(Model.PageIndex - 1)" asp-route-currentFilter="@ViewData["CurrentFilter"]" class="btn btn-default @prevDisabled"> 上一頁 </a> <a asp-action="Index" asp-route-sortOrder="@ViewData["CurrentSort"]" asp-route-pageNumber="@(Model.PageIndex + 1)" asp-route-currentFilter="@ViewData["CurrentFilter"]" class="btn btn-default @nextDisabled"> 下一頁 </a> <div class="panel-footer add-item-form"> <!--TODO: Add item form --> </div> </div>
(5).Controller部分的全部程式碼,注意看程式碼註釋
public class ArticleController : Controller { private readonly IHostEnvironment _hostEnvironment; private readonly IArticleService _articleService; private readonly IArticleCategoryService _articleCategoryService; private readonly AppDbContext _appDbContext; public ArticleController(IArticleService articleService, IArticleCategoryService articleCategoryService,AppDbContext appDbContext,IHostEnvironment hostEnvironment) { _hostEnvironment = hostEnvironment; _appDbContext = appDbContext; _articleService = articleService; _articleCategoryService = articleCategoryService; } public async Task<IActionResult> Index(string sortOrder, string currentFilter, string searchString, int? pageNumber) { ViewData["CurrentSort"] = sortOrder; ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : ""; ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date"; if (searchString != null) { pageNumber = 1; } else { searchString = currentFilter; } ViewData["CurrentFilter"] = searchString; var article = from s in _appDbContext.Article join p in _appDbContext.ArticleCategory on s.CategoryId equals p.Id select new ArticleView { Id = s.Id, CategoryId = s.CategoryId, CategoryName = p.Title, Title = s.Title, Sort = s.Sort, AddManagerId = s.AddManagerId, AddTime = s.AddTime, Author = s.Author, Source = s.Source, ViewCount = s.ViewCount, }; if (!string.IsNullOrEmpty(searchString)) { article = article.Where(s => s.Title.Contains(searchString)); } switch (sortOrder) { case "name_desc": article = article.OrderByDescending(s => s.Title) ; break; case "Date": article = article.OrderBy(s => s.AddTime); break; case "date_desc": article = article.OrderByDescending(s => s.AddTime); break; default: article = article.OrderBy(s => s.Title); break; } int pageSize = 4; return View(await PaginatedList<ArticleView>.CreateAsync(article.AsNoTracking(), pageNumber ?? 1, pageSize)); } [HttpGet] public async Task<IActionResult> CreateAsync() { #region 繫結類別下拉框 var categories = await _articleCategoryService.GetArticleCategory();//列出文章類別字典 var categoryItems = new List<SelectListItem>() { new SelectListItem(){ Value="0",Text="全部",Selected=true} }; //全部列出並轉成DropDownList物件 List<CategorySelectItemListView> list = new List<CategorySelectItemListView>(); foreach (var category in categories) { list.Add(new CategorySelectItemListView { Id=category.Id, Title = category.Title, ParentId = category.ParentId }); } #region 迴圈巢狀呼叫 //List<CategorySelectItemListView> list1 = GetChildCategory(list); //foreach (var li in list1) //{ // categoryItems.Add(new SelectListItem { Value = li.Id.ToString(), Text = li.Title }); // if (li.Children.Count > 0) // { // foreach(var t in li.Children) // categoryItems.Add(new SelectListItem { Value = t.Id.ToString(),Text= "|-" + t.Title }); // } //} #endregion #region 遞迴呼叫 List<CategorySelectItemListView> list1 = GetChildCategory(list, new List<CategorySelectItemListView>(), 0); foreach (var li in list1) { categoryItems.Add(new SelectListItem { Value = li.Id.ToString(), Text = li.Title }); if (li.Children.Count > 0) { foreach (var t in li.Children) categoryItems.Add(new SelectListItem { Value = t.Id.ToString(), Text = " |-" + t.Title }); } } #endregion ViewBag.database = categoryItems; #endregion return View(); } [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> CreateAsync(Article article,[FromForm]IFormCollection fromData) { //去掉對欄位IsSystem的驗證,IsSystem在資料庫是bool型別,而前端是0和1,ModelState的驗證總是報false,所以去掉對其驗證 //ModelState.Remove("IsSystem");//在View端已經解決了了bool型別,那麼此行程式碼可以不用 #region 下拉式選單 string strCategoryId = Request.Form["ddl_CategoryId"]; if (!string.IsNullOrEmpty(strCategoryId)) article.CategoryId = int.Parse(strCategoryId); else article.CategoryId = 0; #endregion #region 核取方塊 article.IsTop = fromData["IsTop"] != "false";//使用FormCollection時,可以這樣 article.IsRed = fromData["IsRed"] != "false"; article.IsSlide = fromData["IsSlide"] != "false"; //也可以這樣取值,但要注意View內的寫法 //if (!string.IsNullOrEmpty(fromData["IsTop"])) // article.IsTop = true; //else // article.IsTop = false; #endregion #region 上傳檔案 IFormFileCollection files = fromData.Files; foreach(var formFile in files) { //var fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"'); string webContentPath = _hostEnvironment.ContentRootPath; var fileExt = formFile.FileName.Substring(formFile.FileName.LastIndexOf('.'));//副檔名 var fileNew = DateTime.Now.ToString("yyyyMMddHHmmss") + fileExt; //給檔案重新命名 //string upLoadPath = webContentPath + $@"\{fileName}"; string upLoadPath = webContentPath + $@"\UpFiles"; var fileUrl = upLoadPath + $@"\{fileNew}"; if (formFile.Length > 0) { using (var stream = new FileStream(fileUrl,FileMode.Create)) { await formFile.CopyToAsync(stream); } } article.ImageUrl = "../UpFiles/" + fileNew; } #endregion if (ModelState.IsValid) { var successful = await _articleService.AddArticleAysnc(article); if (successful) return RedirectToAction("Index"); else return BadRequest("失敗"); } return View(article); } [HttpGet] public async Task<IActionResult> Edit(int id) { if (string.IsNullOrEmpty(id.ToString())) return NotFound(); var article = await _articleService.FindArticleAsync(id); if (article == null) return NotFound(); #region 繫結角色下拉框 var categories = await _articleCategoryService.GetArticleCategory();//列出文章類別字典 var categoryItems = new List<SelectListItem>() { new SelectListItem(){ Value="0",Text="全部",Selected=true} }; //全部列出並轉成DropDownList物件 List<CategorySelectItemListView> list = new List<CategorySelectItemListView>(); foreach (var category in categories) { list.Add(new CategorySelectItemListView { Id = category.Id, Title = category.Title, ParentId = category.ParentId }); } #region 遞迴呼叫 List<CategorySelectItemListView> list1 = GetChildCategory(list, new List<CategorySelectItemListView>(), 0); foreach (var li in list1) { categoryItems.Add(new SelectListItem { Value = li.Id.ToString(), Text = li.Title }); if (li.Children.Count > 0) { foreach (var t in li.Children) categoryItems.Add(new SelectListItem { Value = t.Id.ToString(), Text = " |-" + t.Title }); } } #endregion #region 遍歷並選中 foreach (SelectListItem item in categoryItems) { if (item.Value == article.CategoryId.ToString()) item.Selected = true; } #endregion ViewBag.database = categoryItems; #endregion return View(article); } [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Edit(int id, [FromForm]Article article) { if (id != article.Id) { return NotFound(); } #region 下拉式選單 string strCategoryId = Request.Form["ddl_CategoryId"]; if (!string.IsNullOrEmpty(strCategoryId)) article.CategoryId = int.Parse(strCategoryId); else article.CategoryId = 0; #endregion #region 核取方塊 if (Request.Form["IsTop"].Contains("true")) article.IsTop = true; else article.IsTop = false; if (Request.Form["IsRed"].Contains("true")) article.IsRed = true; else article.IsRed = false; if (Request.Form["IsSlide"].Contains("true")) article.IsSlide = true; else article.IsSlide = false; #endregion //ModelState.Remove("IsTop"); //ModelState.Remove("IsRed"); //ModelState.Remove("IsSlide"); if (ModelState.IsValid) { try { var result = await _articleService.UpdateArticleAsync(id, article); //跳轉 if (result) return RedirectToAction("Index"); else return BadRequest("編輯失敗"); } catch (Exception ex) { return BadRequest("編輯失敗"); } } else { return BadRequest("資料輸入有誤!"); } } /// <summary> /// 遞迴函數,實現獲取子選單 /// </summary> /// <param name="lists">遞迴前的列表</param> /// <param name="newlists">遞迴後的新列表</param> /// <param name="parentId">父Id</param> /// <returns></returns> public static List<CategorySelectItemListView> GetChildCategory(List<CategorySelectItemListView> lists, List<CategorySelectItemListView> newlists, int parentId) { newlists = new List<CategorySelectItemListView>(); List<CategorySelectItemListView> tempList = lists.Where(c => c.ParentId == parentId).ToList(); for (int i = 0; i < tempList.Count; i++) { CategorySelectItemListView category = new CategorySelectItemListView(); category.Id = tempList[i].Id; category.ParentId = tempList[i].ParentId; category.Title = tempList[i].Title; category.Children = GetChildCategory(lists, newlists, category.Id); newlists.Add(category); } return newlists; } /// <summary> /// 迴圈巢狀,實現獲取子選單 /// </summary> /// <param name="lists">迴圈遍歷前的列表</param> /// <returns></returns> public static List<CategorySelectItemListView> GetChildCategory(List<CategorySelectItemListView> lists) { List<CategorySelectItemListView> categorylist = new List<CategorySelectItemListView>(); for (int i = 0; i < lists.Count; i++) { if (0 == lists[i].ParentId) categorylist.Add(lists[i]); for (int j = 0; j < lists.Count; j++) { if (lists[j].ParentId == lists[i].Id) lists[i].Children.Add(lists[j]); } } return categorylist; } }
(6).Service應用層程式碼
public class ArticleService : IArticleService { private readonly AppDbContext _appDbContext; public ArticleService(AppDbContext appDbContext) { _appDbContext = appDbContext; } /// <summary> /// 新增文章 /// </summary> /// <param name="article"></param> /// <returns></returns> public async Task<bool> AddArticleAysnc(Article article) { article.IsDeleted = false; article.AddManagerId = 1;//使用者id article.AddTime = DateTime.Now; article.IsPublish = true; await _appDbContext.Article.AddAsync(article); var result = await _appDbContext.SaveChangesAsync(); return result == 1; } /// <summary> /// 刪除文章 /// </summary> /// <param name="Id"></param> /// <returns></returns> public async Task<bool> DeleteArticleAsync(int Id) { var article = await _appDbContext.Article.FirstOrDefaultAsync(x => x.Id == Id); if (article != null) { _appDbContext.Article.Remove(article); } var result = await _appDbContext.SaveChangesAsync(); return result == 1; //注意(result==1 如果等式成立,則返回true,說明刪除成功) } /// <summary> /// 按Id查詢文章 /// </summary> /// <param name="Id"></param> /// <returns></returns> public async Task<Article> FindArticleAsync(int Id) { var item = await _appDbContext.Article.Where(x => x.Id == Id).FirstOrDefaultAsync(); return item; } /// <summary> /// 按標題查詢文章 /// </summary> /// <param name="title"></param> /// <returns></returns> public async Task<Article[]> GetArtcleByTitle(string title) { var items = await _appDbContext.Article.Where(x => x.Title.Contains(title)).ToArrayAsync(); return items; } /// <summary> /// 查詢文章 /// </summary> /// <returns></returns> public async Task<Article[]> GetArticles() { var items = await _appDbContext.Article.Where(x => x.IsDeleted==false).ToArrayAsync(); return items; } /// <summary> /// 更新文章 /// </summary> /// <param name="id"></param> /// <param name="article"></param> /// <returns></returns> public async Task<bool> UpdateArticleAsync(int id, Article article) { var oldArticle = await FindArticleAsync(id); //找出舊物件 //將新值賦到舊物件上 oldArticle.Title = article.Title; oldArticle.CategoryId = article.CategoryId; oldArticle.SeoDescription = article.SeoDescription; oldArticle.SeoTitle = article.SeoTitle; oldArticle.SeoKeyword = article.SeoKeyword; oldArticle.Content = article.Content; oldArticle.Sort = article.Sort; oldArticle.Source = article.Source; oldArticle.IsSlide = article.IsSlide; oldArticle.IsPublish = article.IsPublish; oldArticle.IsRed = article.IsRed; oldArticle.IsTop = article.IsTop; oldArticle.ViewCount = article.ViewCount; oldArticle.Author = article.Author; oldArticle.ImageUrl = article.ImageUrl; oldArticle.ModifyManagerId = 11;// oldArticle.ModifyTime = DateTime.Parse(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); //對舊物件執行更新 _appDbContext.Entry(oldArticle).State = EntityState.Modified; var result = await _appDbContext.SaveChangesAsync(); return result == 1; } }
再談CheckBox的使用
1.在View檢視頁增加的程式碼格式如果為:
<input type="checkbox" name="IsTop" asp-for="IsTop" />置頂
或者是這樣:
<input asp-for="IsTop" />置頂
那麼在生成的html程式碼中,都會自動成id,name,type=「checkbox」 value的屬性。
2.在Controller中進行取值時的程式碼為:
if (Request.Form["IsTop"].Contains("true"))
article.IsTop = true;
else
article.IsTop = false;
跟蹤時發現,View中Checkbox選中是,會產生true和false兩個值,如圖跟蹤變數發現:
如此,取值時,就用了Contains功能,因為View中CheckBox沒選中,這隻有一個false值;
3.在View檢視頁增加程式碼的格式如果為:
<input type="checkbox" name="IsTop" @(Html.Raw(@Model.IsTop ? "checked=\"checked\"" : "")) asp-for="IsTop" />置頂
4.在Controller中進行取值時的程式碼為:
if (!string.IsNullOrEmpty(Request.Form["IsTop"]))
article.IsTop = true;
else
article.IsTop = false;
跟蹤時發現,View中的CheckBox選中是,取到的值為「on」,如圖跟蹤發現:
所以,才用了IsNullOrEmpty這個函數,依據判空來確定是否選中。
但是這樣寫有個問題,在ModelState.IsValid()的模型驗證中,一直無法通過,IsTop一直為false,為此,我乾脆就把其去除掉驗證:
ModelState.Remove("IsTop");//去除name=IsTop的checkbox的模型驗證
5.使用Checkbox,還是要看給在資料表中為其定義的欄位型別,Model中的指定型別和驗證屬性,如果你賦予了Value值,那麼就在Controller中取值,Asp.Net Core中Checkbox預設是True和False的值,網上關於@Html.CheckBox()形式也行,你可以嘗試跟蹤變數值來判斷如何處理,其宗旨就是根據具體條件來處理。