[学习笔记] Cordova+AmazeUI+React 作个通信录 - 联系人详情

[学习笔记] Cordova+AmazeUI+React 作个通信录 系列文章javascript

目录

  1. 准备
  2. 联系人列表(1)
  3. 联系人列表(2)
  4. 联系人详情
  5. 单页应用 (With Router)
  6. 使用 SQLite

传送门:所有章节 示例代码css


前面已经完成了联系人列表,然而彷佛与 Cordova 没啥关系,随便找个 Web 服务器就能够跑。那么标题上的 Cordova 就只是一个简单的 Web 窗口而已么?html

固然不是,目前咱们的数据是保存在 json 文件中的,之后会放在数据库中,以便于实现数据的增删改。数据库选用 SQLite,操做 SQLite 必须使用 Android 提供的一些 API,这时候就须要经过 Cordova 的插件来实现了。甚至再日后添加二维码等功能,都须要用到 Cordova。可是 Cordova 的 API 参与了开发以后,调试时就要部署在手机上进行了,操做多有不变,因此先处理前端不须要 Cordova 的部分,待前端成熟以后再来链接 Cordova。前端

详情页面

联系人列表完成以后,如今开始考虑详情页面。之因此须要详情页面,是由于还有一些不便于在列表中展现的信息(列表项显示范围有限),因此再在测试数据中添加更多的字段,好比性别、城市等java

规划

详情页面写在 detail.html 中,对应的在 js 目录中建立一个 detail.jsx 保存 React 组件和脚本。样式表暂时仍然使用 index.css,在里面添加详情页面所须要的样式。因此新的 www 目录结构会是这样react

clipboard.png

详情页面仍然须要显示一个页头,标题就是姓名。以后的内容仍然用列表显示。用 HTML 表示大概会像这样git

<ul>
    <li>
        <span class="label">姓名</span>
        <span>张三</span>
    </li>
    <li>
        <span class="label">电话</span>
        <span>13801234567</span>
    </li>
    <li>
        <span class="label">性别</span>
        <span>男</span>
    </li>
    <li>
        <span class="label">城市</span>
        <span>四川省绵阳市</span>
    </li>
</ul>

代码

detail.html

detail.html 的代码和 index.html 几乎同样,惟一的区别是github

<script type="text/jsx" src="js/index.jsx"></script>

换成了数据库

<script type="text/jsx" src="js/detail.jsx"></script>

detail.jsx

detail.jsx 中主要定义 3 个组件json

  1. Detail 组件渲染联系人信息详情列表
  2. DetailItem 组件渲染联系某一项信息的内容
  3. Page 组件渲染页面内容,包括页头和 Detail 组件等

由于是静态页面,不能处理 GET 或 POST 参数,因此由地址栏的 HASH 传递联系人的 ID 参数,再在页面中经过 AJAX 获取数据,筛选出该详情页面须要显示的联系人信息。而数据获取就由 Page 组件处理(处理过程参考 index.jsx 中的 Page 组件。

以后由 Page 经过属性方式向 Detail 传递联系人数据;而 Detail 则拆分数据项,仍然经过 props 方式,逐项向 DetailItem 传递数据。

在容错方面,为了简化处理过程,若是没能取得联系人数据,则用一个姓名叫“查无此人”的默认的联系人数据代替。

定义 Detail 组件

var Detail = React.createClass({
    render: function() {
        var person = this.props.person;

        return (
            <A.List data-id={person.id}>
                <DetailItem label="姓名" value={person.name} />
                <DetailItem label="电话" value={person.tel} />
                <DetailItem label="性别" value={person.isMan ? "男" : "女"} />
                <DetailItem label="城市" value={person.city} />
            </A.List>
        );
    }
});

注意,这里在 <A.List> 中传入了一个 data-id 属性,这个属性在做为 React 对象属性的同时,也会以标签属性形式渲染到 HTML 中——React 会把 data- 前缀的属性直接渲染为 HTML 标签属性。

DetailItem 组件

var DetailItem = React.createClass({
    render: function() {
        return (
            <A.ListItem>
                <span className="label">{this.props.label}</span>
                <span>{this.props.value}</span>
            </A.ListItem>
        );
    }
});

这个组件没什么悬念,只是直接把 label 的文本显示出来而已。

Page 组件

var Page = React.createClass({
    defaultPerson: {
        id: "0000",
        name: "查无此人",
        phone: "00000000000"
    },
    getInitialState: function() {
        return {
            person: null
        };
    },
    componentDidMount: function() {
        $.getJSON("/js/data.json").then(function(data) {
            if (this.isMounted()) {
                this.setState({
                    person: data.filter(function(p) {
                        return "#" + p.id === window.location.hash;
                    })[0]
                });
            }
        }.bind(this));
    },
    render: function() {
        var person = this.state.person || this.defaultPerson;

        return (
            <div>
                <A.Header title={person.name} />
                <Detail person={person} />
            </div>
        );
    }
});

Page 组件和以前列表页面的 Page 组件同样,在 componentDidMount() 中经过 AJAX 加载数据。以后,经过 window.location.hash 按 ID 精确查找联系人,设置到 state 中。

显示的时候,若是没有找到 this.state.person,则使用默认的“查无此人”联系人信息。

渲染 Page

最后固然不能忘了渲染根组件:Page。

React.render(<Page />, document.body);

这时候,经过 http://localhost/detail.html#1001 这个地址,已经能够看到数据显示出来,只不过因为没有添加样式,还不够美观。

clipboard.png

在列表页面上添加到详情页面的链接

按照 Amazi UI React 文档,在列表页面上添加到详情页面的链接,只须要在 Person 组件中为 <A.ListItem> 添加 href 属性便可,

<A.ListItem className="person" href={"detail.html#" + this.props.id}>
</A.ListItem>

而后添加以后显示出来的效果却大大出乎意料。主要缘由是 Amaze UI 将 li>a 的 display 设置为 block了,因此带连接的电话图标会显示在下一行。固然经过修改 CSS 是能够解决的,可是我想用另外一种方法来解决:点击事件。

列表项上的点击事件

React 是支付事件处理的,在其 Event System 一章中说明了事件处理的方式和注意事项。React 能够处理的事件分几大类共计数十个,都在 Event System 中列举出来了。这里须要用到的是 onClick 事件。

注意,下面说的内容不是在 detail.jsx 而是在 index.jsx 中

首先为 Person 组件定义一个处理函数,用于处理列表项被点击后的动做:设置 window.location.href 跳转到详情页。

var Person = React.createClass({
    handleClick: function(event) {
        window.location.href = "detail.html#" + this.props.id;
    },
    render() { ... }
});

能够看到,数据来源仍然能够是 this.props,同理,也能够是 this.state

以后在 <A.ListItem> 中绑定事件处理函数

render: function() {
        var link = "tel:" + this.props.tel;
        return (
            <A.ListItem className="person" onClick={this.handleClick}>
            ...
            </A.ListItem>
        );
    }

注意到 onClick={this.handleClick} 的写法,联想到在 HTML 标签属性中绑定事件处理函数的状况,函数在执行时 this 指针会变为全局对象 (window)。这里是否是须要 bind(this) 呢?

不须要!

React 已经处理了 this 的问题,因此这里只须要简单绑定 this.handleClick,而在 handleClick 中使用 this 就是当前组件对象,不会是全局对象。

若是有强迫症,可能还须要给 <A.ListItem> 加个内联样式:style={{ cursor: "pointer" }},不过我认为不必,由于咱们的目标是手机 App,指哪点哪,彻底没有 hover 一说。

可点击的详情电话

要让电话可点击只须要加 <a href="tel:xxxxxx"> 便可。可是在详情页,每一个数据项都是这个结构:

<ListItem>
    <span>{label}</span>
    <span>{value}</span>
</ListItem>

若是要加连接,就会变成下面的结构

<ListItem>
    <span>{label}></span>
    <span>
        <a href={href}>{value}</a>
    </span>
</ListItem>

而后再在 Detail 中使用组件的时候多传入一个 href 参数。

提及来,两个 DetailItem 中的区别只有第 2 个的 <span> 中的内容那一点,有没有办法能够重用呢?——能够试试 mixins,由于从 Reusable Components 的理解,mixins 有点像继承。

尝试(第 1 次失败)

var DetailItem = React.createClass({
    getValueContent: function() {
        return this.props.value;
    },
    render: function() {
        return (
            <A.ListItem className="person-detail-item">
                <span className="label">{this.props.label}</span>
                <span>{this.getValueContent()}</span>
            </A.ListItem>
        );
    }
});
var DetailLinkItem = React.createClass({
    mixins: [DetailItem],
    getValueContent: function() {
        return <a href={this.props.href}>{this.props.value}</a>;
    }
});

结果失败了。再次阅读 Reusable Components 中的示例,而后发现 mixins 数组中应该是一个原型对象更贴切,尝试

第 2 次尝试(再次失败)

var DetailLinkItem = React.createClass({
    mixins: [DetailItem.prototype],
    getValueContent: function() {
        return <a href={this.props.href}>{this.props.value}</a>;
    }
});

此次是由于重复定义了 constructor。DetailItem.prototype 中有一个 constructor,而 React.createClass() 又定义了一个。看来不能偷懒,只能定义个独立对象了

终于成功的尝试

var detailBase = {
    render: function() {
        return (
            <A.ListItem className="person-detail-item">
                <span className="label">{this.props.label}</span>
                <span>{this.getValueContent()}</span>
            </A.ListItem>
        );
    }
};

var DetailItem = React.createClass({
    mixins: [detailBase],
    getValueContent: function() {
        return this.props.value
    }
});

var DetailLinkItem = React.createClass({
    mixins: [detailBase],
    getValueContent: function() {
        return <a href={this.props.href}>{this.props.value}</a>;
    }
});

补充一下 Detail 中的变动

这个变动是在第 1 次尝试的时候就修改了的,主要是处理“电话”那一项时用 <DetailLinkItem> 代替了 <DetailItem>

var Detail = React.createClass({
    render: function() {
        var person = this.props.person;

        return (
            <A.List className="person-detail" data-id={person.id}>
                <DetailItem label="姓名" value={person.name} />
                <DetailLinkItem label="电话" value={person.tel} href={"tel:" + person.tel} />
                <DetailItem label="性别" value={person.isMan ? "男" : "女"} />
                <DetailItem label="城市" value={person.city} />
            </A.List>
        );
    }
});

美化 Detail

美化一般放在最后,但不表明不须要,如今来加 className 和 CSS。

从效果上来看,主要是须要把 label 和后面的内容区分开来,顺便参照列表页面的列表样式,作一些细节上的美化。

先为 <A.List> 添加 className="person-detail",再为 <A.ListItem> 添加 className="person-detail-item"className="label" 是有先见之明早就加了的。最后修改 CSS,去掉 li 的类限定,再加上 ul.person-detailli.person-detail-item 的样式

ul.person-list {
    margin-top: 0;
}

li {
    padding: 3px 6px;
}

li>.person-icon {
    margin-right: 6px;
}

li>.person-phone {
    margin-top: 4px;
}

ul.person-detail li {
    margin-bottom: 4px;
    border-top: 0;
}

li.person-detail-item .label {
    margin-right: 8px;
    padding: 2px 5px;
    background-color: #41afff;
    border-radius: 3px;
    color: white;
}

最终效果仍是比较使人满意的

clipboard.png

相关文章
相关标签/搜索