[学习笔记] Cordova+AmazeUI+React 做个通讯录 系列文章

目录

  1. 准备
  2. 联系人列表(1)
  3. 联系人列表(2)
  4. 联系人详情
  5. 单页应用 (With Router)
  6. 使用 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 />
});

很明显,上面的代码是有问题的:

  1. Person 组件中的 t 变量没有赋值,所以 t.name 一定会抛异常;
  2. return <Person /> 看起来没有问题,但是并没有传入 t 值,所以列表的每一项都会是一模一样的

于是这里遇到了问题:怎么从父级控件向子级控件传入参数?

从父级控件向子级控件传入参数

这里可以作个比喻:从父级控件调用子级控件,就像在某个函数中调用其它函数一样。那么传入参数也就像调用函数时传入的参数一样。

React 通过 props 向子级控件传入参数,在 JSX 语法中,写法和 XML 的属性定义类似。比如向 Person 控件传入 nametel 参数——应该叫属性更准确,就可以这样

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 组件的时候,也向其传入了 amStyleradius 两个属性……等等,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 的两个属性 nametel 都被传递给了 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

除此之外还有几点需要注意

  1. $.getJSON().then() 的回调函数中,直接使用了 this.isMounted()this.setState() 等。函数中的 this 怎么还会是组件对象呢?——请注意回调函数后的 .bind(this)。这个方法在 React 的各方实例中经常出现,不失为传递 this 的一个好办法。
  2. this.isMounted() 的作用是判断当前组件仍然处于 mounted 状态,只有在这个状态下 setState 才有意义。虽然在 componentDidMount 事件中写的这段代码,但是由于是异步加载,所以并不知道当前组件是否已经有所变化。
  3. 如果在 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 属性

  1. 在 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>);}
    });
  2. 在 Person 组件中分别添加 personperson-iconperson-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>);}
    });
  3. 修改样式表

    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)相关推荐

  1. [学习笔记] Cordova+AmazeUI+React 做个通讯录 - 单页应用 (With Router)

    [学习笔记] Cordova+AmazeUI+React 做个通讯录 系列文章 目录 准备 联系人列表(1) 联系人列表(2) 联系人详情 单页应用 (With Router) 使用 SQLite 传 ...

  2. [学习笔记] Cordova+AmazeUI+React 做个通讯录 - 使用 SQLite

    [学习笔记] Cordova+AmazeUI+React 做个通讯录 系列文章 目录 准备 联系人列表(1) 联系人列表(2) 联系人详情 单页应用 (With Router) 使用 SQLite 传 ...

  3. amazeui学习笔记一(开始使用3)--兼容性列表compatibility

    amazeui学习笔记一(开始使用3)--兼容性列表compatibility 一.总结 1.不要用ie做前端测试,不要碰ie,尽量用google 浏览器: 按照微软官方的说法,IE 开发者工具中的浏 ...

  4. amazeui学习笔记--css(常用组件5)--评论列表Comment

    amazeui学习笔记--css(常用组件5)--评论列表Comment 一.总结 1.am-comment:使用am-comment来声明评论对象,这个是放在article里面的,虽然article ...

  5. 影像组学视频学习笔记(14)-特征权重做图及美化、Li‘s have a solution and plan.

    本笔记来源于B站Up主: 有Li 的影像组学系列教学视频 本节(14)主要介绍: 特征权重做图及美化 import matplotlib.pyplot as plt %matplotlib inlin ...

  6. Javascript学习笔记8——用JSON做原型

    在Javascript学习笔记5--类和对象中,我简单地提到了利用JSON去构造一个对象.代码如下: <script type="text/javascript">va ...

  7. 阿里云HaaS100物联网开发板学习笔记(六)做个智能灯---一个完整的开发例子

    摘要:本篇文章将前期几个专题综合起来,基于阿里云HaaS100的新固件设计制作一个智能灯.这个智能灯由云平台.手机APP端和设备端组成,基本上涵盖了一个物联网小项目所需的主要步骤. 目录 1.在阿里云 ...

  8. React学习笔记6:React Hooks API总结

    useState-保存状态(惰性初始化) 作用 函数组件添加状态 注意事项 初始化以及更新state 用来声明状态变量 使用步骤(使用useState来创建状态) 引入import React,{us ...

  9. ExtJs学习笔记(3)_GridPanel[XML做数据源]

    这一节,将学习到除了用JSON做GridPanel的数据源外,还可以使用XML 一.静态示例 1.xml文件内容: <?xml version="1.0" encoding= ...

  10. React学习笔记5:React Hooks概述

    文章目录 概述 React的两套API 类和函数的差异 副效应(副作用)是什么? 钩子(hook)出现的意义 三大框架对比 为什么学习hooks 应用场景 新版本特性解读 Hook使用规则 概述 Re ...

最新文章

  1. Linux操作系统下查看硬件信息的命令总结
  2. 高等数学同济第七版课后答案下册
  3. C#在DataTable中使用LINQ
  4. ANSI,ASCII,UNICODE
  5. 利用Service Fabric承载eShop On Containers
  6. java类型转换造成的字节丢失,java 编码转换(已解决,转换字节丢失,无法实现)
  7. java同时关闭两个窗口_在一个窗口中同时关闭多个窗口的问题(Swing中事件多点传送的问题) | 学步园...
  8. Windows平台下的多线程编程
  9. html页面缓存meta,html中怎么用meta语句禁用页面缓存?
  10. Ubuntu 下VNC(Real VNC) 的安装和配置
  11. 第二阶段冲刺第六天,6月5日。
  12. 自定义UITabBar的两种方式
  13. wifi精灵android版,WiFi精灵安卓版
  14. psp android 模拟器,安卓psp模拟器
  15. 2017腾讯实习生招聘笔试编程题
  16. Android Studio系统盘瘦身
  17. NOIP2015总结
  18. Flak执行端口被占用OSError: [Errno 48] Address already in use
  19. css 按空格键对按钮暂停,当按下回车键后,怎么清空回车键的空格,或者模拟发送按键让光标向上?...
  20. 计算机桌面体验,平板电脑Aero桌面体验

热门文章

  1. atitit. it软件项目管理---自己的员工,雇佣军、援军,混合的员工 杂牌 人员管理架构
  2. PHOTOSHOP使用总结
  3. 调整单元格宽度无法生效的问题
  4. (转)国内各家智能投顾比较
  5. OpenAnolis社区致Linux开发者的一封信
  6. 如何写出优秀的开源简历
  7. 【优化求址】基于matlab遗传算法求解变电站选址优化问题【含Matlab源码 YC006期】
  8. 【优化算法】粒子群优化模拟器【含Matlab源码 1553期】
  9. 【滤波器】基于matlab GUI分数延迟滤波器设计【含Matlab源码 1347期】
  10. 【图像提取】基于matlab形态学矩阵块+线段提取【含Matlab源码 1014期】