父类和子类在同一张表
现在出现了父类和子类一般都会存在一张表中,然后设计数据库的时候把父类的ID存到子类另一个字段中就实现了关联。
例如: 网站的左边导航 第一层就可以看做父类,里面就可以看做子类。
我们就先来看看怎么做导航。
效果图:
一切为了用户,这个只显示一个下面出来,不要显示多个不然很难看
① 前台页面
<div > <ul id="test" > <asp:Repeater ID="replist" runat="server" OnItemDataBound="replist_ItemDataBound"> <ItemTemplate> <li> <a class="one" href="javascript:void()"><em><%# Eval("Name")%></em></a> <ul style="display: none;"> <asp:HiddenField ID="hfId" runat="server" Value='<%# Eval("ID") %>' /> <asp:Literal ID="LitFirst" runat="server"></asp:Literal> //这个标签用来替换数据 </ul> </li> </ItemTemplate> </asp:Repeater> </ul> </div> <script src="../JS/jquery-1.11.0.min.js"></script> <script> $("ul#test").on("click", "li", function () { //$("ul li ul").css("display", "none"); if ($(this).hasClass("show")) { $("ul#test li").removeClass("show"); Test(); //$(this).removeClass("show"); // $(this).find("ul").css("display", "none"); } else { $("ul#test li").removeClass("show"); $(this).addClass("show"); Test(); //$(this).find("ul").css("display", "block"); } }); //用来隐藏和显示下面的数据 function Test() { $("ul#test li").each(function () { if ($(this).hasClass("show")) { $(this).find("ul").css("display", "block"); } else { $(this).find("ul").css("display", "none"); } }); } </script>
②样式 简单设置一下
<style> ul { list-style:none; } em { font-style: normal; } a {text-decoration:none; } </style>
③取数据 由于我们搭建的div的原因,想要显示出所有的数据需要用到Repter里面的数据绑定事件
protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { Binging(); } } private void Binging() { DataSet ds = new FunctionDemo.BLL.Category().GetList("Pid=0"); replist.DataSource = ds.Tables[0]; replist.DataBind(); } /// <summary> /// 每绑定一行就触发一次 一般这个方法用来加载每一行的子类数据 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void replist_ItemDataBound(object sender, RepeaterItemEventArgs e) { Literal LitFirst = (Literal)e.Item.FindControl("LitFirst"); HiddenField hfId = (HiddenField)e.Item.FindControl("hfId"); List<FunctionDemo.Model.Category> cateModel = new FunctionDemo.BLL.Category().GetModelList("Pid=" + hfId.Value); foreach (FunctionDemo.Model.Category item in cateModel) { LitFirst.Text = LitFirst.Text + "<li>"+item.Name+"</li>"; } }
④数据的设计
父类 Pid=0 子类的Pid就是父类的ID LevalNum用来表示
第一种父子类就说到这,下面说的是第二种方式
其中的有个方法值得学习下
我们换成下拉框来试试 按分类显示数据 列: 父类 子类 父类 子类 。。
①页面
<form id="form1" runat="server"> <asp:DropDownList ID="ddlDemo" runat="server"></asp:DropDownList> </form>
②代码
public FunctionDemo.BLL.Category categoryBLL = new FunctionDemo.BLL.Category(); protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { BingDDL(); } } public void BingDDL() { DataTable dt = categoryBLL.GetList("").Tables[0]; //categoryBLL.GetListChild(0, true); ddlDemo.Items.Add(new ListItem("类别", "0")); foreach (DataRow item in dt.Rows) { string title = ""; if (int.Parse(item["LevalNum"].ToString()) > 0) { title =" " + "|--" + item["Name"].ToString(); } else { title = item["Name"].ToString(); } ddlDemo.Items.Add(new ListItem(title, item["ID"].ToString())); } }
③显示效果
这一切尽是如此的完美,好像很好一样的。
我们做项目时并不是说所以的东西都是好的,很可能做了一半让你加几条数据,我们来试一试 NBA 加一条 ,爱好加一条,别找我加一条
我们的数据
然后再显示
我们如何把NBA子类往上面移动呢,这是问题的关键,那么就是查询数据的问题了。
我们改动了取数据的方法
然后显示数据
这样很符合我们的要求。
我们来看看这个方法写了什么。
/// <summary> /// 获取全部类别 /// </summary> /// <param name="strWhere">后面的bool值可以显示是否显示禁用的数据,这里并没做处理</param> /// <returns></returns> public DataTable GetListChild(int PID, bool isState) { StringBuilder strSql = new StringBuilder(); strSql.Append("select ID,Name,Pid,LevalNum"); strSql.Append(" FROM Category "); //strSql.Append(" Where ParentId=" + PId); if (isState) { strSql.Append("Where 1=1 "); } strSql.Append(" order by Id asc"); DataSet ds = DbHelperSQL.Query(strSql.ToString()); 我们的数据查找完了,跟以前是一样的 DataTable oldData = ds.Tables[0] as DataTable; if (oldData == null) { return null; } //复制结构 DataTable newData = oldData.Clone(); clone()是基类Object的方法 //调用迭代组合成DAGATABLE 用旧数据得到新数据 GetChannelChild(oldData, newData, PID); return newData; } /// <summary> /// 获取子类别 /// </summary> /// <param name="parentId">父编号</param> /// <param name="stateInfo">状态</param> /// <returns></returns> private void GetChannelChild(DataTable oldData, DataTable newData, int PId) { //第一遍进来的时候是获取父类 pid=0 我们只需每取一条父类数据,然后把改类的子类数据先加载进去就可以了 DataRow[] dr = oldData.Select("Pid=" + PId); for (int i = 0; i < dr.Length; i++) { //添加一行数据 DataRow row = newData.NewRow(); row["ID"] = int.Parse(dr[i]["ID"].ToString()); row["Name"] = dr[i]["Name"].ToString(); row["Pid"] = dr[i]["Pid"].ToString(); row["LevalNum"] = dr[i]["LevalNum"].ToString(); newData.Rows.Add(row); //调用自身迭代 this.GetChannelChild(oldData, newData, int.Parse(dr[i]["ID"].ToString())); //把父类的ID作为条件传进去,子类的数据就会出来 } }
这个方法是一种思想,会的话可以做别的事。
上面是满足我们的原数据和新数据都是在一张表,我们就可以用clone()方法,如果我的新表需要添加字段
//创建一个新的DataTable,这里可以添加我们的列 DataTable newData = new DataTable(); newData.Columns.Add("id", typeof(int)); newData.Columns.Add("parent_id", typeof(int)); newData.Columns.Add("class_layer", typeof(int)); newData.Columns.Add("nav_type", typeof(string)); newData.Columns.Add("name", typeof(string)); newData.Columns.Add("title", typeof(string)); newData.Columns.Add("sub_title", typeof(string)); newData.Columns.Add("icon_url", typeof(string)); newData.Columns.Add("link_url", typeof(string)); newData.Columns.Add("sort_id", typeof(int)); newData.Columns.Add("is_lock", typeof(int)); newData.Columns.Add("remark", typeof(string)); newData.Columns.Add("action_type", typeof(string)); newData.Columns.Add("is_sys", typeof(int));
//调用迭代组合成DAGATABLE 用旧数据得到新数据
GetChannelChild(oldData, newData, PID);
return newData;
//在GetChannelChild()这个方法里面的添加数据里面就可以把新列的数据添加进去