[学习笔记] Cordova+AmazeUI+React 做个通讯录 - 联系人列表(2)
[学习笔记] Cordova+AmazeUI+React 做个通讯录 系列文章
目录
- 准备
- 联系人列表(1)
- 联系人列表(2)
- 联系人详情
- 单页应用 (With Router)
- 使用 SQLite
传送门:全部章节 示例代码
前一节已经讲述了表头和列表的组件应用,但组件列表项的简单应用并不能满足我们的需求。本节将继续深入探讨联系人列表的细节实现
自定义列表项
本来列表项并不需要自定义,只需要在 <A.ListItem>
中加入合适的组件就行,比如
var items = data.map(function(t) {return (<A.ListItem><A.Icon icon="user" />{t.name}<A.Icon icon="phone" /></A.ListItem>);
});
然而为了将列表项抽象出来,以及便于对列表项的细节调整和后期可能需要实现的操作,还是把它定义成一个组件的好,这个组件就叫 Pserson
,只需要把原来 map()
中的代码拷贝到 render()
的 return
中,再把 map()
中的 return
稍稍改一下
var Person = React.createClass({render: function() {return (<A.ListItem><A.Icon icon="user" />{t.name}<A.Icon icon="phone" /></A.ListItem>);}
});
var items = data.map(function(t) {return <Person />
});
很明显,上面的代码是有问题的:
- Person 组件中的
t
变量没有赋值,所以t.name
一定会抛异常; return <Person />
看起来没有问题,但是并没有传入t
值,所以列表的每一项都会是一模一样的
于是这里遇到了问题:怎么从父级控件向子级控件传入参数?
从父级控件向子级控件传入参数
这里可以作个比喻:从父级控件调用子级控件,就像在某个函数中调用其它函数一样。那么传入参数也就像调用函数时传入的参数一样。
React 通过 props
向子级控件传入参数,在 JSX 语法中,写法和 XML 的属性定义类似。比如向 Person 控件传入 name
和 tel
参数——应该叫属性更准确,就可以这样
var items = data.map(function(t) {return <Person name={t.name} tel={t.tel} />
});
注意到属性值是用的花括号
{}
包起来的,这表示传入的是表达式,需要先计算其结果。如果是已知的字符串,不需要计算的,可以像 XML 属性那样用使用引号。
然后,在 Person 组件中,通过 this.props
对象可以使用传入的属性值,比如 this.props.name
就引用了传入的 name
属性。同时,为了稍稍解决一点显示仍然不够美观的问题,这里可以用 AMUIReact.Badge
组件包装一下第 2 个 Icon。
正确的 Person 组件定义如下
var Person = React.createClass({render: function() {return (<A.ListItem><A.Icon icon="user" />{this.props.name}<A.Badge amStyle="success" radius><A.Icon icon="phone" /></A.Badge></A.ListItem>);}
});
对了,这里 {this.props.name}
是引用了输入的 name
属性,而传入的 tel
属性暂时还没使用。同时在使用 Badge 组件的时候,也向其传入了 amStyle
和 radius
两个属性……等等,radius
是属性?
正确,radius
是属性,作为布尔值属性,是可以省略值,这时其值会被当作 true
。这当然不符合 XML 的语法,不过这不是问题,因为这是 JSX 不是 XML。当然对于有强迫症的朋友,也可以显示的指定布尔值:radius={true}
或者 radius={false}
。
千万注意
{true}
和{false}
是用花括号而不是引号包起来的。当然如果用"true"
也不会有问题,但是用"false"
就有问题了——因为在 JavaScript 中"false"
是“真”值(不懂原因的找度娘)!
添加拨号功能
说起来,添加拨号功能真不难——只需要提供一个到 "tel:电话号码
的链接就可以了。上例的 Badge 中,使用一个 <a>
标签包裹 Icon 组件即可,只不过第一次尝试通常都不怎么顺利:
<A.Badge amStyle="success" radius={false}><a href="tel:{this.props.tel}"><A.Icon icon="phone" /></a>
</A.Badge>
上例中 {this.props.tel}
并没有被计算出来,直接作为 URI 字符串的一部分了。好吧,JSX 解释器不认识字符串中的 {}
表达式,所以只好换个写法
<A.Badge amStyle="success" radius={false}><a href={"tel:" + this.props.tel}><A.Icon icon="phone" /></a>
</A.Badge>
由于个人洁癖,最终把 {"tel:" + this.props.tel}
先赋值给一个变量再在 JSX 中引用,所以最终的 Person 组件定义如下
var Person = React.createClass({render: function() {var link = "tel:" + this.props.tel;return (<A.ListItem><A.Icon icon="user" />{this.props.name}<A.Badge amStyle="success" radius><a href={link}><A.Icon icon="phone" /></a></A.Badge></A.ListItem>);}
});
有没有注意到,link
变量定义和赋值都在 return
之前——因为,一定记住,return
后面的是个表达式,不能写语句!
Spread Attributes (传播属性)
“传播属性”这个词翻译得很别扭,所以我宁愿用“Spread 属性”。
在 Page 的 render()
中,可以发现 t
的两个属性 name
和 tel
都被传递给了 Person 组件对象。还好这里只需要传递 2 个属性,如果需要传递的属性是十个八个的,光写属性传递就得累死。
React 当然不会想不到这个问题,所以 JSX 提供了“Spread 属性”语法。只需要简单的使用 {...t}
就可以将 t
的所有属性拷贝到组件的 props 中。因此,map()
部分可以简化为
var items = this.state.persons.map(function(t) {return <Person {...t} />
});
Spread 属性很容易让人联想到 ES2015(ES6)中的“可变参数”(或称“不定参数”)语法——那么在不支持 ESA015 的浏览器中是不是就不能使用 Spread 属性了呢?——当然不会,因为 Spread 属性是 JSX 提供的语法,则 React 解释,而不是由 JavaScript 引擎解释。
效果
现在的效果已经有点像样了,但仍然需要改进。不过如之前所述,这个可以通过样式表来解决,待功能完成得差不多了再来调整。
Ajax 加载数据
到目前为止,数据仍然是以硬代码的形式写在 index.jsx 中的,这是一个同步过程。虽然目前这么做没有问题,但是如果数据需要保存在数据库中,而从数据库获取数据像 AJAX 一样是一个异步过程(到目前状态,是同步还是异步并不清楚,这涉及到 Corodva 对数据库的操作,暂未研究)就麻烦了。因此,现在先把数据独立出来保存在 /js/data.json
中,通过 AJAX 方式先研究一下异步加载数据。
说实在的,为了研究这个问题费了不少脑筋,最后还是在 React 文档中得到了答案(参阅:Load Initial Data vi AJAX)。解决这个问题涉及到了 React 组件数据的另一种保存形式:state,以及 render
之外的两个组件生命周期方法 getInitalState()
和 `componentDidMount()。
到目前为止,一共只写了两个组件:Page 和 Person。很显示,加载整个列表数据的任务应该落在 Page 上。依葫芦画瓢,先把功能实现了再说
var Page = React.createClass({// [1]getInitialState: function() {return {persons: []}},// [2]componentDidMount: function() {$.getJSON("/js/data.json").then(function(data) {if (this.isMounted()) {this.setState({persons: data});}}.bind(this));},render: function() {// [3]var items = this.state.persons.map(function(t) {return <Person {...t} />});return (<div><A.Header title="通讯录" /><A.List>{items}</A.List></div>);}
});
注意 Page 组件中 3 个地方的变化,
- [1],添加了
getInitialState()
方法 - [2],添加了
componentDidMount()
方法 - [3],修改了
map()
的数据源。原来的data
已经不存在了,取而代之的是this.state.persons
。
除此之外还有几点需要注意
$.getJSON().then()
的回调函数中,直接使用了this.isMounted()
和this.setState()
等。函数中的this
怎么还会是组件对象呢?——请注意回调函数后的.bind(this)
。这个方法在 React 的各方实例中经常出现,不失为传递this
的一个好办法。this.isMounted()
的作用是判断当前组件仍然处于 mounted 状态,只有在这个状态下 setState 才有意义。虽然在componentDidMount
事件中写的这段代码,但是由于是异步加载,所以并不知道当前组件是否已经有所变化。- 如果在 render 中写上日志,可以发现,它在 componentDidMount 之前和之后都有执行。很显然,在之前执行的那一次,
this.state.persons
是不存在的。如果没有getIntialState()
,会发现第1次 render 的时候连this.state
都还不存在(不过是null
不是undefined
)。由此证明在使用了React 组件状态数据的情况下,从getInitalSate()
返回初始的状态对象是非常有必要的。
使用样式表美化
现在联系人列表的功能部分已经基本完成,是时候美化一下了。通过浏览器的 Inspect 功能可以看到列表部分的 HTML 渲染出来是这样的(只保留了一个 <li>
示例)
<ul class="am-list"><li class=""><i icon="user" class="am-icon-user"></i><span>张三</span><span class="am-badge am-badge-success am-radius"><a href="tel:13801234567"><i icon="phone" class="am-icon-phone"></i></a></span></li>
</ul>
现在需要美化的事项包括
- 去掉 ul 的 margin-top
- 给 li 加上 padding
- 在第 1 个 icon 后加上 margin-right
所以在 index.css
中删除原来的内容,改为如下内容
ul {margin-top: 0;
}li {padding: 3px 6px;
}li i:first-child {margin-right: 8px;
}li span:last-child {margin-top: 4px;
}
最后一句是在调试的时候发现电话图标不在正中才加的。
用 className 来定位样式元素
前面的 CSS 最大的问题是选择器不够精准,样式表内容多了之后容易发生各种冲突。在 HTML 中比较好的解决办法是添加 class="xxx"
属性。但是在 React 中添加 class="xxx"
属性,会被认为是 props 数据。React 中是用 className
来表示样式类的。
所以,需要将原来的代码稍做变动,加上适当的 className
属性
在 Page 组件中为
<A.List>
添加person-list
类var Page = React.createClass({// ...... 省略代码若干render: function() {var items = this.state.persons.map(function(t) {return <Person {...t} />});return (<div><A.Header title="通讯录" /><A.List className="person-list">{items}</A.List></div>);} });
在 Person 组件中分别添加
person
、person-icon
、person-phone
类var Person = React.createClass({render: function() {var link = "tel:" + this.props.tel;return (<A.ListItem className="person"><A.Icon icon="user" className="person-icon" />{this.props.name}<A.Badge amStyle="success" radius className="person-phone"><a href={link}><A.Icon icon="phone" /></a></A.Badge></A.ListItem>);} });
修改样式表
ul.person-list {margin-top: 0; }li.person {padding: 3px 6px; }li>.person-icon {margin-right: 6px; }li>.person-phone {margin-top: 4px; }
内联样式
个人习惯,我不太喜欢使用内联样式。但如果确实需要使用内联样式,可以通过组件的 style
属性设置,其值可以是一个对象,示例:
render: function() {var styles = {color: "#666666","background-color": "#efefef"};return <A.List style={styles}></A.List>
}
[学习笔记] Cordova+AmazeUI+React 做个通讯录 - 联系人列表(2)相关推荐
- [学习笔记] Cordova+AmazeUI+React 做个通讯录 - 单页应用 (With Router)
[学习笔记] Cordova+AmazeUI+React 做个通讯录 系列文章 目录 准备 联系人列表(1) 联系人列表(2) 联系人详情 单页应用 (With Router) 使用 SQLite 传 ...
- [学习笔记] Cordova+AmazeUI+React 做个通讯录 - 使用 SQLite
[学习笔记] Cordova+AmazeUI+React 做个通讯录 系列文章 目录 准备 联系人列表(1) 联系人列表(2) 联系人详情 单页应用 (With Router) 使用 SQLite 传 ...
- amazeui学习笔记一(开始使用3)--兼容性列表compatibility
amazeui学习笔记一(开始使用3)--兼容性列表compatibility 一.总结 1.不要用ie做前端测试,不要碰ie,尽量用google 浏览器: 按照微软官方的说法,IE 开发者工具中的浏 ...
- amazeui学习笔记--css(常用组件5)--评论列表Comment
amazeui学习笔记--css(常用组件5)--评论列表Comment 一.总结 1.am-comment:使用am-comment来声明评论对象,这个是放在article里面的,虽然article ...
- 影像组学视频学习笔记(14)-特征权重做图及美化、Li‘s have a solution and plan.
本笔记来源于B站Up主: 有Li 的影像组学系列教学视频 本节(14)主要介绍: 特征权重做图及美化 import matplotlib.pyplot as plt %matplotlib inlin ...
- Javascript学习笔记8——用JSON做原型
在Javascript学习笔记5--类和对象中,我简单地提到了利用JSON去构造一个对象.代码如下: <script type="text/javascript">va ...
- 阿里云HaaS100物联网开发板学习笔记(六)做个智能灯---一个完整的开发例子
摘要:本篇文章将前期几个专题综合起来,基于阿里云HaaS100的新固件设计制作一个智能灯.这个智能灯由云平台.手机APP端和设备端组成,基本上涵盖了一个物联网小项目所需的主要步骤. 目录 1.在阿里云 ...
- React学习笔记6:React Hooks API总结
useState-保存状态(惰性初始化) 作用 函数组件添加状态 注意事项 初始化以及更新state 用来声明状态变量 使用步骤(使用useState来创建状态) 引入import React,{us ...
- ExtJs学习笔记(3)_GridPanel[XML做数据源]
这一节,将学习到除了用JSON做GridPanel的数据源外,还可以使用XML 一.静态示例 1.xml文件内容: <?xml version="1.0" encoding= ...
- React学习笔记5:React Hooks概述
文章目录 概述 React的两套API 类和函数的差异 副效应(副作用)是什么? 钩子(hook)出现的意义 三大框架对比 为什么学习hooks 应用场景 新版本特性解读 Hook使用规则 概述 Re ...
最新文章
- Linux操作系统下查看硬件信息的命令总结
- 高等数学同济第七版课后答案下册
- C#在DataTable中使用LINQ
- ANSI,ASCII,UNICODE
- 利用Service Fabric承载eShop On Containers
- java类型转换造成的字节丢失,java 编码转换(已解决,转换字节丢失,无法实现)
- java同时关闭两个窗口_在一个窗口中同时关闭多个窗口的问题(Swing中事件多点传送的问题) | 学步园...
- Windows平台下的多线程编程
- html页面缓存meta,html中怎么用meta语句禁用页面缓存?
- Ubuntu 下VNC(Real VNC) 的安装和配置
- 第二阶段冲刺第六天,6月5日。
- 自定义UITabBar的两种方式
- wifi精灵android版,WiFi精灵安卓版
- psp android 模拟器,安卓psp模拟器
- 2017腾讯实习生招聘笔试编程题
- Android Studio系统盘瘦身
- NOIP2015总结
- Flak执行端口被占用OSError: [Errno 48] Address already in use
- css 按空格键对按钮暂停,当按下回车键后,怎么清空回车键的空格,或者模拟发送按键让光标向上?...
- 计算机桌面体验,平板电脑Aero桌面体验
热门文章
- atitit. it软件项目管理---自己的员工,雇佣军、援军,混合的员工 杂牌 人员管理架构
- PHOTOSHOP使用总结
- 调整单元格宽度无法生效的问题
- (转)国内各家智能投顾比较
- OpenAnolis社区致Linux开发者的一封信
- 如何写出优秀的开源简历
- 【优化求址】基于matlab遗传算法求解变电站选址优化问题【含Matlab源码 YC006期】
- 【优化算法】粒子群优化模拟器【含Matlab源码 1553期】
- 【滤波器】基于matlab GUI分数延迟滤波器设计【含Matlab源码 1347期】
- 【图像提取】基于matlab形态学矩阵块+线段提取【含Matlab源码 1014期】