Angular实现双向折叠列表组件的示例代码


Posted in Javascript onNovember 21, 2017

最近在做一个双向折叠组件,如下图所示,页面是分为两组,左边页面是Summary Panel,主要是一组列表,右边页面是Detail Panel,展示左边列表中某一项的具体信息,我们把它记作“Middle State”。

Angular实现双向折叠列表组件的示例代码

我们还看到有“<”和“>”两组按钮,这就是我们要做的“双向折叠组件”。点击左边的“<”,Summary Panel折叠起来,Detail Panel铺满整个页面,我们把它记作“Left State”,如下图:

Angular实现双向折叠列表组件的示例代码

在Summary Panel折叠状态下,点击“>”,又回到“Middle State”。点击“>”,Detail Panel折叠起来,Summary Panel铺满整个页面,我们把它记作“Right State”,如下图:

Angular实现双向折叠列表组件的示例代码

我们通过以上的需求分析可知,

1.页面的总体布局是一个Summary的div,两个箭头buttons,一个Detail的div。

2.页面总共有三种state:“Middle”、“Left”、“Right”,有两个button:“<”和“>”,也就是两个button去控制三个state。

因此我们需要定义一个枚举来记录页面的三种状态(注意,定义枚举要用export导出,否则后面会出错)

export enum CollapseExpandState {
  Middle = 1,
  Left,
  Right
}

页面的结构如下,并且通过一个变量_collapseExpandState去控制“left”和“right”两个button,具体为“<”会在页面状态为“Middle”和“Right”的情况下出现,“>”会在页面状态为“Middle”和“Left”状态下出现,从需求图中即可得知:

<div id="container">
  <div id="summary"></div>
  <div id="buttons">
    <div id="left" *ngIf="_collapseExpandState === CollapseExpandState.Middle || CollapseExpandState.Right" (click)="_onHandleLeft($event)">《</div>
    <div id="right"> *ngIf="_collapseExpandState === CollapseExpandState.Middle || CollapseExpandState.Left" (click)="_onHandleLeft($event)">》</div>
  </div>
  <div id="detail"></div>
</div>

这里在angular的template中用到了枚举,遭遇了一些麻烦,如果我们按上述定义了枚举,并且在Angular Component的template中用了枚举,我们会得到以下的错误提示:

TypeError: Cannot read property 'Middle' of undefined

也就是说,在Angular2的template中无法识别定义的枚举类型CollapseExpandState,这是因为你写的Angular Component的template模板的执行环境是你定义的component class,但是在class中并没有关于CollapseExpandState枚举的任何引用,所以Angular在为你的component生成模板的时候认为CollapseExpandState是undefined的。知道了原因,解决方案就很容易了,只需要在component class中加入这个枚举的引用即可:

@component(...)
export class ContainerWidget {
  public CollpaseExpandState: any = CollapseExpandState;
}

我们通过枚举状态来控制了两个buttons是否在恰当的页面状态显示与否,但是即使是同一个buttons,在不同的页面状态下所用到的样式也会不同,这里的样式其实最主要的就是位置了。我们先来考虑如何去控制button的样式,再来考虑如何去正确定位不同页面状态下button的位置。

对于控制button的样式,我们需要控制三个样式:"left button"、"right button"还有“buttons”。能够想到有三种方案:

I、用ng-class

ng-class一般的用法如下:

<some-element [ngClass]="{'first': true, 'second': true, 'third': false}">...</some-element>

因此它需要用boolean去控制,每一种样式需要一个boolean去控制,left和right各有两种样式,buttons有三种样式,这样就需要用5个boolean去控制,略显麻烦。

II、 用ElementRef.nativeElement.className

分别在buttons、left和right上用模板变量,然后在class中定义:

@ViewChild("buttons") buttons: ElementRef;
@ViewChild("left") left: ElementRef;
@ViewChild("right") right: ElementRef;

在处理函数中这样去给class赋值从而改变样式:

this.left.nativeElement.className = "XXXXX";

这样的话,我们需要从元素的角度出发,只需要3个元素变量,从而改变元素上的className即可。但是这样做有个隐患,注意到我们是用的ngIf来控制left和right在不同状态下是否存在,因为每一次事件处理都需要对三个元素的样式进行赋值,但是如果某个页面状态下ngIf为false从而元素不存在,那么就会报“Null Pointer”的错误,所以如果所引用的元素受到了ngIf的控制,不确定是否一定存在的情况下,要慎用该方法为元素赋予样式。

III、 用class="{{}}"

为了II中的尴尬,我们采用在HTML元素上对class进行直接赋值的方式,但是需要借用插值表达式{{}}。我们在css中用class的形式定义好样式,并且在compoennt class中定义三个字符串变量记录className,然后在事件处理函数中把相应的className赋予变量即可。这样我们就不用担心元素是否存在而导致的空指针了。

考虑完如何控制样式,下面我们进入CSS样式的讨论,这里其实主要考虑的就是位置。

我们采用flex布局,从左到右依次排列Summary Panel, buttons和Detail Panel。我们希望Buttons向左移,但是空出的位置被Detail Panel来填充。首先来看一下不设样式的效果图:

Angular实现双向折叠列表组件的示例代码

显然buttons是占据了文档流的位置的,如果这时候我们用relative定位buttpms,并且设置left的值为-74px(注意到left为负数就会把元素往左推):

.buttons{
  display: flex;
  position: relative;
  margin-top: 23px;
  left: -74px;
}

效果图为:

Angular实现双向折叠列表组件的示例代码

发现如果用left的话,buttons原来的文档流位置依然存在,只是buttons相对于原来的位置移动了一定的位移。

如果我们用margin-left来设置呢:

.buttons{
  display: flex;
  position: relative;
  margin-top: 23px;
  margin-left: -74px;
}

效果图为:

Angular实现双向折叠列表组件的示例代码

它和left不同之处在于,left会留住原来的文档流位置,但是用margin-left原来的文档流位置会消失,而由后面的元素补充过来,而我们想要的效果,正好是用margin-left来实现的。

所以用CSS定位的时候,要明白left和margin-left的区别,从而选择正确的方式来定位。

总结一下,从这个案例中我们学习到了:

  1. 双向折叠可以用“3种页面状态去控制2个按钮”来实现
  2. enum在Angular Component的template中用到时,需要在compnent class中添加它的引用
  3. 控制元素样式有很多方法,要选择合适的方法
  4. CSS定位中left和margin-left虽然都能把元素推向左边,但是left保留原来文档流位置,margin-left不保留原来文档流位置。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript学习笔记(十二) RegExp类型介绍
Jun 20 Javascript
jquery高效反选具体实现
May 05 Javascript
JavaScript原生对象之Date对象的属性和方法详解
Mar 13 Javascript
javascript实现倒计时(精确到秒)
Jun 26 Javascript
基于jQuery实现二级下拉菜单效果
Feb 01 Javascript
JS提示:Uncaught SyntaxError: Unexpected token ILLEGAL错误的解决方法
Aug 19 Javascript
AngularJS控制器之间的通信方式详解
Nov 03 Javascript
JavaScript实现设置默认日期范围为最近40天的方法分析
Jul 12 Javascript
浅谈Vuex的状态管理(全家桶)
Nov 04 Javascript
JavaScript设计模式之缓存代理模式原理与简单用法示例
Aug 07 Javascript
微信小程序五子棋游戏AI实现方法【附demo源码下载】
Feb 20 Javascript
浅谈JSON5解决了JSON的两大痛点
Dec 14 Javascript
bootstrap表格内容过长时用省略号表示的解决方法
Nov 21 #Javascript
bootstrap treeview 扩展addNode方法动态添加子节点的方法
Nov 21 #Javascript
Angular4自制一个市县二级联动组件示例
Nov 21 #Javascript
微信小程序实现跟随菜单效果和循环嵌套加载数据
Nov 21 #Javascript
基于bootstrap写的一点localStorage本地储存
Nov 21 #Javascript
微信小程序实现下拉刷新和轮播图效果
Nov 21 #Javascript
基于 Vue.js 之 iView UI 框架非工程化实践记录(推荐)
Nov 21 #Javascript
You might like
如何使用PHP中的字符串函数
2006/10/09 PHP
解析curl提交GET,POST,Cookie的简单方法
2013/06/29 PHP
js代码实现微博导航栏
2015/07/30 PHP
WordPress用户登录框密码的隐藏与部分显示技巧
2015/12/31 PHP
Yii2搭建后台并实现rbac权限控制完整实例教程
2016/04/28 PHP
PHP下 Mongodb 连接远程数据库的实例代码
2017/08/30 PHP
PHP使用 Pear 进行安装和卸载包的方法详解
2019/07/08 PHP
用javascript来实现动画导航效果的代码
2007/12/16 Javascript
Node.js生成HttpStatusCode辅助类发布到npm
2013/04/09 Javascript
JS实现字体选色板实例代码
2013/11/20 Javascript
jQuery横向擦除焦点图特效代码分享
2015/09/06 Javascript
JavaScript排序算法动画演示效果的实现方法
2016/10/18 Javascript
jquery的父、子、兄弟节点查找,节点的子节点循环方法
2016/12/07 Javascript
JavaScript中双符号的运算详解
2017/03/12 Javascript
jquery插件ContextMenu设置右键菜单
2017/03/13 Javascript
微信小程序 登录的简单实现
2017/04/19 Javascript
在vue中添加Echarts图表的基本使用教程
2017/11/22 Javascript
react项目实践之webpack-dev-serve
2018/09/14 Javascript
vue插件--仿微信小程序showModel实现模态提示窗功能
2020/08/19 Javascript
[04:44]DOTA2西游记战队视频彩蛋流出 师徒开黑巧遇林书豪
2016/08/03 DOTA
Python实现的简单算术游戏实例
2015/05/26 Python
Python基于有道实现英汉字典功能
2015/07/25 Python
Python3中条件控制、循环与函数的简易教程
2017/11/21 Python
在python中利用opencv简单做图片比对的方法
2019/01/24 Python
python 实现将文件或文件夹用相对路径打包为 tar.gz 文件的方法
2019/06/10 Python
django的ORM操作 删除和编辑实现详解
2019/07/24 Python
python requests使用socks5的例子
2019/07/25 Python
Python中调用其他程序的方式详解
2019/08/06 Python
基于Tensorflow的MNIST手写数字识别分类
2020/06/17 Python
Python爬虫自动化获取华图和粉笔网站的错题(推荐)
2021/01/08 Python
阿迪达斯意大利在线商店:adidas意大利
2016/09/19 全球购物
好矿嫂事迹材料
2014/01/21 职场文书
应聘销售主管的求职信
2014/04/26 职场文书
生产设备维护保养制度
2015/08/06 职场文书
文案策划岗位个人自我评价(范文)
2019/08/08 职场文书
微信小程序APP页面的之间的相互传递参数以及自定义组件
2022/04/19 Javascript